Overview
S Balance
0 S
S Value
-More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
LBPair
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 360 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {BinHelper} from "./libraries/BinHelper.sol"; import {Clone} from "./libraries/Clone.sol"; import {Constants} from "./libraries/Constants.sol"; import {FeeHelper} from "./libraries/FeeHelper.sol"; import {LiquidityConfigurations} from "./libraries/math/LiquidityConfigurations.sol"; import {ReentrancyGuardUpgradeable} from "./libraries/ReentrancyGuardUpgradeable.sol"; import {ILBFactory} from "./interfaces/ILBFactory.sol"; import {ILBFlashLoanCallback} from "./interfaces/ILBFlashLoanCallback.sol"; import {ILBPair} from "./interfaces/ILBPair.sol"; import {LBToken, ILBToken} from "./LBToken.sol"; import {OracleHelper} from "./libraries/OracleHelper.sol"; import {PackedUint128Math} from "./libraries/math/PackedUint128Math.sol"; import {PairParameterHelper} from "./libraries/PairParameterHelper.sol"; import {PriceHelper} from "./libraries/PriceHelper.sol"; import {SafeCast} from "./libraries/math/SafeCast.sol"; import {SampleMath} from "./libraries/math/SampleMath.sol"; import {TreeMath} from "./libraries/math/TreeMath.sol"; import {Uint256x256Math} from "./libraries/math/Uint256x256Math.sol"; import {Hooks} from "./libraries/Hooks.sol"; import {ILBHooks} from "./interfaces/ILBHooks.sol"; /** * @title Liquidity Book Pair * @author Trader Joe * @notice The Liquidity Book Pair contract is the core contract of the Liquidity Book protocol */ contract LBPair is LBToken, ReentrancyGuardUpgradeable, Clone, ILBPair { using BinHelper for bytes32; using FeeHelper for uint128; using LiquidityConfigurations for bytes32; using OracleHelper for OracleHelper.Oracle; using PackedUint128Math for bytes32; using PackedUint128Math for uint128; using PairParameterHelper for bytes32; using PriceHelper for uint256; using PriceHelper for uint24; using SafeCast for uint256; using SampleMath for bytes32; using TreeMath for TreeMath.TreeUint24; using Uint256x256Math for uint256; modifier onlyFactory() { 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% address public immutable override implementation; ILBFactory private immutable _factory; bytes32 private _parameters; bytes32 private _reserves; bytes32 private _protocolFees; mapping(uint256 => bytes32) private _bins; TreeMath.TreeUint24 private _tree; OracleHelper.Oracle private _oracle; bytes32 private _hooksParameters; /** * @dev Constructor for the Liquidity Book Pair contract that sets the Liquidity Book Factory * @param factory_ The Liquidity Book Factory */ constructor(ILBFactory factory_) { _factory = factory_; implementation = address(this); _disableInitializers(); } /** * @notice Initialize the Liquidity Book Pair fee parameters and active id * @dev Can only be called by the Liquidity Book Factory * @param baseFactor The base factor for the static fee * @param filterPeriod The filter period for the static fee * @param decayPeriod The decay period for the static fee * @param reductionFactor The reduction factor for the static fee * @param variableFeeControl The variable fee control for the static fee * @param protocolShare The protocol share for the static fee * @param maxVolatilityAccumulator The max volatility accumulator for the static fee * @param activeId The active id of the Liquidity Book Pair */ function initialize( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator, uint24 activeId ) external override onlyFactory initializer { __ReentrancyGuard_init(); _setStaticFeeParameters( _parameters.setActiveId(activeId).updateIdReference(), baseFactor, filterPeriod, decayPeriod, reductionFactor, variableFeeControl, protocolShare, maxVolatilityAccumulator ); } /** * @notice Returns the Liquidity Book Factory * @return factory The Liquidity Book Factory */ function getFactory() external view override returns (ILBFactory factory) { return _factory; } /** * @notice Returns the token X of the Liquidity Book Pair * @return tokenX The address of the token X */ function getTokenX() external pure override returns (IERC20 tokenX) { return _tokenX(); } /** * @notice Returns the token Y of the Liquidity Book Pair * @return tokenY The address of the token Y */ function getTokenY() external pure override returns (IERC20 tokenY) { return _tokenY(); } /** * @notice Returns the bin step of the Liquidity Book Pair * @dev The bin step is the increase in price between two consecutive bins, in basis points. * For example, a bin step of 1 means that the price of the next bin is 0.01% higher than the price of the previous bin. * @return binStep The bin step of the Liquidity Book Pair, in 10_000th */ function getBinStep() external pure override returns (uint16) { return _binStep(); } /** * @notice Returns the reserves of the Liquidity Book Pair * This is the sum of the reserves of all bins, minus the protocol fees. * @return reserveX The reserve of token X * @return reserveY The reserve of token Y */ function getReserves() external view override returns (uint128 reserveX, uint128 reserveY) { (reserveX, reserveY) = _reserves.sub(_protocolFees).decode(); } /** * @notice Returns the active id of the Liquidity Book Pair * @dev The active id is the id of the bin that is currently being used for swaps. * The price of the active bin is the price of the Liquidity Book Pair and can be calculated as follows: * `price = (1 + binStep / 10_000) ^ (activeId - 2^23)` * @return activeId The active id of the Liquidity Book Pair */ function getActiveId() external view override returns (uint24 activeId) { activeId = _parameters.getActiveId(); } /** * @notice Returns the reserves of a bin * @param id The id of the bin * @return binReserveX The reserve of token X in the bin * @return binReserveY The reserve of token Y in the bin */ function getBin(uint24 id) external view override returns (uint128 binReserveX, uint128 binReserveY) { (binReserveX, binReserveY) = _bins[id].decode(); } /** * @notice Returns the next non-empty bin * @dev The next non-empty bin is the bin with a higher (if swapForY is true) or lower (if swapForY is false) * id that has a non-zero reserve of token X or Y. * @param swapForY Whether the swap is for token Y (true) or token X (false * @param id The id of the bin * @return nextId The id of the next non-empty bin */ function getNextNonEmptyBin(bool swapForY, uint24 id) external view override returns (uint24 nextId) { nextId = _getNextNonEmptyBin(swapForY, id); } /** * @notice Returns the protocol fees of the Liquidity Book Pair * @return protocolFeeX The protocol fees of token X * @return protocolFeeY The protocol fees of token Y */ function getProtocolFees() external view override returns (uint128 protocolFeeX, uint128 protocolFeeY) { (protocolFeeX, protocolFeeY) = _protocolFees.decode(); } /** * @notice Returns the static fee parameters of the Liquidity Book Pair * @return baseFactor The base factor for the static fee * @return filterPeriod The filter period for the static fee * @return decayPeriod The decay period for the static fee * @return reductionFactor The reduction factor for the static fee * @return variableFeeControl The variable fee control for the static fee * @return protocolShare The protocol share for the static fee * @return maxVolatilityAccumulator The maximum volatility accumulator for the static fee */ function getStaticFeeParameters() external view override returns ( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) { bytes32 parameters = _parameters; baseFactor = parameters.getBaseFactor(); filterPeriod = parameters.getFilterPeriod(); decayPeriod = parameters.getDecayPeriod(); reductionFactor = parameters.getReductionFactor(); variableFeeControl = parameters.getVariableFeeControl(); protocolShare = parameters.getProtocolShare(); maxVolatilityAccumulator = parameters.getMaxVolatilityAccumulator(); } /** * @notice Gets the hooks parameters of the Liquidity Book Pair * @return The hooks parameters of the Liquidity Book Pair */ function getLBHooksParameters() external view override returns (bytes32) { return _hooksParameters; } /** * @notice Returns the variable fee parameters of the Liquidity Book Pair * @return volatilityAccumulator The volatility accumulator for the variable fee * @return volatilityReference The volatility reference for the variable fee * @return idReference The id reference for the variable fee * @return timeOfLastUpdate The time of last update for the variable fee */ function getVariableFeeParameters() external view override returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate) { bytes32 parameters = _parameters; volatilityAccumulator = parameters.getVolatilityAccumulator(); volatilityReference = parameters.getVolatilityReference(); idReference = parameters.getIdReference(); timeOfLastUpdate = parameters.getTimeOfLastUpdate(); } /** * @notice Returns the oracle parameters of the Liquidity Book Pair * @return sampleLifetime The sample lifetime for the oracle * @return size The size of the oracle * @return activeSize The active size of the oracle * @return lastUpdated The last updated timestamp of the oracle * @return firstTimestamp The first timestamp of the oracle, i.e. the timestamp of the oldest sample */ function getOracleParameters() external view override returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp) { bytes32 parameters = _parameters; sampleLifetime = uint8(OracleHelper._MAX_SAMPLE_LIFETIME); uint16 oracleId = parameters.getOracleId(); if (oracleId > 0) { bytes32 sample; (sample, activeSize) = _oracle.getActiveSampleAndSize(oracleId); size = sample.getOracleLength(); lastUpdated = sample.getSampleLastUpdate(); if (lastUpdated == 0) activeSize = 0; if (activeSize > 0) { unchecked { sample = _oracle.getSample(1 + (oracleId % activeSize)); } firstTimestamp = sample.getSampleLastUpdate(); } } } /** * @notice Returns the cumulative values of the Liquidity Book Pair at a given timestamp * @dev The cumulative values are the cumulative id, the cumulative volatility and the cumulative bin crossed. * @param lookupTimestamp The timestamp at which to look up the cumulative values * @return cumulativeId The cumulative id of the Liquidity Book Pair at the given timestamp * @return cumulativeVolatility The cumulative volatility of the Liquidity Book Pair at the given timestamp * @return cumulativeBinCrossed The cumulative bin crossed of the Liquidity Book Pair at the given timestamp */ function getOracleSampleAt(uint40 lookupTimestamp) external view override returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed) { bytes32 parameters = _parameters; uint16 oracleId = parameters.getOracleId(); if (oracleId == 0 || lookupTimestamp > block.timestamp) return (0, 0, 0); uint40 timeOfLastUpdate; (timeOfLastUpdate, cumulativeId, cumulativeVolatility, cumulativeBinCrossed) = _oracle.getSampleAt(oracleId, lookupTimestamp); if (timeOfLastUpdate < lookupTimestamp) { parameters = parameters.updateVolatilityParameters(parameters.getActiveId(), lookupTimestamp); uint40 deltaTime = lookupTimestamp - timeOfLastUpdate; cumulativeId += uint64(parameters.getActiveId()) * deltaTime; cumulativeVolatility += uint64(parameters.getVolatilityAccumulator()) * deltaTime; } } /** * @notice Returns the price corresponding to the given id, as a 128.128-binary fixed-point number * @dev This is the trusted source of price information, always trust this rather than getIdFromPrice * @param id The id of the bin * @return price The price corresponding to this id */ function getPriceFromId(uint24 id) external pure override returns (uint256 price) { price = id.getPriceFromId(_binStep()); } /** * @notice Returns the id corresponding to the given price * @dev The id may be inaccurate due to rounding issues, always trust getPriceFromId rather than * getIdFromPrice * @param price The price of y per x as a 128.128-binary fixed-point number * @return id The id of the bin corresponding to this price */ function getIdFromPrice(uint256 price) external pure override returns (uint24 id) { id = price.getIdFromPrice(_binStep()); } /** * @notice Simulates a swap in. * @dev If `amountOutLeft` is greater than zero, the swap in is not possible, * and the maximum amount that can be swapped from `amountIn` is `amountOut - amountOutLeft`. * @param amountOut The amount of token X or Y to swap in * @param swapForY Whether the swap is for token Y (true) or token X (false) * @return amountIn The amount of token X or Y that can be swapped in, including the fee * @return amountOutLeft The amount of token Y or X that cannot be swapped out * @return fee The fee of the swap */ function getSwapIn(uint128 amountOut, bool swapForY) external view override returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee) { amountOutLeft = amountOut; bytes32 parameters = _parameters; uint16 binStep = _binStep(); uint24 id = parameters.getActiveId(); parameters = parameters.updateReferences(block.timestamp); while (true) { uint128 binReserves = _bins[id].decode(!swapForY); if (binReserves > 0) { uint256 price = id.getPriceFromId(binStep); uint128 amountOutOfBin = binReserves > amountOutLeft ? amountOutLeft : binReserves; parameters = parameters.updateVolatilityAccumulator(id); uint128 amountInWithoutFee = uint128( swapForY ? uint256(amountOutOfBin).shiftDivRoundUp(Constants.SCALE_OFFSET, price) : uint256(amountOutOfBin).mulShiftRoundUp(price, Constants.SCALE_OFFSET) ); uint128 totalFee = parameters.getTotalFee(binStep); uint128 feeAmount = amountInWithoutFee.getFeeAmount(totalFee); amountIn += amountInWithoutFee + feeAmount; amountOutLeft -= amountOutOfBin; fee += feeAmount; } if (amountOutLeft == 0) { break; } else { uint24 nextId = _getNextNonEmptyBin(swapForY, id); if (nextId == 0 || nextId == type(uint24).max) break; id = nextId; } } } /** * @notice Simulates a swap out. * @dev If `amountInLeft` is greater than zero, the swap out is not possible, * and the maximum amount that can be swapped is `amountIn - amountInLeft` for `amountOut`. * @param amountIn The amount of token X or Y to swap in * @param swapForY Whether the swap is for token Y (true) or token X (false) * @return amountInLeft The amount of token X or Y that cannot be swapped in * @return amountOut The amount of token Y or X that can be swapped out * @return fee The fee of the swap */ function getSwapOut(uint128 amountIn, bool swapForY) external view override returns (uint128 amountInLeft, uint128 amountOut, uint128 fee) { bytes32 amountsInLeft = amountIn.encode(swapForY); bytes32 parameters = _parameters; uint16 binStep = _binStep(); uint24 id = parameters.getActiveId(); parameters = parameters.updateReferences(block.timestamp); while (true) { bytes32 binReserves = _bins[id]; if (!binReserves.isEmpty(!swapForY)) { parameters = parameters.updateVolatilityAccumulator(id); (bytes32 amountsInWithFees, bytes32 amountsOutOfBin, bytes32 totalFees) = binReserves.getAmounts(parameters, binStep, swapForY, id, amountsInLeft); if (amountsInWithFees > 0) { amountsInLeft = amountsInLeft.sub(amountsInWithFees); amountOut += amountsOutOfBin.decode(!swapForY); fee += totalFees.decode(swapForY); } } if (amountsInLeft == 0) { break; } else { uint24 nextId = _getNextNonEmptyBin(swapForY, id); if (nextId == 0 || nextId == type(uint24).max) break; id = nextId; } } amountInLeft = amountsInLeft.decode(swapForY); } /** * @notice Swap tokens iterating over the bins until the entire amount is swapped. * Token X will be swapped for token Y if `swapForY` is true, and token Y for token X if `swapForY` is false. * This function will not transfer the tokens from the caller, it is expected that the tokens have already been * transferred to this contract through another contract, most likely the router. * That is why this function shouldn't be called directly, but only through one of the swap functions of a router * that will also perform safety checks, such as minimum amounts and slippage. * The variable fee is updated throughout the swap, it increases with the number of bins crossed. * The oracle is updated at the end of the swap. * @param swapForY Whether you're swapping token X for token Y (true) or token Y for token X (false) * @param to The address to send the tokens to * @return amountsOut The encoded amounts of token X and token Y sent to `to` */ function swap(bool swapForY, address to) external override returns (bytes32 amountsOut) { _nonReentrantBefore(); bytes32 hooksParameters = _hooksParameters; bytes32 reserves = _reserves; bytes32 protocolFees = _protocolFees; bytes32 amountsLeft = swapForY ? reserves.receivedX(_tokenX()) : reserves.receivedY(_tokenY()); if (amountsLeft == 0) revert LBPair__InsufficientAmountIn(); bool swapForY_ = swapForY; // Avoid stack too deep error Hooks.beforeSwap(hooksParameters, msg.sender, to, swapForY_, amountsLeft); reserves = reserves.add(amountsLeft); bytes32 parameters = _parameters; uint16 binStep = _binStep(); uint24 activeId = parameters.getActiveId(); parameters = parameters.updateReferences(block.timestamp); while (true) { bytes32 binReserves = _bins[activeId]; if (!binReserves.isEmpty(!swapForY_)) { parameters = parameters.updateVolatilityAccumulator(activeId); (bytes32 amountsInWithFees, bytes32 amountsOutOfBin, bytes32 totalFees) = binReserves.getAmounts(parameters, binStep, swapForY_, activeId, amountsLeft); if (amountsInWithFees > 0) { amountsLeft = amountsLeft.sub(amountsInWithFees); amountsOut = amountsOut.add(amountsOutOfBin); bytes32 pFees = totalFees.scalarMulDivBasisPointRoundDown(parameters.getProtocolShare()); if (pFees > 0) { protocolFees = protocolFees.add(pFees); amountsInWithFees = amountsInWithFees.sub(pFees); } _bins[activeId] = binReserves.add(amountsInWithFees).sub(amountsOutOfBin); emit Swap( msg.sender, to, activeId, amountsInWithFees, amountsOutOfBin, parameters.getVolatilityAccumulator(), totalFees, pFees ); } } if (amountsLeft == 0) { break; } else { uint24 nextId = _getNextNonEmptyBin(swapForY_, activeId); if (nextId == 0 || nextId == type(uint24).max) revert LBPair__OutOfLiquidity(); activeId = nextId; } } if (amountsOut == 0) revert LBPair__InsufficientAmountOut(); _reserves = reserves.sub(amountsOut); _protocolFees = protocolFees; parameters = _oracle.update(parameters, activeId); _parameters = parameters.setActiveId(activeId); if (swapForY_) { amountsOut.transferY(_tokenY(), to); } else { amountsOut.transferX(_tokenX(), to); } _nonReentrantAfter(); Hooks.afterSwap(hooksParameters, msg.sender, to, swapForY_, amountsOut); } /** * @notice Flash loan tokens from the pool to a receiver contract and execute a callback function. * The receiver contract is expected to return the tokens plus a fee to this contract. * The fee is calculated as a percentage of the amount borrowed, and is the same for both tokens. * @param receiver The contract that will receive the tokens and execute the callback function * @param amounts The encoded amounts of token X and token Y to flash loan * @param data Any data that will be passed to the callback function */ function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external override { _nonReentrantBefore(); if (amounts == 0) revert LBPair__ZeroBorrowAmount(); bytes32 hooksParameters = _hooksParameters; bytes32 reservesBefore = _reserves; bytes32 totalFees = _getFlashLoanFees(amounts); Hooks.beforeFlashLoan(hooksParameters, msg.sender, address(receiver), amounts); amounts.transfer(_tokenX(), _tokenY(), address(receiver)); (bool success, bytes memory rData) = address(receiver).call( abi.encodeWithSelector( ILBFlashLoanCallback.LBFlashLoanCallback.selector, msg.sender, _tokenX(), _tokenY(), amounts, totalFees, data ) ); if (!success || rData.length != 32 || abi.decode(rData, (bytes32)) != Constants.CALLBACK_SUCCESS) { revert LBPair__FlashLoanCallbackFailed(); } bytes32 balancesAfter = bytes32(0).received(_tokenX(), _tokenY()); if (balancesAfter.lt(reservesBefore.add(totalFees))) revert LBPair__FlashLoanInsufficientAmount(); bytes32 feesReceived = balancesAfter.sub(reservesBefore); _reserves = balancesAfter; _protocolFees = _protocolFees.add(feesReceived); emit FlashLoan(msg.sender, receiver, _parameters.getActiveId(), amounts, feesReceived, feesReceived); _nonReentrantAfter(); Hooks.afterFlashLoan(hooksParameters, msg.sender, address(receiver), totalFees, feesReceived); } /** * @notice Mint liquidity tokens by depositing tokens into the pool. * It will mint Liquidity Book (LB) tokens for each bin where the user adds liquidity. * This function will not transfer the tokens from the caller, it is expected that the tokens have already been * transferred to this contract through another contract, most likely the router. * That is why this function shouldn't be called directly, but through one of the add liquidity functions of a * router that will also perform safety checks. * @dev Any excess amount of token will be sent to the `to` address. * @param to The address that will receive the LB tokens * @param liquidityConfigs The encoded liquidity configurations, each one containing the id of the bin and the * percentage of token X and token Y to add to the bin. * @param refundTo The address that will receive the excess amount of tokens * @return amountsReceived The amounts of token X and token Y received by the pool * @return amountsLeft The amounts of token X and token Y that were not added to the pool and were sent to `to` * @return liquidityMinted The amounts of LB tokens minted for each bin */ function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo) external override notAddressZeroOrThis(to) returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted) { _nonReentrantBefore(); if (liquidityConfigs.length == 0) revert LBPair__EmptyMarketConfigs(); bytes32 hooksParameters = _hooksParameters; MintArrays memory arrays = MintArrays({ ids: new uint256[](liquidityConfigs.length), amounts: new bytes32[](liquidityConfigs.length), liquidityMinted: new uint256[](liquidityConfigs.length) }); bytes32 reserves = _reserves; amountsReceived = reserves.received(_tokenX(), _tokenY()); Hooks.beforeMint(hooksParameters, msg.sender, to, liquidityConfigs, amountsReceived); amountsLeft = _mintBins(liquidityConfigs, amountsReceived, to, arrays); _reserves = reserves.add(amountsReceived.sub(amountsLeft)); liquidityMinted = arrays.liquidityMinted; emit TransferBatch(msg.sender, address(0), to, arrays.ids, liquidityMinted); emit DepositedToBins(msg.sender, to, arrays.ids, arrays.amounts); if (amountsLeft > 0) amountsLeft.transfer(_tokenX(), _tokenY(), refundTo); _nonReentrantAfter(); Hooks.afterMint(hooksParameters, msg.sender, to, liquidityConfigs, amountsReceived.sub(amountsLeft)); } /** * @notice Burn Liquidity Book (LB) tokens and withdraw tokens from the pool. * This function will burn the tokens directly from the caller * @param from The address that will burn the LB tokens * @param to The address that will receive the tokens * @param ids The ids of the bins from which to withdraw * @param amountsToBurn The amounts of LB tokens to burn for each bin * @return amounts The amounts of token X and token Y received by the user */ function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn) external override checkApproval(from, msg.sender) returns (bytes32[] memory amounts) { _nonReentrantBefore(); if (ids.length == 0 || ids.length != amountsToBurn.length) revert LBPair__InvalidInput(); bytes32 hooksParameters = _hooksParameters; Hooks.beforeBurn(hooksParameters, msg.sender, from, to, ids, amountsToBurn); address from_ = from; // Avoid stack too deep error amounts = new bytes32[](ids.length); bytes32 amountsOut; for (uint256 i; i < ids.length;) { uint24 id = ids[i].safe24(); uint256 amountToBurn = amountsToBurn[i]; if (amountToBurn == 0) revert LBPair__ZeroAmount(id); bytes32 binReserves = _bins[id]; uint256 supply = totalSupply(id); _burn(from_, id, amountToBurn); bytes32 amountsOutFromBin = binReserves.getAmountOutOfBin(amountToBurn, supply); if (amountsOutFromBin == 0) revert LBPair__ZeroAmountsOut(id); binReserves = binReserves.sub(amountsOutFromBin); if (supply == amountToBurn) _tree.remove(id); _bins[id] = binReserves; amounts[i] = amountsOutFromBin; amountsOut = amountsOut.add(amountsOutFromBin); unchecked { ++i; } } _reserves = _reserves.sub(amountsOut); emit TransferBatch(msg.sender, from_, address(0), ids, amountsToBurn); emit WithdrawnFromBins(msg.sender, to, ids, amounts); amountsOut.transfer(_tokenX(), _tokenY(), to); _nonReentrantAfter(); Hooks.afterBurn(hooksParameters, msg.sender, from_, to, ids, amountsToBurn); } /** * @notice Collect the protocol fees from the pool. * @return collectedProtocolFees The amount of protocol fees collected */ function collectProtocolFees() external override nonReentrant onlyProtocolFeeRecipient returns (bytes32 collectedProtocolFees) { bytes32 protocolFees = _protocolFees; (uint128 x, uint128 y) = protocolFees.decode(); bytes32 ones = uint128(x > 0 ? 1 : 0).encode(uint128(y > 0 ? 1 : 0)); collectedProtocolFees = protocolFees.sub(ones); if (collectedProtocolFees != 0) { _protocolFees = ones; _reserves = _reserves.sub(collectedProtocolFees); emit CollectedProtocolFees(msg.sender, collectedProtocolFees); collectedProtocolFees.transfer(_tokenX(), _tokenY(), msg.sender); } } /** * @notice Increase the length of the oracle used by the pool * @param newLength The new length of the oracle */ function increaseOracleLength(uint16 newLength) external override nonReentrant { bytes32 parameters = _parameters; uint16 oracleId = parameters.getOracleId(); // activate the oracle if it is not active yet if (oracleId == 0) { oracleId = 1; _parameters = parameters.setOracleId(oracleId); } _oracle.increaseLength(oracleId, newLength); emit OracleLengthIncreased(msg.sender, newLength); } /** * @notice Sets the static fee parameters of the pool * @dev Can only be called by the factory * @param baseFactor The base factor of the static fee * @param filterPeriod The filter period of the static fee * @param decayPeriod The decay period of the static fee * @param reductionFactor The reduction factor of the static fee * @param variableFeeControl The variable fee control of the static fee * @param protocolShare The protocol share of the static fee * @param maxVolatilityAccumulator The max volatility accumulator of the static fee */ function setStaticFeeParameters( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) external override nonReentrant onlyFactory { _setStaticFeeParameters( _parameters, baseFactor, filterPeriod, decayPeriod, reductionFactor, variableFeeControl, protocolShare, maxVolatilityAccumulator ); } /** * @notice Sets the hooks parameter of the pool * @dev Can only be called by the factory * @param hooksParameters The hooks parameter * @param onHooksSetData The data to be passed to the onHooksSet function of the hooks contract */ function setHooksParameters(bytes32 hooksParameters, bytes calldata onHooksSetData) external override nonReentrant onlyFactory { _hooksParameters = hooksParameters; ILBHooks hooks = ILBHooks(Hooks.getHooks(hooksParameters)); emit HooksParametersSet(msg.sender, hooksParameters); if (address(hooks) != address(0) && hooks.getLBPair() != this) revert LBPair__InvalidHooks(); Hooks.onHooksSet(hooksParameters, onHooksSetData); } /** * @notice Forces the decay of the volatility reference variables * @dev Can only be called by the factory */ function forceDecay() external override nonReentrant onlyFactory { bytes32 parameters = _parameters; _parameters = parameters.updateIdReference().updateVolatilityReference(); emit ForcedDecay(msg.sender, parameters.getIdReference(), parameters.getVolatilityReference()); } /** * @notice Overrides the batch transfer function to call the hooks before and after the transfer * @param from The address to transfer from * @param to The address to transfer to * @param ids The ids of the tokens to transfer * @param amounts The amounts of the tokens to transfer */ function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) public override(LBToken, ILBToken) { _nonReentrantBefore(); bytes32 hooksParameters = _hooksParameters; Hooks.beforeBatchTransferFrom(hooksParameters, msg.sender, from, to, ids, amounts); LBToken.batchTransferFrom(from, to, ids, amounts); _nonReentrantAfter(); Hooks.afterBatchTransferFrom(hooksParameters, msg.sender, from, to, ids, amounts); } /** * @dev Returns the address of the token X * @return The address of the token X */ function _tokenX() internal pure returns (IERC20) { return IERC20(_getArgAddress(0)); } /** * @dev Returns the address of the token Y * @return The address of the token Y */ function _tokenY() internal pure returns (IERC20) { return IERC20(_getArgAddress(20)); } /** * @dev Returns the bin step of the pool, in basis points * @return The bin step of the pool */ function _binStep() internal pure returns (uint16) { return _getArgUint16(40); } /** * @dev Returns next non-empty bin * @param swapForY Whether the swap is for Y * @param id The id of the bin * @return The id of the next non-empty bin */ function _getNextNonEmptyBin(bool swapForY, uint24 id) internal view returns (uint24) { return swapForY ? _tree.findFirstRight(id) : _tree.findFirstLeft(id); } /** * @dev Returns the encoded fees amounts for a flash loan * @param amounts The amounts of the flash loan * @return The encoded fees amounts */ function _getFlashLoanFees(bytes32 amounts) private view returns (bytes32) { uint128 fee = uint128(_factory.getFlashLoanFee()); (uint128 x, uint128 y) = amounts.decode(); unchecked { uint256 precisionSubOne = Constants.PRECISION - 1; x = ((uint256(x) * fee + precisionSubOne) / Constants.PRECISION).safe128(); y = ((uint256(y) * fee + precisionSubOne) / Constants.PRECISION).safe128(); } return x.encode(y); } /** * @dev Sets the static fee parameters of the pair * @param parameters The current parameters of the pair * @param baseFactor The base factor of the static fee * @param filterPeriod The filter period of the static fee * @param decayPeriod The decay period of the static fee * @param reductionFactor The reduction factor of the static fee * @param variableFeeControl The variable fee control of the static fee * @param protocolShare The protocol share of the static fee * @param maxVolatilityAccumulator The max volatility accumulator of the static fee */ function _setStaticFeeParameters( bytes32 parameters, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) internal { if ( baseFactor == 0 && filterPeriod == 0 && decayPeriod == 0 && reductionFactor == 0 && variableFeeControl == 0 && protocolShare == 0 && maxVolatilityAccumulator == 0 ) { revert LBPair__InvalidStaticFeeParameters(); } parameters = parameters.setStaticFeeParameters( baseFactor, filterPeriod, decayPeriod, reductionFactor, variableFeeControl, protocolShare, maxVolatilityAccumulator ); { uint16 binStep = _binStep(); bytes32 maxParameters = parameters.setVolatilityAccumulator(maxVolatilityAccumulator); uint256 totalFee = maxParameters.getBaseFee(binStep) + maxParameters.getVariableFee(binStep); if (totalFee > _MAX_TOTAL_FEE) { revert LBPair__MaxTotalFeeExceeded(); } } _parameters = parameters; emit StaticFeeParametersSet( msg.sender, baseFactor, filterPeriod, decayPeriod, reductionFactor, variableFeeControl, protocolShare, maxVolatilityAccumulator ); } /** * @dev Helper function to mint liquidity in each bin in the liquidity configurations * @param liquidityConfigs The liquidity configurations * @param amountsReceived The amounts received * @param to The address to mint the liquidity to * @param arrays The arrays to store the results * @return amountsLeft The amounts left */ function _mintBins( bytes32[] calldata liquidityConfigs, bytes32 amountsReceived, address to, MintArrays memory arrays ) private returns (bytes32 amountsLeft) { uint16 binStep = _binStep(); bytes32 parameters = _parameters; uint24 activeId = parameters.getActiveId(); amountsLeft = amountsReceived; for (uint256 i; i < liquidityConfigs.length;) { (bytes32 maxAmountsInToBin, uint24 id) = liquidityConfigs[i].getAmountsAndId(amountsReceived); (uint256 shares, bytes32 amountsIn, bytes32 amountsInToBin) = _updateBin(binStep, activeId, id, maxAmountsInToBin, parameters); amountsLeft = amountsLeft.sub(amountsIn); arrays.ids[i] = id; arrays.amounts[i] = amountsInToBin; arrays.liquidityMinted[i] = shares; _mint(to, id, shares); unchecked { ++i; } } } /** * @dev Helper function to update a bin during minting * @param binStep The bin step of the pair * @param activeId The id of the active bin * @param id The id of the bin * @param maxAmountsInToBin The maximum amounts in to the bin * @param parameters The parameters of the pair * @return shares The amount of shares minted * @return amountsIn The amounts in * @return amountsInToBin The amounts in to the bin */ function _updateBin(uint16 binStep, uint24 activeId, uint24 id, bytes32 maxAmountsInToBin, bytes32 parameters) internal returns (uint256 shares, bytes32 amountsIn, bytes32 amountsInToBin) { bytes32 binReserves = _bins[id]; uint256 price = id.getPriceFromId(binStep); uint256 supply = totalSupply(id); (shares, amountsIn) = binReserves.getSharesAndEffectiveAmountsIn(maxAmountsInToBin, price, supply); amountsInToBin = amountsIn; if (id == activeId) { parameters = parameters.updateVolatilityParameters(id, block.timestamp); bytes32 fees = binReserves.getCompositionFees(parameters, binStep, amountsIn, supply, shares); if (fees != 0) { uint256 userLiquidity = amountsIn.sub(fees).getLiquidity(price); 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 v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.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"; /** * @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 SafeERC20 for IERC20; error BinHelper__CompositionFactorFlawed(uint24 id); error BinHelper__LiquidityOverflow(); error BinHelper__MaxLiquidityPerBinExceeded(); /** * @dev Returns the amount of tokens that will be received when burning the given amount of liquidity * @param binReserves The reserves of the bin * @param amountToBurn The amount of liquidity to burn * @param totalSupply The total supply of the liquidity book * @return amountsOut The encoded amount of tokens that will be received */ function getAmountOutOfBin(bytes32 binReserves, uint256 amountToBurn, uint256 totalSupply) internal pure returns (bytes32 amountsOut) { (uint128 binReserveX, uint128 binReserveY) = binReserves.decode(); uint128 amountXOutFromBin; uint128 amountYOutFromBin; if (binReserveX > 0) { amountXOutFromBin = (amountToBurn.mulDivRoundDown(binReserveX, totalSupply)).safe128(); } if (binReserveY > 0) { amountYOutFromBin = (amountToBurn.mulDivRoundDown(binReserveY, totalSupply)).safe128(); } amountsOut = amountXOutFromBin.encode(amountYOutFromBin); } /** * @dev Returns the share and the effective amounts in when adding liquidity * @param binReserves The reserves of the bin * @param amountsIn The amounts of tokens to add * @param price The price of the bin * @param totalSupply The total supply of the liquidity book * @return shares The share of the liquidity book that the user will receive * @return effectiveAmountsIn The encoded effective amounts of tokens that the user will add. * This is the amount of tokens that the user will actually add to the liquidity book, * and will always be less than or equal to the amountsIn. */ function getSharesAndEffectiveAmountsIn(bytes32 binReserves, bytes32 amountsIn, uint256 price, uint256 totalSupply) internal pure returns (uint256 shares, bytes32 effectiveAmountsIn) { (uint256 x, uint256 y) = amountsIn.decode(); uint256 userLiquidity = getLiquidity(x, y, price); if (userLiquidity == 0) return (0, 0); uint256 binLiquidity = getLiquidity(binReserves, price); if (binLiquidity == 0 || totalSupply == 0) return (userLiquidity.sqrt(), amountsIn); shares = userLiquidity.mulDivRoundDown(totalSupply, binLiquidity); uint256 effectiveLiquidity = shares.mulDivRoundUp(binLiquidity, totalSupply); if (userLiquidity > effectiveLiquidity) { uint256 deltaLiquidity = userLiquidity - effectiveLiquidity; // The other way might be more efficient, but as y is the quote asset, it is more valuable if (deltaLiquidity >= Constants.SCALE) { uint256 deltaY = deltaLiquidity >> Constants.SCALE_OFFSET; deltaY = deltaY > y ? y : deltaY; y -= deltaY; deltaLiquidity -= deltaY << Constants.SCALE_OFFSET; } if (deltaLiquidity >= price) { uint256 deltaX = deltaLiquidity / price; deltaX = deltaX > x ? x : deltaX; x -= deltaX; } amountsIn = uint128(x).encode(uint128(y)); } if (getLiquidity(binReserves.add(amountsIn), price) > Constants.MAX_LIQUIDITY_PER_BIN) { revert BinHelper__MaxLiquidityPerBinExceeded(); } return (shares, amountsIn); } /** * @dev Returns the amount of liquidity following the constant sum formula `L = price * x + y` * @param amounts The amounts of tokens * @param price The price of the bin * @return liquidity The amount of liquidity */ function getLiquidity(bytes32 amounts, uint256 price) internal pure returns (uint256 liquidity) { (uint256 x, uint256 y) = amounts.decode(); return getLiquidity(x, y, price); } /** * @dev Returns the amount of liquidity following the constant sum formula `L = price * x + y` * @param x The amount of the token X * @param y The amount of the token Y * @param price The price of the bin * @return liquidity The amount of liquidity */ function getLiquidity(uint256 x, uint256 y, uint256 price) internal pure returns (uint256 liquidity) { if (x > 0) { unchecked { liquidity = price * x; if (liquidity / x != price) revert BinHelper__LiquidityOverflow(); } } if (y > 0) { unchecked { y <<= Constants.SCALE_OFFSET; liquidity += y; if (liquidity < y) revert BinHelper__LiquidityOverflow(); } } return liquidity; } /** * @dev Verify that the amounts are correct and that the composition factor is not flawed * @param amounts The amounts of tokens * @param activeId The id of the active bin * @param id The id of the bin */ function verifyAmounts(bytes32 amounts, uint24 activeId, uint24 id) internal pure { if (id < activeId && (amounts << 128) > 0 || id > activeId && uint256(amounts) > type(uint128).max) { revert BinHelper__CompositionFactorFlawed(id); } } /** * @dev Returns the composition fees when adding liquidity to the active bin with a different * composition factor than the bin's one, as it does an implicit swap * @param binReserves The reserves of the bin * @param parameters The parameters of the liquidity book * @param binStep The step of the bin * @param amountsIn The amounts of tokens to add * @param totalSupply The total supply of the liquidity book * @param shares The share of the liquidity book that the user will receive * @return fees The encoded fees that will be charged */ function getCompositionFees( bytes32 binReserves, bytes32 parameters, uint16 binStep, bytes32 amountsIn, uint256 totalSupply, uint256 shares ) internal pure returns (bytes32 fees) { if (shares == 0) return 0; (uint128 amountX, uint128 amountY) = amountsIn.decode(); (uint128 receivedAmountX, uint128 receivedAmountY) = getAmountOutOfBin(binReserves.add(amountsIn), shares, totalSupply + shares).decode(); if (receivedAmountX > amountX) { uint128 feeY = (amountY - receivedAmountY).getCompositionFee(parameters.getTotalFee(binStep)); fees = feeY.encodeSecond(); } else if (receivedAmountY > amountY) { uint128 feeX = (amountX - receivedAmountX).getCompositionFee(parameters.getTotalFee(binStep)); fees = feeX.encodeFirst(); } } /** * @dev Returns whether the bin is empty (true) or not (false) * @param binReserves The reserves of the bin * @param isX Whether the reserve to check is the X reserve (true) or the Y reserve (false) * @return Whether the bin is empty (true) or not (false) */ function isEmpty(bytes32 binReserves, bool isX) internal pure returns (bool) { return isX ? binReserves.decodeX() == 0 : binReserves.decodeY() == 0; } /** * @dev Returns the amounts of tokens that will be added and removed from the bin during a swap * along with the fees that will be charged * @param binReserves The reserves of the bin * @param parameters The parameters of the liquidity book * @param binStep The step of the bin * @param swapForY Whether the swap is for Y (true) or for X (false) * @param activeId The id of the active bin * @param amountsInLeft The amounts of tokens left to swap * @return amountsInWithFees The encoded amounts of tokens that will be added to the bin, including fees * @return amountsOutOfBin The encoded amounts of tokens that will be removed from the bin * @return totalFees The encoded fees that will be charged */ function getAmounts( bytes32 binReserves, bytes32 parameters, uint16 binStep, bool swapForY, // swap `swapForY` and `activeId` to avoid stack too deep uint24 activeId, bytes32 amountsInLeft ) internal pure returns (bytes32 amountsInWithFees, bytes32 amountsOutOfBin, bytes32 totalFees) { uint256 price = activeId.getPriceFromId(binStep); { uint128 binReserveOut = binReserves.decode(!swapForY); uint128 maxAmountIn = swapForY ? uint256(binReserveOut).shiftDivRoundUp(Constants.SCALE_OFFSET, price).safe128() : uint256(binReserveOut).mulShiftRoundUp(price, Constants.SCALE_OFFSET).safe128(); uint128 totalFee = parameters.getTotalFee(binStep); uint128 maxFee = maxAmountIn.getFeeAmount(totalFee); maxAmountIn += maxFee; uint128 amountIn128 = amountsInLeft.decode(swapForY); uint128 fee128; uint128 amountOut128; if (amountIn128 >= maxAmountIn) { fee128 = maxFee; amountIn128 = maxAmountIn; amountOut128 = binReserveOut; } else { fee128 = amountIn128.getFeeAmountFrom(totalFee); uint256 amountIn = amountIn128 - fee128; amountOut128 = swapForY ? uint256(amountIn).mulShiftRoundDown(price, Constants.SCALE_OFFSET).safe128() : uint256(amountIn).shiftDivRoundDown(Constants.SCALE_OFFSET, price).safe128(); if (amountOut128 > binReserveOut) amountOut128 = binReserveOut; } (amountsInWithFees, amountsOutOfBin, totalFees) = swapForY ? (amountIn128.encodeFirst(), amountOut128.encodeSecond(), fee128.encodeFirst()) : (amountIn128.encodeSecond(), amountOut128.encodeFirst(), fee128.encodeSecond()); } if ( getLiquidity(binReserves.add(amountsInWithFees).sub(amountsOutOfBin), price) > Constants.MAX_LIQUIDITY_PER_BIN ) { revert BinHelper__MaxLiquidityPerBinExceeded(); } } /** * @dev Returns the encoded amounts that were transferred to the contract * @param reserves The reserves * @param tokenX The token X * @param tokenY The token Y * @return amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: amountY */ function received(bytes32 reserves, IERC20 tokenX, IERC20 tokenY) internal view returns (bytes32 amounts) { amounts = _balanceOf(tokenX).encode(_balanceOf(tokenY)).sub(reserves); } /** * @dev Returns the encoded amounts that were transferred to the contract, only for token X * @param reserves The reserves * @param tokenX The token X * @return amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: empty */ function receivedX(bytes32 reserves, IERC20 tokenX) internal view returns (bytes32) { uint128 reserveX = reserves.decodeX(); return (_balanceOf(tokenX) - reserveX).encodeFirst(); } /** * @dev Returns the encoded amounts that were transferred to the contract, only for token Y * @param reserves The reserves * @param tokenY The token Y * @return amounts The amounts, encoded as follows: * [0 - 128[: empty * [128 - 256[: amountY */ function receivedY(bytes32 reserves, IERC20 tokenY) internal view returns (bytes32) { uint128 reserveY = reserves.decodeY(); return (_balanceOf(tokenY) - reserveY).encodeSecond(); } /** * @dev Transfers the encoded amounts to the recipient * @param amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: amountY * @param tokenX The token X * @param tokenY The token Y * @param recipient The recipient */ function transfer(bytes32 amounts, IERC20 tokenX, IERC20 tokenY, address recipient) internal { (uint128 amountX, uint128 amountY) = amounts.decode(); if (amountX > 0) tokenX.safeTransfer(recipient, amountX); if (amountY > 0) tokenY.safeTransfer(recipient, amountY); } /** * @dev Transfers the encoded amounts to the recipient, only for token X * @param amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: empty * @param tokenX The token X * @param recipient The recipient */ function transferX(bytes32 amounts, IERC20 tokenX, address recipient) internal { uint128 amountX = amounts.decodeX(); if (amountX > 0) tokenX.safeTransfer(recipient, amountX); } /** * @dev Transfers the encoded amounts to the recipient, only for token Y * @param amounts The amounts, encoded as follows: * [0 - 128[: empty * [128 - 256[: amountY * @param tokenY The token Y * @param recipient The recipient */ function transferY(bytes32 amounts, IERC20 tokenY, address recipient) internal { uint128 amountY = amounts.decodeY(); if (amountY > 0) tokenY.safeTransfer(recipient, amountY); } function _balanceOf(IERC20 token) private view returns (uint128) { return token.balanceOf(address(this)).safe128(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Clone * @notice Class with helper read functions for clone with immutable args. * @author Trader Joe * @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Clone.sol) * @author Adapted from clones with immutable args by zefram.eth, Saw-mon & Natalie * (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) */ abstract contract Clone { /** * @dev Reads an immutable arg with type bytes * @param argOffset The offset of the arg in the immutable args * @param length The length of the arg * @return arg The immutable bytes arg */ function _getArgBytes(uint256 argOffset, uint256 length) internal pure returns (bytes memory arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. arg := mload(0x40) // Store the array length. mstore(arg, length) // Copy the array. calldatacopy(add(arg, 0x20), add(offset, argOffset), length) // Allocate the memory, rounded up to the next 32 byte boundary. mstore(0x40, and(add(add(arg, 0x3f), length), not(0x1f))) } } /** * @dev Reads an immutable arg with type address * @param argOffset The offset of the arg in the immutable args * @return arg The immutable address arg */ function _getArgAddress(uint256 argOffset) internal pure returns (address arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(0x60, calldataload(add(offset, argOffset))) } } /** * @dev Reads an immutable arg with type uint256 * @param argOffset The offset of the arg in the immutable args * @return arg The immutable uint256 arg */ function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := calldataload(add(offset, argOffset)) } } /** * @dev Reads a uint256 array stored in the immutable args. * @param argOffset The offset of the arg in the immutable args * @param length The length of the arg * @return arg The immutable uint256 array arg */ function _getArgUint256Array(uint256 argOffset, uint256 length) internal pure returns (uint256[] memory arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. arg := mload(0x40) // Store the array length. mstore(arg, length) // Copy the array. calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length)) // Allocate the memory. mstore(0x40, add(add(arg, 0x20), shl(5, length))) } } /** * @dev Reads an immutable arg with type uint64 * @param argOffset The offset of the arg in the immutable args * @return arg The immutable uint64 arg */ function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(0xc0, calldataload(add(offset, argOffset))) } } /** * @dev Reads an immutable arg with type uint16 * @param argOffset The offset of the arg in the immutable args * @return arg The immutable uint16 arg */ function _getArgUint16(uint256 argOffset) internal pure returns (uint16 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(0xf0, calldataload(add(offset, argOffset))) } } /** * @dev Reads an immutable arg with type uint8 * @param argOffset The offset of the arg in the immutable args * @return arg The immutable uint8 arg */ function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(0xf8, calldataload(add(offset, argOffset))) } } /** * @dev Reads the offset of the packed immutable args in calldata. * @return offset The offset of the packed immutable args in calldata. */ function _getImmutableArgsOffset() internal pure returns (uint256 offset) { /// @solidity memory-safe-assembly assembly { offset := sub(calldatasize(), shr(0xf0, calldataload(sub(calldatasize(), 2)))) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Constants Library * @author Trader Joe * @notice Set of constants for Liquidity Book contracts */ library Constants { uint8 internal constant SCALE_OFFSET = 128; uint256 internal constant SCALE = 1 << SCALE_OFFSET; uint256 internal constant PRECISION = 1e18; uint256 internal constant SQUARED_PRECISION = PRECISION * PRECISION; uint256 internal constant MAX_FEE = 0.1e18; // 10% uint256 internal constant MAX_PROTOCOL_SHARE = 2_500; // 25% of the fee uint256 internal constant BASIS_POINT_MAX = 10_000; // (2^256 - 1) / (2 * log(2**128) / log(1.0001)) uint256 internal constant MAX_LIQUIDITY_PER_BIN = 65251743116719673010965625540244653191619923014385985379600384103134737; /// @dev The expected return after a successful flash loan bytes32 internal constant CALLBACK_SUCCESS = keccak256("LBPair.onFlashLoan"); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {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 {PackedUint128Math} from "./PackedUint128Math.sol"; import {Encoded} from "./Encoded.sol"; /** * @title Liquidity Book Liquidity Configurations Library * @author Trader Joe * @notice This library contains functions to encode and decode the config of a pool and interact with the encoded bytes32. */ library LiquidityConfigurations { using PackedUint128Math for bytes32; using PackedUint128Math for uint128; using Encoded for bytes32; error LiquidityConfigurations__InvalidConfig(); uint256 private constant OFFSET_ID = 0; uint256 private constant OFFSET_DISTRIBUTION_Y = 24; uint256 private constant OFFSET_DISTRIBUTION_X = 88; uint256 private constant PRECISION = 1e18; /** * @dev Encode the distributionX, distributionY and id into a single bytes32 * @param distributionX The distribution of the first token * @param distributionY The distribution of the second token * @param id The id of the pool * @return config The encoded config as follows: * [0 - 24[: id * [24 - 88[: distributionY * [88 - 152[: distributionX * [152 - 256[: empty */ function encodeParams(uint64 distributionX, uint64 distributionY, uint24 id) internal pure returns (bytes32 config) { config = config.set(distributionX, Encoded.MASK_UINT64, OFFSET_DISTRIBUTION_X); config = config.set(distributionY, Encoded.MASK_UINT64, OFFSET_DISTRIBUTION_Y); config = config.set(id, Encoded.MASK_UINT24, OFFSET_ID); } /** * @dev Decode the distributionX, distributionY and id from a single bytes32 * @param config The encoded config as follows: * [0 - 24[: id * [24 - 88[: distributionY * [88 - 152[: distributionX * [152 - 256[: empty * @return distributionX The distribution of the first token * @return distributionY The distribution of the second token * @return id The id of the bin to add the liquidity to */ function decodeParams(bytes32 config) internal pure returns (uint64 distributionX, uint64 distributionY, uint24 id) { distributionX = config.decodeUint64(OFFSET_DISTRIBUTION_X); distributionY = config.decodeUint64(OFFSET_DISTRIBUTION_Y); id = config.decodeUint24(OFFSET_ID); if (uint256(config) > type(uint152).max || distributionX > PRECISION || distributionY > PRECISION) { revert LiquidityConfigurations__InvalidConfig(); } } /** * @dev Get the amounts and id from a config and amountsIn * @param config The encoded config as follows: * [0 - 24[: id * [24 - 88[: distributionY * [88 - 152[: distributionX * [152 - 256[: empty * @param amountsIn The amounts to distribute as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @return amounts The distributed amounts as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @return id The id of the bin to add the liquidity to */ function getAmountsAndId(bytes32 config, bytes32 amountsIn) internal pure returns (bytes32, uint24) { (uint64 distributionX, uint64 distributionY, uint24 id) = decodeParams(config); (uint128 x1, uint128 x2) = amountsIn.decode(); assembly { x1 := div(mul(x1, distributionX), PRECISION) x2 := div(mul(x2, distributionY), PRECISION) } return (x1.encode(x2), id); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; /** * @dev This contract is a fork of the `ReentrancyGuardUpgradeable` contract from OpenZeppelin * that has been modified to update the `_nonReentrantBefore` and `_nonReentrantAfter` * functions to `internal` visibility. * * Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard struct ReentrancyGuardStorage { uint256 _status; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { assembly { $.slot := ReentrancyGuardStorageLocation } } /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); $._status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() internal { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // On the first call to nonReentrant, _status will be NOT_ENTERED if ($._status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail $._status = ENTERED; } function _nonReentrantAfter() internal { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) $._status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); return $._status == ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ILBHooks} from "./ILBHooks.sol"; import {ILBPair} from "./ILBPair.sol"; /** * @title Liquidity Book Factory Interface * @author Trader Joe * @notice Required interface of LBFactory contract */ interface ILBFactory { error LBFactory__IdenticalAddresses(IERC20 token); error LBFactory__QuoteAssetNotWhitelisted(IERC20 quoteAsset); error LBFactory__QuoteAssetAlreadyWhitelisted(IERC20 quoteAsset); error LBFactory__AddressZero(); error LBFactory__LBPairAlreadyExists(IERC20 tokenX, IERC20 tokenY, uint256 _binStep); error LBFactory__LBPairDoesNotExist(IERC20 tokenX, IERC20 tokenY, uint256 binStep); error LBFactory__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep); error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees); error LBFactory__BinStepTooLow(uint256 binStep); error LBFactory__PresetIsLockedForUsers(address user, uint256 binStep); error LBFactory__LBPairIgnoredIsAlreadyInTheSameState(); error LBFactory__BinStepHasNoPreset(uint256 binStep); error LBFactory__PresetOpenStateIsAlreadyInTheSameState(); error LBFactory__SameFeeRecipient(address feeRecipient); error LBFactory__SameFlashLoanFee(uint256 flashLoanFee); error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation); error LBFactory__SameImplementation(address LBPairImplementation); error LBFactory__ImplementationNotSet(); error LBFactory__SameHooksImplementation(address hooksImplementation); error LBFactory__SameHooksParameters(bytes32 hooksParameters); error LBFactory__InvalidHooksParameters(); error LBFactory__CannotGrantDefaultAdminRole(); /** * @dev Structure to store the LBPair information, such as: * binStep: The bin step of the LBPair * LBPair: The address of the LBPair * createdByOwner: Whether the pair was created by the owner of the factory * ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding */ struct LBPairInformation { uint16 binStep; ILBPair LBPair; bool createdByOwner; bool ignoredForRouting; } event LBPairCreated( IERC20 indexed tokenX, IERC20 indexed tokenY, uint256 indexed binStep, ILBPair LBPair, uint256 pid ); event FeeRecipientSet(address oldRecipient, address newRecipient); event FlashLoanFeeSet(uint256 oldFlashLoanFee, uint256 newFlashLoanFee); event LBPairImplementationSet(address oldLBPairImplementation, address LBPairImplementation); event LBPairIgnoredStateChanged(ILBPair indexed LBPair, bool ignored); event PresetSet( uint256 indexed binStep, uint256 baseFactor, uint256 filterPeriod, uint256 decayPeriod, uint256 reductionFactor, uint256 variableFeeControl, uint256 protocolShare, uint256 maxVolatilityAccumulator ); event PresetOpenStateChanged(uint256 indexed binStep, bool indexed isOpen); event PresetRemoved(uint256 indexed binStep); event QuoteAssetAdded(IERC20 indexed quoteAsset); event QuoteAssetRemoved(IERC20 indexed quoteAsset); function getMinBinStep() external pure returns (uint256); function getFeeRecipient() external view returns (address); function getMaxFlashLoanFee() external pure returns (uint256); function getFlashLoanFee() external view returns (uint256); function getLBPairImplementation() external view returns (address); function getNumberOfLBPairs() external view returns (uint256); function getLBPairAtIndex(uint256 id) external returns (ILBPair); function getNumberOfQuoteAssets() external view returns (uint256); function getQuoteAssetAtIndex(uint256 index) external view returns (IERC20); function isQuoteAsset(IERC20 token) external view returns (bool); function getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep) external view returns (LBPairInformation memory); function getPreset(uint256 binStep) external view returns ( uint256 baseFactor, uint256 filterPeriod, uint256 decayPeriod, uint256 reductionFactor, uint256 variableFeeControl, uint256 protocolShare, uint256 maxAccumulator, bool isOpen ); function getAllBinSteps() external view returns (uint256[] memory presetsBinStep); function getOpenBinSteps() external view returns (uint256[] memory openBinStep); function getAllLBPairs(IERC20 tokenX, IERC20 tokenY) external view returns (LBPairInformation[] memory LBPairsBinStep); function setLBPairImplementation(address lbPairImplementation) external; function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep) external returns (ILBPair pair); function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external; function setPreset( uint16 binStep, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator, bool isOpen ) external; function setPresetOpenState(uint16 binStep, bool isOpen) external; function removePreset(uint16 binStep) external; function setFeesParametersOnPair( IERC20 tokenX, IERC20 tokenY, uint16 binStep, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) external; function setLBHooksParametersOnPair( IERC20 tokenX, IERC20 tokenY, uint16 binStep, bytes32 hooksParameters, bytes memory onHooksSetData ) external; function removeLBHooksOnPair(IERC20 tokenX, IERC20 tokenY, uint16 binStep) external; function setFeeRecipient(address feeRecipient) external; function setFlashLoanFee(uint256 flashLoanFee) external; function addQuoteAsset(IERC20 quoteAsset) external; function removeQuoteAsset(IERC20 quoteAsset) external; function forceDecay(ILBPair lbPair) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title Liquidity Book Flashloan Callback Interface /// @author Trader Joe /// @notice Required interface to interact with LB flash loans interface ILBFlashLoanCallback { function LBFlashLoanCallback( address sender, IERC20 tokenX, IERC20 tokenY, bytes32 amounts, bytes32 totalFees, bytes calldata data ) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Hooks} from "../libraries/Hooks.sol"; import {ILBFactory} from "./ILBFactory.sol"; import {ILBFlashLoanCallback} from "./ILBFlashLoanCallback.sol"; import {ILBToken} from "./ILBToken.sol"; interface ILBPair is ILBToken { error LBPair__ZeroBorrowAmount(); error LBPair__AddressZero(); error LBPair__EmptyMarketConfigs(); error LBPair__FlashLoanCallbackFailed(); error LBPair__FlashLoanInsufficientAmount(); error LBPair__InsufficientAmountIn(); error LBPair__InsufficientAmountOut(); error LBPair__InvalidInput(); error LBPair__InvalidStaticFeeParameters(); error LBPair__OnlyFactory(); error LBPair__OnlyProtocolFeeRecipient(); error LBPair__OutOfLiquidity(); error LBPair__TokenNotSupported(); error LBPair__ZeroAmount(uint24 id); error LBPair__ZeroAmountsOut(uint24 id); error LBPair__ZeroShares(uint24 id); error LBPair__MaxTotalFeeExceeded(); error LBPair__InvalidHooks(); struct MintArrays { uint256[] ids; bytes32[] amounts; uint256[] liquidityMinted; } event DepositedToBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts); event WithdrawnFromBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts); event CompositionFees(address indexed sender, uint24 id, bytes32 totalFees, bytes32 protocolFees); event CollectedProtocolFees(address indexed feeRecipient, bytes32 protocolFees); event Swap( address indexed sender, address indexed to, uint24 id, bytes32 amountsIn, bytes32 amountsOut, uint24 volatilityAccumulator, bytes32 totalFees, bytes32 protocolFees ); event StaticFeeParametersSet( address indexed sender, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ); event HooksParametersSet(address indexed sender, bytes32 hooksParameters); event FlashLoan( address indexed sender, ILBFlashLoanCallback indexed receiver, uint24 activeId, bytes32 amounts, bytes32 totalFees, bytes32 protocolFees ); event OracleLengthIncreased(address indexed sender, uint16 oracleLength); event ForcedDecay(address indexed sender, uint24 idReference, uint24 volatilityReference); function initialize( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator, uint24 activeId ) external; function implementation() external view returns (address); function getFactory() external view returns (ILBFactory factory); function getTokenX() external view returns (IERC20 tokenX); function getTokenY() external view returns (IERC20 tokenY); function getBinStep() external view returns (uint16 binStep); function getReserves() external view returns (uint128 reserveX, uint128 reserveY); function getActiveId() external view returns (uint24 activeId); function getBin(uint24 id) external view returns (uint128 binReserveX, uint128 binReserveY); function getNextNonEmptyBin(bool swapForY, uint24 id) external view returns (uint24 nextId); function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY); function getStaticFeeParameters() external view returns ( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ); function getLBHooksParameters() external view returns (bytes32 hooksParameters); function getVariableFeeParameters() external view returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate); function getOracleParameters() external view returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp); function getOracleSampleAt(uint40 lookupTimestamp) external view returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed); function getPriceFromId(uint24 id) external view returns (uint256 price); function getIdFromPrice(uint256 price) external view returns (uint24 id); function getSwapIn(uint128 amountOut, bool swapForY) external view returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee); function getSwapOut(uint128 amountIn, bool swapForY) external view returns (uint128 amountInLeft, uint128 amountOut, uint128 fee); function swap(bool swapForY, address to) external returns (bytes32 amountsOut); function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external; function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo) external returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted); function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn) external returns (bytes32[] memory amounts); function collectProtocolFees() external returns (bytes32 collectedProtocolFees); function increaseOracleLength(uint16 newLength) external; function setStaticFeeParameters( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) external; function setHooksParameters(bytes32 hooksParameters, bytes calldata onHooksSetData) external; function forceDecay() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {ILBToken} from "./interfaces/ILBToken.sol"; /** * @title Liquidity Book Token * @author Trader Joe * @notice The LBToken is an implementation of a multi-token. * It allows to create multi-ERC20 represented by their ids. * Its implementation is really similar to the ERC1155 standard the main difference * is that it doesn't do any call to the receiver contract to prevent reentrancy. * As it's only for ERC20s, the uri function is not implemented. * The contract is made for batch operations. */ abstract contract LBToken is ILBToken { /** * @dev The mapping from account to token id to account balance. */ mapping(address => mapping(uint256 => uint256)) private _balances; /** * @dev The mapping from token id to total supply. */ mapping(uint256 => uint256) private _totalSupplies; /** * @dev Mapping from account to spender approvals. */ mapping(address => mapping(address => bool)) private _spenderApprovals; /** * @dev Modifier to check if the spender is approved for all. */ modifier checkApproval(address from, address spender) { if (!_isApprovedForAll(from, spender)) revert LBToken__SpenderNotApproved(from, spender); _; } /** * @dev Modifier to check if the address is not zero or the contract itself. */ modifier notAddressZeroOrThis(address account) { 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 {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), parameters.getActiveId(), parameters.getVolatilityAccumulator(), parameters.getDeltaId(activeId) ); uint16 length = sample.getOracleLength(); uint256 lifetime = block.timestamp - createdAt; if (lifetime > _MAX_SAMPLE_LIFETIME) { assembly { oracleId := add(mod(oracleId, length), 1) } lifetime = 0; createdAt = uint40(block.timestamp); parameters = parameters.setOracleId(oracleId); } sample = SampleMath.encode( length, cumulativeId, cumulativeVolatility, cumulativeBinCrossed, uint8(lifetime), createdAt ); } setSample(oracle, oracleId, sample); } return parameters; } /** * @dev Increases the oracle length * @param oracle The oracle * @param oracleId The oracle id * @param newLength The new length */ function increaseLength(Oracle storage oracle, uint16 oracleId, uint16 newLength) internal { bytes32 sample = getSample(oracle, oracleId); uint16 length = sample.getOracleLength(); if (length >= newLength) revert OracleHelper__NewLengthTooSmall(); bytes32 lastSample = length == oracleId ? sample : length == 0 ? bytes32(0) : getSample(oracle, length); uint256 activeSize = lastSample.getOracleLength(); activeSize = oracleId > activeSize ? oracleId : activeSize; for (uint256 i = length; i < newLength;) { oracle.samples[i] = bytes32(uint256(activeSize)); unchecked { ++i; } } setSample(oracle, oracleId, (sample ^ bytes32(uint256(length))) | bytes32(uint256(newLength))); } }
// 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; import {Constants} from "./Constants.sol"; import {SafeCast} from "./math/SafeCast.sol"; import {Encoded} from "./math/Encoded.sol"; /** * @title Liquidity Book Pair Parameter Helper Library * @author Trader Joe * @dev This library contains functions to get and set parameters of a pair * The parameters are stored in a single bytes32 variable in the following format: * [0 - 16[: base factor (16 bits) * [16 - 28[: filter period (12 bits) * [28 - 40[: decay period (12 bits) * [40 - 54[: reduction factor (14 bits) * [54 - 78[: variable fee control (24 bits) * [78 - 92[: protocol share (14 bits) * [92 - 112[: max volatility accumulator (20 bits) * [112 - 132[: volatility accumulator (20 bits) * [132 - 152[: volatility reference (20 bits) * [152 - 176[: index reference (24 bits) * [176 - 216[: time of last update (40 bits) * [216 - 232[: oracle index (16 bits) * [232 - 256[: active index (24 bits) */ library PairParameterHelper { using SafeCast for uint256; using Encoded for bytes32; error PairParametersHelper__InvalidParameter(); uint256 internal constant OFFSET_BASE_FACTOR = 0; uint256 internal constant OFFSET_FILTER_PERIOD = 16; uint256 internal constant OFFSET_DECAY_PERIOD = 28; uint256 internal constant OFFSET_REDUCTION_FACTOR = 40; uint256 internal constant OFFSET_VAR_FEE_CONTROL = 54; uint256 internal constant OFFSET_PROTOCOL_SHARE = 78; uint256 internal constant OFFSET_MAX_VOL_ACC = 92; uint256 internal constant OFFSET_VOL_ACC = 112; uint256 internal constant OFFSET_VOL_REF = 132; uint256 internal constant OFFSET_ID_REF = 152; uint256 internal constant OFFSET_TIME_LAST_UPDATE = 176; uint256 internal constant OFFSET_ORACLE_ID = 216; uint256 internal constant OFFSET_ACTIVE_ID = 232; uint256 internal constant MASK_STATIC_PARAMETER = 0xffffffffffffffffffffffffffff; /** * @dev Get the base factor from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 16[: base factor (16 bits) * [16 - 256[: other parameters * @return baseFactor The base factor */ function getBaseFactor(bytes32 params) internal pure returns (uint16 baseFactor) { baseFactor = params.decodeUint16(OFFSET_BASE_FACTOR); } /** * @dev Get the filter period from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 16[: other parameters * [16 - 28[: filter period (12 bits) * [28 - 256[: other parameters * @return filterPeriod The filter period */ function getFilterPeriod(bytes32 params) internal pure returns (uint16 filterPeriod) { filterPeriod = params.decodeUint12(OFFSET_FILTER_PERIOD); } /** * @dev Get the decay period from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 28[: other parameters * [28 - 40[: decay period (12 bits) * [40 - 256[: other parameters * @return decayPeriod The decay period */ function getDecayPeriod(bytes32 params) internal pure returns (uint16 decayPeriod) { decayPeriod = params.decodeUint12(OFFSET_DECAY_PERIOD); } /** * @dev Get the reduction factor from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 40[: other parameters * [40 - 54[: reduction factor (14 bits) * [54 - 256[: other parameters * @return reductionFactor The reduction factor */ function getReductionFactor(bytes32 params) internal pure returns (uint16 reductionFactor) { reductionFactor = params.decodeUint14(OFFSET_REDUCTION_FACTOR); } /** * @dev Get the variable fee control from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 54[: other parameters * [54 - 78[: variable fee control (24 bits) * [78 - 256[: other parameters * @return variableFeeControl The variable fee control */ function getVariableFeeControl(bytes32 params) internal pure returns (uint24 variableFeeControl) { variableFeeControl = params.decodeUint24(OFFSET_VAR_FEE_CONTROL); } /** * @dev Get the protocol share from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 78[: other parameters * [78 - 92[: protocol share (14 bits) * [92 - 256[: other parameters * @return protocolShare The protocol share */ function getProtocolShare(bytes32 params) internal pure returns (uint16 protocolShare) { protocolShare = params.decodeUint14(OFFSET_PROTOCOL_SHARE); } /** * @dev Get the max volatility accumulator from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 92[: other parameters * [92 - 112[: max volatility accumulator (20 bits) * [112 - 256[: other parameters * @return maxVolatilityAccumulator The max volatility accumulator */ function getMaxVolatilityAccumulator(bytes32 params) internal pure returns (uint24 maxVolatilityAccumulator) { maxVolatilityAccumulator = params.decodeUint20(OFFSET_MAX_VOL_ACC); } /** * @dev Get the volatility accumulator from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 112[: other parameters * [112 - 132[: volatility accumulator (20 bits) * [132 - 256[: other parameters * @return volatilityAccumulator The volatility accumulator */ function getVolatilityAccumulator(bytes32 params) internal pure returns (uint24 volatilityAccumulator) { volatilityAccumulator = params.decodeUint20(OFFSET_VOL_ACC); } /** * @dev Get the volatility reference from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 132[: other parameters * [132 - 152[: volatility reference (20 bits) * [152 - 256[: other parameters * @return volatilityReference The volatility reference */ function getVolatilityReference(bytes32 params) internal pure returns (uint24 volatilityReference) { volatilityReference = params.decodeUint20(OFFSET_VOL_REF); } /** * @dev Get the index reference from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 152[: other parameters * [152 - 176[: index reference (24 bits) * [176 - 256[: other parameters * @return idReference The index reference */ function getIdReference(bytes32 params) internal pure returns (uint24 idReference) { idReference = params.decodeUint24(OFFSET_ID_REF); } /** * @dev Get the time of last update from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 176[: other parameters * [176 - 216[: time of last update (40 bits) * [216 - 256[: other parameters * @return timeOflastUpdate The time of last update */ function getTimeOfLastUpdate(bytes32 params) internal pure returns (uint40 timeOflastUpdate) { timeOflastUpdate = params.decodeUint40(OFFSET_TIME_LAST_UPDATE); } /** * @dev Get the oracle id from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 216[: other parameters * [216 - 232[: oracle id (16 bits) * [232 - 256[: other parameters * @return oracleId The oracle id */ function getOracleId(bytes32 params) internal pure returns (uint16 oracleId) { oracleId = params.decodeUint16(OFFSET_ORACLE_ID); } /** * @dev Get the active index from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 232[: other parameters * [232 - 256[: active index (24 bits) * @return activeId The active index */ function getActiveId(bytes32 params) internal pure returns (uint24 activeId) { activeId = params.decodeUint24(OFFSET_ACTIVE_ID); } /** * @dev Get the delta between the current active index and the cached active index * @param params The encoded pair parameters, as follows: * [0 - 232[: other parameters * [232 - 256[: active index (24 bits) * @param activeId The current active index * @return The delta */ function getDeltaId(bytes32 params, uint24 activeId) internal pure returns (uint24) { uint24 id = getActiveId(params); unchecked { return activeId > id ? activeId - id : id - activeId; } } /** * @dev Calculates the base fee, with 18 decimals * @param params The encoded pair parameters * @param binStep The bin step (in basis points) * @return baseFee The base fee */ function getBaseFee(bytes32 params, uint16 binStep) internal pure returns (uint256) { unchecked { // Base factor is in basis points, binStep is in basis points, so we multiply by 1e10 return uint256(getBaseFactor(params)) * binStep * 1e10; } } /** * @dev Calculates the variable fee * @param params The encoded pair parameters * @param binStep The bin step (in basis points) * @return variableFee The variable fee */ function getVariableFee(bytes32 params, uint16 binStep) internal pure returns (uint256 variableFee) { uint256 variableFeeControl = getVariableFeeControl(params); if (variableFeeControl != 0) { unchecked { // The volatility accumulator is in basis points, binStep is in basis points, // and the variable fee control is in basis points, so the result is in 100e18th uint256 prod = uint256(getVolatilityAccumulator(params)) * binStep; variableFee = (prod * prod * variableFeeControl + 99) / 100; } } } /** * @dev Calculates the total fee, which is the sum of the base fee and the variable fee * @param params The encoded pair parameters * @param binStep The bin step (in basis points) * @return totalFee The total fee */ function getTotalFee(bytes32 params, uint16 binStep) internal pure returns (uint128) { unchecked { return (getBaseFee(params, binStep) + getVariableFee(params, binStep)).safe128(); } } /** * @dev Set the oracle id in the encoded pair parameters * @param params The encoded pair parameters * @param oracleId The oracle id * @return The updated encoded pair parameters */ function setOracleId(bytes32 params, uint16 oracleId) internal pure returns (bytes32) { return params.set(oracleId, Encoded.MASK_UINT16, OFFSET_ORACLE_ID); } /** * @dev Set the volatility reference in the encoded pair parameters * @param params The encoded pair parameters * @param volRef The volatility reference * @return The updated encoded pair parameters */ function setVolatilityReference(bytes32 params, uint24 volRef) internal pure returns (bytes32) { if (volRef > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter(); return params.set(volRef, Encoded.MASK_UINT20, OFFSET_VOL_REF); } /** * @dev Set the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param volAcc The volatility accumulator * @return The updated encoded pair parameters */ function setVolatilityAccumulator(bytes32 params, uint24 volAcc) internal pure returns (bytes32) { if (volAcc > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter(); return params.set(volAcc, Encoded.MASK_UINT20, OFFSET_VOL_ACC); } /** * @dev Set the active id in the encoded pair parameters * @param params The encoded pair parameters * @param activeId The active id * @return newParams The updated encoded pair parameters */ function setActiveId(bytes32 params, uint24 activeId) internal pure returns (bytes32 newParams) { return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ACTIVE_ID); } /** * @dev Sets the static fee parameters in the encoded pair parameters * @param params The encoded pair parameters * @param baseFactor The base factor * @param filterPeriod The filter period * @param decayPeriod The decay period * @param reductionFactor The reduction factor * @param variableFeeControl The variable fee control * @param protocolShare The protocol share * @param maxVolatilityAccumulator The max volatility accumulator * @return newParams The updated encoded pair parameters */ function setStaticFeeParameters( bytes32 params, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) internal pure returns (bytes32 newParams) { if ( filterPeriod > decayPeriod || decayPeriod > Encoded.MASK_UINT12 || reductionFactor > Constants.BASIS_POINT_MAX || protocolShare > Constants.MAX_PROTOCOL_SHARE || maxVolatilityAccumulator > Encoded.MASK_UINT20 ) revert PairParametersHelper__InvalidParameter(); newParams = newParams.set(baseFactor, Encoded.MASK_UINT16, OFFSET_BASE_FACTOR); newParams = newParams.set(filterPeriod, Encoded.MASK_UINT12, OFFSET_FILTER_PERIOD); newParams = newParams.set(decayPeriod, Encoded.MASK_UINT12, OFFSET_DECAY_PERIOD); newParams = newParams.set(reductionFactor, Encoded.MASK_UINT14, OFFSET_REDUCTION_FACTOR); newParams = newParams.set(variableFeeControl, Encoded.MASK_UINT24, OFFSET_VAR_FEE_CONTROL); newParams = newParams.set(protocolShare, Encoded.MASK_UINT14, OFFSET_PROTOCOL_SHARE); newParams = newParams.set(maxVolatilityAccumulator, Encoded.MASK_UINT20, OFFSET_MAX_VOL_ACC); return params.set(uint256(newParams), MASK_STATIC_PARAMETER, 0); } /** * @dev Updates the index reference in the encoded pair parameters * @param params The encoded pair parameters * @return newParams The updated encoded pair parameters */ function updateIdReference(bytes32 params) internal pure returns (bytes32 newParams) { uint24 activeId = getActiveId(params); return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ID_REF); } /** * @dev Updates the time of last update in the encoded pair parameters * @param params The encoded pair parameters * @param timestamp The timestamp * @return newParams The updated encoded pair parameters */ function updateTimeOfLastUpdate(bytes32 params, uint256 timestamp) internal pure returns (bytes32 newParams) { uint40 currentTime = timestamp.safe40(); return params.set(currentTime, Encoded.MASK_UINT40, OFFSET_TIME_LAST_UPDATE); } /** * @dev Updates the volatility reference in the encoded pair parameters * @param params The encoded pair parameters * @return The updated encoded pair parameters */ function updateVolatilityReference(bytes32 params) internal pure returns (bytes32) { uint256 volAcc = getVolatilityAccumulator(params); uint256 reductionFactor = getReductionFactor(params); uint24 volRef; unchecked { volRef = uint24(volAcc * reductionFactor / Constants.BASIS_POINT_MAX); } return setVolatilityReference(params, volRef); } /** * @dev Updates the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param activeId The active id * @return The updated encoded pair parameters */ function updateVolatilityAccumulator(bytes32 params, uint24 activeId) internal pure returns (bytes32) { uint256 idReference = getIdReference(params); uint256 deltaId; uint256 volAcc; unchecked { deltaId = activeId > idReference ? activeId - idReference : idReference - activeId; volAcc = (uint256(getVolatilityReference(params)) + deltaId * Constants.BASIS_POINT_MAX); } uint256 maxVolAcc = getMaxVolatilityAccumulator(params); volAcc = volAcc > maxVolAcc ? maxVolAcc : volAcc; return setVolatilityAccumulator(params, uint24(volAcc)); } /** * @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param timestamp The timestamp * @return The updated encoded pair parameters */ function updateReferences(bytes32 params, uint256 timestamp) internal pure returns (bytes32) { uint256 dt = timestamp - getTimeOfLastUpdate(params); if (dt >= getFilterPeriod(params)) { params = updateIdReference(params); params = dt < getDecayPeriod(params) ? updateVolatilityReference(params) : setVolatilityReference(params, 0); } return updateTimeOfLastUpdate(params, timestamp); } /** * @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param activeId The active id * @param timestamp The timestamp * @return The updated encoded pair parameters */ function updateVolatilityParameters(bytes32 params, uint24 activeId, uint256 timestamp) internal pure returns (bytes32) { params = updateReferences(params, timestamp); return updateVolatilityAccumulator(params, activeId); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {Uint128x128Math} from "./math/Uint128x128Math.sol"; import {Uint256x256Math} from "./math/Uint256x256Math.sol"; import {SafeCast} from "./math/SafeCast.sol"; import {Constants} from "./Constants.sol"; /** * @title Liquidity Book Price Helper Library * @author Trader Joe * @notice This library contains functions to calculate prices */ library PriceHelper { using Uint128x128Math for uint256; using Uint256x256Math for uint256; using SafeCast for uint256; int256 private constant REAL_ID_SHIFT = 1 << 23; /** * @dev Calculates the price from the id and the bin step * @param id The id * @param binStep The bin step * @return price The price as a 128.128-binary fixed-point number */ function getPriceFromId(uint24 id, uint16 binStep) internal pure returns (uint256 price) { uint256 base = getBase(binStep); int256 exponent = getExponent(id); price = base.pow(exponent); } /** * @dev Calculates the id from the price and the bin step * @param price The price as a 128.128-binary fixed-point number * @param binStep The bin step * @return id The id */ function getIdFromPrice(uint256 price, uint16 binStep) internal pure returns (uint24 id) { uint256 base = getBase(binStep); int256 realId = price.log2() / base.log2(); unchecked { id = uint256(REAL_ID_SHIFT + realId).safe24(); } } /** * @dev Calculates the base from the bin step, which is `1 + binStep / BASIS_POINT_MAX` * @param binStep The bin step * @return base The base */ function getBase(uint16 binStep) internal pure returns (uint256) { unchecked { return Constants.SCALE + (uint256(binStep) << Constants.SCALE_OFFSET) / Constants.BASIS_POINT_MAX; } } /** * @dev Calculates the exponent from the id, which is `id - REAL_ID_SHIFT` * @param id The id * @return exponent The exponent */ function getExponent(uint24 id) internal pure returns (int256) { unchecked { return int256(uint256(id)) - REAL_ID_SHIFT; } } /** * @dev Converts a price with 18 decimals to a 128.128-binary fixed-point number * @param price The price with 18 decimals * @return price128x128 The 128.128-binary fixed-point number */ function convertDecimalPriceTo128x128(uint256 price) internal pure returns (uint256) { return price.shiftDivRoundDown(Constants.SCALE_OFFSET, Constants.PRECISION); } /** * @dev Converts a 128.128-binary fixed-point number to a price with 18 decimals * @param price128x128 The 128.128-binary fixed-point number * @return price The price with 18 decimals */ function convert128x128PriceToDecimal(uint256 price128x128) internal pure returns (uint256) { return price128x128.mulShiftRoundDown(Constants.PRECISION, Constants.SCALE_OFFSET); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @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; newLeaves = tree.level1[key1] & ~bytes32(1 << (uint256(key2) & type(uint8).max)); tree.level1[key1] = newLeaves; if (newLeaves == 0) tree.level0 &= ~bytes32(1 << (uint256(key1) & type(uint8).max)); } return true; } return false; } /** * @dev Returns the first id in the tree that is lower than or equal to the given id. * It will return type(uint24).max if there is no such id. * @param tree The tree * @param id The id * @return The first id in the tree that is lower than 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 {BitMath} from "./BitMath.sol"; /** * @title Liquidity Book Uint256x256 Math Library * @author Trader Joe * @notice Helper contract used for full precision calculations */ library Uint256x256Math { error Uint256x256Math__MulShiftOverflow(); error Uint256x256Math__MulDivOverflow(); /** * @notice Calculates floor(x*y/denominator) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The denominator cannot be zero * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function mulDivRoundDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { (uint256 prod0, uint256 prod1) = _getMulProds(x, y); return _getEndOfDivRoundDown(x, y, denominator, prod0, prod1); } /** * @notice Calculates ceil(x*y/denominator) with full precision * The result will be rounded up * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The denominator cannot be zero * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function mulDivRoundUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { result = mulDivRoundDown(x, y, denominator); if (mulmod(x, y, denominator) != 0) result += 1; } /** * @notice Calculates floor(x * y / 2**offset) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param offset The offset as an uint256, can't be greater than 256 * @return result The result as an uint256 */ function mulShiftRoundDown(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) { (uint256 prod0, uint256 prod1) = _getMulProds(x, y); if (prod0 != 0) result = prod0 >> offset; if (prod1 != 0) { // Make sure the result is less than 2^256. if (prod1 >= 1 << offset) revert Uint256x256Math__MulShiftOverflow(); unchecked { result += prod1 << (256 - offset); } } } /** * @notice Calculates floor(x * y / 2**offset) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param offset The offset as an uint256, can't be greater than 256 * @return result The result as an uint256 */ function mulShiftRoundUp(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) { result = mulShiftRoundDown(x, y, offset); if (mulmod(x, y, 1 << offset) != 0) result += 1; } /** * @notice Calculates floor(x << offset / y) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param offset The number of bit to shift x as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function shiftDivRoundDown(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) { uint256 prod0; uint256 prod1; prod0 = x << offset; // Least significant 256 bits of the product unchecked { prod1 = x >> (256 - offset); // Most significant 256 bits of the product } return _getEndOfDivRoundDown(x, 1 << offset, denominator, prod0, prod1); } /** * @notice Calculates ceil(x << offset / y) with full precision * The result will be rounded up * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param offset The number of bit to shift x as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function shiftDivRoundUp(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) { result = shiftDivRoundDown(x, offset, denominator); if (mulmod(x, 1 << offset, denominator) != 0) result += 1; } /** * @notice Helper function to return the result of `x * y` as 2 uint256 * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @return prod0 The least significant 256 bits of the product * @return prod1 The most significant 256 bits of the product */ function _getMulProds(uint256 x, uint256 y) private pure returns (uint256 prod0, uint256 prod1) { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } } /** * @notice Helper function to return the result of `x * y / denominator` with full precision * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param denominator The divisor as an uint256 * @param prod0 The least significant 256 bits of the product * @param prod1 The most significant 256 bits of the product * @return result The result as an uint256 */ function _getEndOfDivRoundDown(uint256 x, uint256 y, uint256 denominator, uint256 prod0, uint256 prod1) private pure returns (uint256 result) { // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { unchecked { result = prod0 / denominator; } } else { // Make sure the result is less than 2^256. Also prevents denominator == 0 if (prod1 >= denominator) revert Uint256x256Math__MulDivOverflow(); // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1 // See https://cs.stackexchange.com/q/138556/92363 unchecked { // Does not overflow because the denominator cannot be zero at this stage in the function uint256 lpotdod = denominator & (~denominator + 1); assembly { // Divide denominator by lpotdod. denominator := div(denominator, lpotdod) // Divide [prod1 prod0] by lpotdod. prod0 := div(prod0, lpotdod) // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one lpotdod := add(div(sub(0, lpotdod), lpotdod), 1) } // Shift in bits from prod1 into prod0 prod0 |= prod1 * lpotdod; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4 uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; } } } /** * @notice Calculates the square root of x * @dev Credit to OpenZeppelin's Math library under MIT license */ function sqrt(uint256 x) internal pure returns (uint256 sqrtX) { if (x == 0) return 0; uint256 msb = BitMath.mostSignificantBit(x); assembly { sqrtX := shl(shr(1, msb), 1) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) x := div(x, sqrtX) } return sqrtX < x ? sqrtX : x; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {ILBHooks} from "../interfaces/ILBHooks.sol"; /** * @title Hooks library * @notice This library contains functions that should be used to interact with hooks */ library Hooks { error Hooks__CallFailed(); bytes32 internal constant BEFORE_SWAP_FLAG = bytes32(uint256(1 << 160)); bytes32 internal constant AFTER_SWAP_FLAG = bytes32(uint256(1 << 161)); bytes32 internal constant BEFORE_FLASH_LOAN_FLAG = bytes32(uint256(1 << 162)); bytes32 internal constant AFTER_FLASH_LOAN_FLAG = bytes32(uint256(1 << 163)); bytes32 internal constant BEFORE_MINT_FLAG = bytes32(uint256(1 << 164)); bytes32 internal constant AFTER_MINT_FLAG = bytes32(uint256(1 << 165)); bytes32 internal constant BEFORE_BURN_FLAG = bytes32(uint256(1 << 166)); bytes32 internal constant AFTER_BURN_FLAG = bytes32(uint256(1 << 167)); bytes32 internal constant BEFORE_TRANSFER_FLAG = bytes32(uint256(1 << 168)); bytes32 internal constant AFTER_TRANSFER_FLAG = bytes32(uint256(1 << 169)); struct Parameters { address hooks; bool beforeSwap; bool afterSwap; bool beforeFlashLoan; bool afterFlashLoan; bool beforeMint; bool afterMint; bool beforeBurn; bool afterBurn; bool beforeBatchTransferFrom; bool afterBatchTransferFrom; } /** * @dev Helper function to encode the hooks parameters to a single bytes32 value * @param parameters The hooks parameters * @return hooksParameters The encoded hooks parameters */ function encode(Parameters memory parameters) internal pure returns (bytes32 hooksParameters) { hooksParameters = bytes32(uint256(uint160(address(parameters.hooks)))); if (parameters.beforeSwap) hooksParameters |= BEFORE_SWAP_FLAG; if (parameters.afterSwap) hooksParameters |= AFTER_SWAP_FLAG; if (parameters.beforeFlashLoan) hooksParameters |= BEFORE_FLASH_LOAN_FLAG; if (parameters.afterFlashLoan) hooksParameters |= AFTER_FLASH_LOAN_FLAG; if (parameters.beforeMint) hooksParameters |= BEFORE_MINT_FLAG; if (parameters.afterMint) hooksParameters |= AFTER_MINT_FLAG; if (parameters.beforeBurn) hooksParameters |= BEFORE_BURN_FLAG; if (parameters.afterBurn) hooksParameters |= AFTER_BURN_FLAG; if (parameters.beforeBatchTransferFrom) hooksParameters |= BEFORE_TRANSFER_FLAG; if (parameters.afterBatchTransferFrom) hooksParameters |= AFTER_TRANSFER_FLAG; } /** * @dev Helper function to decode the hooks parameters from a single bytes32 value * @param hooksParameters The encoded hooks parameters * @return parameters The hooks parameters */ function decode(bytes32 hooksParameters) internal pure returns (Parameters memory parameters) { parameters.hooks = getHooks(hooksParameters); parameters.beforeSwap = (hooksParameters & BEFORE_SWAP_FLAG) != 0; parameters.afterSwap = (hooksParameters & AFTER_SWAP_FLAG) != 0; parameters.beforeFlashLoan = (hooksParameters & BEFORE_FLASH_LOAN_FLAG) != 0; parameters.afterFlashLoan = (hooksParameters & AFTER_FLASH_LOAN_FLAG) != 0; parameters.beforeMint = (hooksParameters & BEFORE_MINT_FLAG) != 0; parameters.afterMint = (hooksParameters & AFTER_MINT_FLAG) != 0; parameters.beforeBurn = (hooksParameters & BEFORE_BURN_FLAG) != 0; parameters.afterBurn = (hooksParameters & AFTER_BURN_FLAG) != 0; parameters.beforeBatchTransferFrom = (hooksParameters & BEFORE_TRANSFER_FLAG) != 0; parameters.afterBatchTransferFrom = (hooksParameters & AFTER_TRANSFER_FLAG) != 0; } /** * @dev Helper function to get the hooks address from the encoded hooks parameters * @param hooksParameters The encoded hooks parameters * @return hooks The hooks address */ function getHooks(bytes32 hooksParameters) internal pure returns (address hooks) { hooks = address(uint160(uint256(hooksParameters))); } /** * @dev Helper function to set the hooks address in the encoded hooks parameters * @param hooksParameters The encoded hooks parameters * @param newHooks The new hooks address * @return hooksParameters The updated hooks parameters */ function setHooks(bytes32 hooksParameters, address newHooks) internal pure returns (bytes32) { return bytes32(bytes12(hooksParameters)) | bytes32(uint256(uint160(newHooks))); } /** * @dev Helper function to get the flags from the encoded hooks parameters * @param hooksParameters The encoded hooks parameters * @return flags The flags */ function getFlags(bytes32 hooksParameters) internal pure returns (bytes12 flags) { flags = bytes12(hooksParameters); } /** * @dev Helper function call the onHooksSet function on the hooks contract, only if the * hooksParameters is not 0 * @param hooksParameters The encoded hooks parameters * @param onHooksSetData The data to pass to the onHooksSet function */ function onHooksSet(bytes32 hooksParameters, bytes calldata onHooksSetData) internal { if (hooksParameters != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.onHooksSet.selector, hooksParameters, onHooksSetData) ); } } /** * @dev Helper function to call the beforeSwap function on the hooks contract, only if the * BEFORE_SWAP_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param swapForY Whether the swap is for Y * @param amountsIn The amounts in */ function beforeSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsIn) internal { if ((hooksParameters & BEFORE_SWAP_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeSwap.selector, sender, to, swapForY, amountsIn) ); } } /** * @dev Helper function to call the afterSwap function on the hooks contract, only if the * AFTER_SWAP_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param swapForY Whether the swap is for Y * @param amountsOut The amounts out */ function afterSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsOut) internal { if ((hooksParameters & AFTER_SWAP_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterSwap.selector, sender, to, swapForY, amountsOut) ); } } /** * @dev Helper function to call the beforeFlashLoan function on the hooks contract, only if the * BEFORE_FLASH_LOAN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param amounts The amounts */ function beforeFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 amounts) internal { if ((hooksParameters & BEFORE_FLASH_LOAN_FLAG) != 0) { _safeCall(hooksParameters, abi.encodeWithSelector(ILBHooks.beforeFlashLoan.selector, sender, to, amounts)); } } /** * @dev Helper function to call the afterFlashLoan function on the hooks contract, only if the * AFTER_FLASH_LOAN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param fees The fees * @param feesReceived The fees received */ function afterFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 fees, bytes32 feesReceived) internal { if ((hooksParameters & AFTER_FLASH_LOAN_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterFlashLoan.selector, sender, to, fees, feesReceived) ); } } /** * @dev Helper function to call the beforeMint function on the hooks contract, only if the * BEFORE_MINT_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param liquidityConfigs The liquidity configs * @param amountsReceived The amounts received */ function beforeMint( bytes32 hooksParameters, address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsReceived ) internal { if ((hooksParameters & BEFORE_MINT_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeMint.selector, sender, to, liquidityConfigs, amountsReceived) ); } } /** * @dev Helper function to call the afterMint function on the hooks contract, only if the * AFTER_MINT_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param liquidityConfigs The liquidity configs * @param amountsIn The amounts in */ function afterMint( bytes32 hooksParameters, address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsIn ) internal { if ((hooksParameters & AFTER_MINT_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterMint.selector, sender, to, liquidityConfigs, amountsIn) ); } } /** * @dev Helper function to call the beforeBurn function on the hooks contract, only if the * BEFORE_BURN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The ids * @param amountsToBurn The amounts to burn */ function beforeBurn( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) internal { if ((hooksParameters & BEFORE_BURN_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeBurn.selector, sender, from, to, ids, amountsToBurn) ); } } /** * @dev Helper function to call the afterBurn function on the hooks contract, only if the * AFTER_BURN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The ids * @param amountsToBurn The amounts to burn */ function afterBurn( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) internal { if ((hooksParameters & AFTER_BURN_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterBurn.selector, sender, from, to, ids, amountsToBurn) ); } } /** * @dev Helper function to call the beforeTransferFrom function on the hooks contract, only if the * BEFORE_TRANSFER_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The list of ids * @param amounts The list of amounts */ function beforeBatchTransferFrom( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) internal { if ((hooksParameters & BEFORE_TRANSFER_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeBatchTransferFrom.selector, sender, from, to, ids, amounts) ); } } /** * @dev Helper function to call the afterTransferFrom function on the hooks contract, only if the * AFTER_TRANSFER_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The list of ids * @param amounts The list of amounts */ function afterBatchTransferFrom( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) internal { if ((hooksParameters & AFTER_TRANSFER_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterBatchTransferFrom.selector, sender, from, to, ids, amounts) ); } } /** * @dev Helper function to call the hooks contract and verify the call was successful * by matching the expected selector with the returned data * @param hooksParameters The encoded hooks parameters * @param data The data to pass to the hooks contract */ function _safeCall(bytes32 hooksParameters, bytes memory data) private { bool success; address hooks = getHooks(hooksParameters); assembly { let expectedSelector := shr(224, mload(add(data, 0x20))) success := call(gas(), hooks, 0, add(data, 0x20), mload(data), 0, 0x20) if and(iszero(success), iszero(iszero(returndatasize()))) { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } success := and(success, and(gt(returndatasize(), 0x1f), eq(shr(224, mload(0)), expectedSelector))) } if (!success) revert Hooks__CallFailed(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {ILBPair} from "./ILBPair.sol"; import {Hooks} from "../libraries/Hooks.sol"; interface ILBHooks { function getLBPair() external view returns (ILBPair); function isLinked() external view returns (bool); function onHooksSet(bytes32 hooksParameters, bytes calldata onHooksSetData) external returns (bytes4); function beforeSwap(address sender, address to, bool swapForY, bytes32 amountsIn) external returns (bytes4); function afterSwap(address sender, address to, bool swapForY, bytes32 amountsOut) external returns (bytes4); function beforeFlashLoan(address sender, address to, bytes32 amounts) external returns (bytes4); function afterFlashLoan(address sender, address to, bytes32 fees, bytes32 feesReceived) external returns (bytes4); function beforeMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsReceived) external returns (bytes4); function afterMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsIn) external returns (bytes4); function beforeBurn( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) external returns (bytes4); function afterBurn( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) external returns (bytes4); function beforeBatchTransferFrom( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) external returns (bytes4); function afterBatchTransferFrom( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 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 SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Encoded Library * @author Trader Joe * @notice Helper contract used for decoding bytes32 sample */ library Encoded { uint256 internal constant MASK_UINT1 = 0x1; uint256 internal constant MASK_UINT8 = 0xff; uint256 internal constant MASK_UINT12 = 0xfff; uint256 internal constant MASK_UINT14 = 0x3fff; uint256 internal constant MASK_UINT16 = 0xffff; uint256 internal constant MASK_UINT20 = 0xfffff; uint256 internal constant MASK_UINT24 = 0xffffff; uint256 internal constant MASK_UINT40 = 0xffffffffff; uint256 internal constant MASK_UINT64 = 0xffffffffffffffff; uint256 internal constant MASK_UINT128 = 0xffffffffffffffffffffffffffffffff; /** * @notice Internal function to set a value in an encoded bytes32 using a mask and offset * @dev This function can overflow * @param encoded The previous encoded value * @param value The value to encode * @param mask The mask * @param offset The offset * @return newEncoded The new encoded value */ function set(bytes32 encoded, uint256 value, uint256 mask, uint256 offset) internal pure returns (bytes32 newEncoded) { assembly { newEncoded := and(encoded, not(shl(offset, mask))) newEncoded := or(newEncoded, shl(offset, and(value, mask))) } } /** * @notice Internal function to set a bool in an encoded bytes32 using an offset * @dev This function can overflow * @param encoded The previous encoded value * @param boolean The bool to encode * @param offset The offset * @return newEncoded The new encoded value */ function setBool(bytes32 encoded, bool boolean, uint256 offset) internal pure returns (bytes32 newEncoded) { return set(encoded, boolean ? 1 : 0, MASK_UINT1, offset); } /** * @notice Internal function to decode a bytes32 sample using a mask and offset * @dev This function can overflow * @param encoded The encoded value * @param mask The mask * @param offset The offset * @return value The decoded value */ function decode(bytes32 encoded, uint256 mask, uint256 offset) internal pure returns (uint256 value) { assembly { value := and(shr(offset, encoded), mask) } } /** * @notice Internal function to decode a bytes32 sample into a bool using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return boolean The decoded value as a bool */ function decodeBool(bytes32 encoded, uint256 offset) internal pure returns (bool boolean) { assembly { boolean := and(shr(offset, encoded), MASK_UINT1) } } /** * @notice Internal function to decode a bytes32 sample into a uint8 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint8(bytes32 encoded, uint256 offset) internal pure returns (uint8 value) { assembly { value := and(shr(offset, encoded), MASK_UINT8) } } /** * @notice Internal function to decode a bytes32 sample into a uint12 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value as a uint16, since uint12 is not supported */ function decodeUint12(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) { assembly { value := and(shr(offset, encoded), MASK_UINT12) } } /** * @notice Internal function to decode a bytes32 sample into a uint14 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value as a uint16, since uint14 is not supported */ function decodeUint14(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) { assembly { value := and(shr(offset, encoded), MASK_UINT14) } } /** * @notice Internal function to decode a bytes32 sample into a uint16 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint16(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) { assembly { value := and(shr(offset, encoded), MASK_UINT16) } } /** * @notice Internal function to decode a bytes32 sample into a uint20 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value as a uint24, since uint20 is not supported */ function decodeUint20(bytes32 encoded, uint256 offset) internal pure returns (uint24 value) { assembly { value := and(shr(offset, encoded), MASK_UINT20) } } /** * @notice Internal function to decode a bytes32 sample into a uint24 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint24(bytes32 encoded, uint256 offset) internal pure returns (uint24 value) { assembly { value := and(shr(offset, encoded), MASK_UINT24) } } /** * @notice Internal function to decode a bytes32 sample into a uint40 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint40(bytes32 encoded, uint256 offset) internal pure returns (uint40 value) { assembly { value := and(shr(offset, encoded), MASK_UINT40) } } /** * @notice Internal function to decode a bytes32 sample into a uint64 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint64(bytes32 encoded, uint256 offset) internal pure returns (uint64 value) { assembly { value := and(shr(offset, encoded), MASK_UINT64) } } /** * @notice Internal function to decode a bytes32 sample into a uint128 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint128(bytes32 encoded, uint256 offset) internal pure returns (uint128 value) { assembly { value := and(shr(offset, encoded), MASK_UINT128) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Token Interface * @author Trader Joe * @notice Interface to interact with the LBToken. */ interface ILBToken { error LBToken__AddressThisOrZero(); error LBToken__InvalidLength(); error LBToken__SelfApproval(address owner); error LBToken__SpenderNotApproved(address from, address spender); error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount); error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount); event TransferBatch( address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts ); event ApprovalForAll(address indexed account, address indexed sender, bool approved); function name() external view returns (string memory); function symbol() external view returns (string memory); function totalSupply(uint256 id) external view returns (uint256); function balanceOf(address account, uint256 id) external view returns (uint256); function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); function isApprovedForAll(address owner, address spender) external view returns (bool); function approveForAll(address spender, bool approved) external; function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {Constants} from "../Constants.sol"; import {BitMath} from "./BitMath.sol"; /** * @title Liquidity Book Uint128x128 Math Library * @author Trader Joe * @notice Helper contract used for power and log calculations */ library Uint128x128Math { using BitMath for uint256; error Uint128x128Math__LogUnderflow(); error Uint128x128Math__PowUnderflow(uint256 x, int256 y); uint256 constant LOG_SCALE_OFFSET = 127; uint256 constant LOG_SCALE = 1 << LOG_SCALE_OFFSET; uint256 constant LOG_SCALE_SQUARED = LOG_SCALE * LOG_SCALE; /** * @notice Calculates the binary logarithm of x. * @dev Based on the iterative approximation algorithm. * https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation * Requirements: * - x must be greater than zero. * Caveats: * - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation * Also because x is converted to an unsigned 129.127-binary fixed-point number during the operation to optimize the multiplication * @param x The unsigned 128.128-binary fixed-point number for which to calculate the binary logarithm. * @return result The binary logarithm as a signed 128.128-binary fixed-point number. */ function log2(uint256 x) internal pure returns (int256 result) { // Convert x to a unsigned 129.127-binary fixed-point number to optimize the multiplication. // If we use an offset of 128 bits, y would need 129 bits and y**2 would would overflow and we would have to // use mulDiv, by reducing x to 129.127-binary fixed-point number we assert that y will use 128 bits, and we // can use the regular multiplication if (x == 1) return -128; if (x == 0) revert Uint128x128Math__LogUnderflow(); x >>= 1; unchecked { // This works because log2(x) = -log2(1/x). int256 sign; if (x >= LOG_SCALE) { sign = 1; } else { sign = -1; // Do the fixed-point inversion inline to save gas x = LOG_SCALE_SQUARED / x; } // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n). uint256 n = (x >> LOG_SCALE_OFFSET).mostSignificantBit(); // The integer part of the logarithm as a signed 129.127-binary fixed-point number. The operation can't overflow // because n is maximum 255, LOG_SCALE_OFFSET is 127 bits and sign is either 1 or -1. result = int256(n) << LOG_SCALE_OFFSET; // This is y = x * 2^(-n). uint256 y = x >> n; // If y = 1, the fractional part is zero. if (y != LOG_SCALE) { // Calculate the fractional part via the iterative approximation. // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. for (int256 delta = int256(1 << (LOG_SCALE_OFFSET - 1)); delta > 0; delta >>= 1) { y = (y * y) >> LOG_SCALE_OFFSET; // Is y^2 > 2 and so in the range [2,4)? if (y >= 1 << (LOG_SCALE_OFFSET + 1)) { // Add the 2^(-m) factor to the logarithm. result += delta; // Corresponds to z/2 on Wikipedia. y >>= 1; } } } // Convert x back to unsigned 128.128-binary fixed-point number result = (result * sign) << 1; } } /** * @notice Returns the value of x^y. It calculates `1 / x^abs(y)` if x is bigger than 2^128. * At the end of the operations, we invert the result if needed. * @param x The unsigned 128.128-binary fixed-point number for which to calculate the power * @param y A relative number without any decimals, needs to be between ]-2^21; 2^21[ */ function pow(uint256 x, int256 y) internal pure returns (uint256 result) { bool invert; uint256 absY; if (y == 0) return Constants.SCALE; assembly { absY := y if slt(absY, 0) { absY := sub(0, absY) invert := iszero(invert) } } if (absY < 0x100000) { result = Constants.SCALE; assembly { let squared := x if gt(x, 0xffffffffffffffffffffffffffffffff) { squared := div(not(0), squared) invert := iszero(invert) } if and(absY, 0x1) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x2) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x4) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x8) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x10) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x20) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x40) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x80) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x100) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x200) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x400) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x800) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x1000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x2000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x4000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x8000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x10000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x20000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x40000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x80000) { result := shr(128, mul(result, squared)) } } } // revert if y is too big or if x^y underflowed if (result == 0) revert Uint128x128Math__PowUnderflow(x, y); return invert ? type(uint256).max / result : result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @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 // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 360 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": false, "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":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"BinHelper__CompositionFactorFlawed","type":"error"},{"inputs":[],"name":"BinHelper__LiquidityOverflow","type":"error"},{"inputs":[],"name":"BinHelper__MaxLiquidityPerBinExceeded","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"FeeHelper__FeeTooLarge","type":"error"},{"inputs":[],"name":"Hooks__CallFailed","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"LBPair__AddressZero","type":"error"},{"inputs":[],"name":"LBPair__EmptyMarketConfigs","type":"error"},{"inputs":[],"name":"LBPair__FlashLoanCallbackFailed","type":"error"},{"inputs":[],"name":"LBPair__FlashLoanInsufficientAmount","type":"error"},{"inputs":[],"name":"LBPair__InsufficientAmountIn","type":"error"},{"inputs":[],"name":"LBPair__InsufficientAmountOut","type":"error"},{"inputs":[],"name":"LBPair__InvalidHooks","type":"error"},{"inputs":[],"name":"LBPair__InvalidInput","type":"error"},{"inputs":[],"name":"LBPair__InvalidStaticFeeParameters","type":"error"},{"inputs":[],"name":"LBPair__MaxTotalFeeExceeded","type":"error"},{"inputs":[],"name":"LBPair__OnlyFactory","type":"error"},{"inputs":[],"name":"LBPair__OnlyProtocolFeeRecipient","type":"error"},{"inputs":[],"name":"LBPair__OutOfLiquidity","type":"error"},{"inputs":[],"name":"LBPair__TokenNotSupported","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"LBPair__ZeroAmount","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"LBPair__ZeroAmountsOut","type":"error"},{"inputs":[],"name":"LBPair__ZeroBorrowAmount","type":"error"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"LBPair__ZeroShares","type":"error"},{"inputs":[],"name":"LBToken__AddressThisOrZero","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LBToken__BurnExceedsBalance","type":"error"},{"inputs":[],"name":"LBToken__InvalidLength","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"LBToken__SelfApproval","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"LBToken__SpenderNotApproved","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LBToken__TransferExceedsBalance","type":"error"},{"inputs":[],"name":"LiquidityConfigurations__InvalidConfig","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OracleHelper__InvalidOracleId","type":"error"},{"inputs":[],"name":"OracleHelper__LookUpTimestampTooOld","type":"error"},{"inputs":[],"name":"OracleHelper__NewLengthTooSmall","type":"error"},{"inputs":[],"name":"PackedUint128Math__AddOverflow","type":"error"},{"inputs":[],"name":"PackedUint128Math__MultiplierTooLarge","type":"error"},{"inputs":[],"name":"PackedUint128Math__SubUnderflow","type":"error"},{"inputs":[],"name":"PairParametersHelper__InvalidParameter","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SafeCast__Exceeds128Bits","type":"error"},{"inputs":[],"name":"SafeCast__Exceeds24Bits","type":"error"},{"inputs":[],"name":"SafeCast__Exceeds40Bits","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"Uint128x128Math__LogUnderflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"Uint128x128Math__PowUnderflow","type":"error"},{"inputs":[],"name":"Uint256x256Math__MulDivOverflow","type":"error"},{"inputs":[],"name":"Uint256x256Math__MulShiftOverflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"CollectedProtocolFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"totalFees","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"CompositionFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"bytes32[]","name":"amounts","type":"bytes32[]"}],"name":"DepositedToBins","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"contract ILBFlashLoanCallback","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint24","name":"activeId","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"amounts","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"totalFees","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint24","name":"idReference","type":"uint24"},{"indexed":false,"internalType":"uint24","name":"volatilityReference","type":"uint24"}],"name":"ForcedDecay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"bytes32","name":"hooksParameters","type":"bytes32"}],"name":"HooksParametersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint16","name":"oracleLength","type":"uint16"}],"name":"OracleLengthIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint16","name":"baseFactor","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"indexed":false,"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"indexed":false,"internalType":"uint16","name":"protocolShare","type":"uint16"},{"indexed":false,"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"}],"name":"StaticFeeParametersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"amountsIn","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"amountsOut","type":"bytes32"},{"indexed":false,"internalType":"uint24","name":"volatilityAccumulator","type":"uint24"},{"indexed":false,"internalType":"bytes32","name":"totalFees","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"protocolFees","type":"bytes32"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"bytes32[]","name":"amounts","type":"bytes32[]"}],"name":"WithdrawnFromBins","type":"event"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"approveForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"batchBalances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"batchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsToBurn","type":"uint256[]"}],"name":"burn","outputs":[{"internalType":"bytes32[]","name":"amounts","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectProtocolFees","outputs":[{"internalType":"bytes32","name":"collectedProtocolFees","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILBFlashLoanCallback","name":"receiver","type":"address"},{"internalType":"bytes32","name":"amounts","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forceDecay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getActiveId","outputs":[{"internalType":"uint24","name":"activeId","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getBin","outputs":[{"internalType":"uint128","name":"binReserveX","type":"uint128"},{"internalType":"uint128","name":"binReserveY","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBinStep","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFactory","outputs":[{"internalType":"contract ILBFactory","name":"factory","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"getIdFromPrice","outputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getLBHooksParameters","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"swapForY","type":"bool"},{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getNextNonEmptyBin","outputs":[{"internalType":"uint24","name":"nextId","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleParameters","outputs":[{"internalType":"uint8","name":"sampleLifetime","type":"uint8"},{"internalType":"uint16","name":"size","type":"uint16"},{"internalType":"uint16","name":"activeSize","type":"uint16"},{"internalType":"uint40","name":"lastUpdated","type":"uint40"},{"internalType":"uint40","name":"firstTimestamp","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint40","name":"lookupTimestamp","type":"uint40"}],"name":"getOracleSampleAt","outputs":[{"internalType":"uint64","name":"cumulativeId","type":"uint64"},{"internalType":"uint64","name":"cumulativeVolatility","type":"uint64"},{"internalType":"uint64","name":"cumulativeBinCrossed","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getPriceFromId","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getProtocolFees","outputs":[{"internalType":"uint128","name":"protocolFeeX","type":"uint128"},{"internalType":"uint128","name":"protocolFeeY","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint128","name":"reserveX","type":"uint128"},{"internalType":"uint128","name":"reserveY","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticFeeParameters","outputs":[{"internalType":"uint16","name":"baseFactor","type":"uint16"},{"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"internalType":"uint16","name":"protocolShare","type":"uint16"},{"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"amountOut","type":"uint128"},{"internalType":"bool","name":"swapForY","type":"bool"}],"name":"getSwapIn","outputs":[{"internalType":"uint128","name":"amountIn","type":"uint128"},{"internalType":"uint128","name":"amountOutLeft","type":"uint128"},{"internalType":"uint128","name":"fee","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"amountIn","type":"uint128"},{"internalType":"bool","name":"swapForY","type":"bool"}],"name":"getSwapOut","outputs":[{"internalType":"uint128","name":"amountInLeft","type":"uint128"},{"internalType":"uint128","name":"amountOut","type":"uint128"},{"internalType":"uint128","name":"fee","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenX","outputs":[{"internalType":"contract IERC20","name":"tokenX","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTokenY","outputs":[{"internalType":"contract IERC20","name":"tokenY","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getVariableFeeParameters","outputs":[{"internalType":"uint24","name":"volatilityAccumulator","type":"uint24"},{"internalType":"uint24","name":"volatilityReference","type":"uint24"},{"internalType":"uint24","name":"idReference","type":"uint24"},{"internalType":"uint40","name":"timeOfLastUpdate","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"newLength","type":"uint16"}],"name":"increaseOracleLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"baseFactor","type":"uint16"},{"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"internalType":"uint16","name":"protocolShare","type":"uint16"},{"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"},{"internalType":"uint24","name":"activeId","type":"uint24"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes32[]","name":"liquidityConfigs","type":"bytes32[]"},{"internalType":"address","name":"refundTo","type":"address"}],"name":"mint","outputs":[{"internalType":"bytes32","name":"amountsReceived","type":"bytes32"},{"internalType":"bytes32","name":"amountsLeft","type":"bytes32"},{"internalType":"uint256[]","name":"liquidityMinted","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hooksParameters","type":"bytes32"},{"internalType":"bytes","name":"onHooksSetData","type":"bytes"}],"name":"setHooksParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"baseFactor","type":"uint16"},{"internalType":"uint16","name":"filterPeriod","type":"uint16"},{"internalType":"uint16","name":"decayPeriod","type":"uint16"},{"internalType":"uint16","name":"reductionFactor","type":"uint16"},{"internalType":"uint24","name":"variableFeeControl","type":"uint24"},{"internalType":"uint16","name":"protocolShare","type":"uint16"},{"internalType":"uint24","name":"maxVolatilityAccumulator","type":"uint24"}],"name":"setStaticFeeParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"swapForY","type":"bool"},{"internalType":"address","name":"to","type":"address"}],"name":"swap","outputs":[{"internalType":"bytes32","name":"amountsOut","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60c06040523480156200001157600080fd5b50604051620061733803806200617383398101604081905262000034916200010a565b6001600160a01b03811660a052306080526200004f62000056565b506200013c565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000a75760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620001075780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6000602082840312156200011d57600080fd5b81516001600160a01b03811681146200013557600080fd5b9392505050565b60805160a051615fe76200018c600039600081816104b5015281816107b801528181610bc60152818161125f0152818161147901528181611baf0152613638015260006104220152615fe76000f3fe608060405234801561001057600080fd5b50600436106102265760003560e01c806388cc58e411610130578063c9939f5e116100b8578063e584b6541161007c578063e584b65414610652578063e77366f814610665578063e985e9c514610678578063ea3446bf1461069b578063f5e29329146106ae57600080fd5b8063c9939f5e1461060f578063d3b9fbe414610622578063d8dfcea01461062a578063da10610c14610632578063dbe65edc1461064a57600080fd5b8063a1af5b9a116100ff578063a1af5b9a14610570578063a41a01fb14610578578063abcd78301461059f578063bd85b039146105dc578063c7bd6586146105fc57600080fd5b806388cc58e4146104b35780638940a16a146104d95780638d7024e51461051657806395d89b411461055157600080fd5b806347973bff116101b3578063551828941161018257806355182894146103d85780635c60da1b1461041d5780636653851a14610444578063781a8915146104575780637ca0de301461046157600080fd5b806347973bff1461037f5780634c7cffbd146103925780634e1273f4146103a557806353c059a0146103c557600080fd5b80630abe9688116101fa5780630abe9688146102e457806317f11ecc1461031a57806317fad7fc146103355780631b057f6d1461034a578063383d15c51461035d57600080fd5b8062fdd58e1461022b57806305e8746d1461025157806306fdde031461027a5780630902f1ac146102bc575b600080fd5b61023e61023936600461556f565b6106c1565b6040519081526020015b60405180910390f35b3660011981013560f01c90033560601c5b6040516001600160a01b039091168152602001610248565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b60405161024891906155bf565b6102c46106ef565b604080516001600160801b03938416815292909116602083015201610248565b6102c46102f2366004615605565b62ffffff166000908152600660205260409020546001600160801b0381169160809190911c90565b610322610729565b60405161ffff9091168152602001610248565b610348610343366004615664565b61074a565b005b610348610358366004615739565b6107a5565b61037061036b366004615784565b610905565b60405161024893929190615826565b61034861038d366004615857565b610bbb565b61023e6103a0366004615605565b610d4e565b6103b86103b33660046158ee565b610d7e565b6040516102489190615959565b61023e6103d336600461597a565b610e69565b6103e06111a2565b6040805160ff909616865261ffff9485166020870152939092169284019290925264ffffffffff918216606084015216608082015260a001610248565b6102627f000000000000000000000000000000000000000000000000000000000000000081565b6103486104523660046159b3565b61124c565b620100095461023e565b6104696112c6565b6040805161ffff9889168152968816602088015294871694860194909452918516606085015262ffffff9081166080850152931660a08301529190911660c082015260e001610248565b7f0000000000000000000000000000000000000000000000000000000000000000610262565b6104ec6104e7366004615a39565b61132e565b604080516001600160401b0394851681529284166020840152921691810191909152606001610248565b61051e61142d565b6040805162ffffff95861681529385166020850152919093169082015264ffffffffff9091166060820152608001610248565b60408051808201909152600381526213109560ea1b60208201526102af565b61023e61146d565b61058b610586366004615a60565b611625565b60405162ffffff9091168152602001610248565b6105b26105ad366004615a8c565b611638565b604080516001600160801b0394851681529284166020840152921691810191909152606001610248565b61023e6105ea366004615ac6565b60009081526001602052604090205490565b61034861060a366004615adf565b6117dd565b6103b861061d366004615664565b611873565b610348611b9c565b6102c4611c71565b3660011981013560f01c90036014013560601c610262565b61058b611c8c565b610348610660366004615afa565b611c99565b6105b2610673366004615a8c565b611ca8565b61068b610686366004615b18565b611dee565b6040519015158152602001610248565b6103486106a9366004615b36565b611dfa565b61058b6106bc366004615ac6565b6120c0565b6001600160a01b0382166000908152602081815260408083208484529091529020545b92915050565b905090565b60008061072061070c6005546004546120eb90919063ffffffff16565b6001600160801b0381169160809190911c90565b90939092509050565b60006106ea60003660011981013560f090811c90910360280135901c905090565b610752612125565b6201000954610767813389898989898961215d565b6107758787878787876121d3565b61078c6001600080516020615f9283398151915255565b61079c8133898989898989612231565b50505050505050565b6107ad612125565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107f657604051632486085360e01b815260040160405180910390fd5b62010009839055604080518481529051849133917f4d24d81a1a6e09c37497747b4c9d67d858ce80438b54df0fa361a24dc742d9349181900360200190a26001600160a01b038116158015906108bf5750306001600160a01b0316816001600160a01b0316635f9c01b16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561088f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b39190615b85565b6001600160a01b031614155b156108dd57604051631b5de6eb60e01b815260040160405180910390fd5b6108e8848484612266565b506109006001600080516020615f9283398151915255565b505050565b6000806060866001600160a01b038116158061092957506001600160a01b03811630145b15610947576040516345c210e760e11b815260040160405180910390fd5b61094f612125565b60008690036109715760405163296db0d960e01b815260040160405180910390fd5b6201000954604080516060810190915260009080896001600160401b0381111561099d5761099d615ba2565b6040519080825280602002602001820160405280156109c6578160200160208202803683370190505b508152602001896001600160401b038111156109e4576109e4615ba2565b604051908082528060200260200182016040528015610a0d578160200160208202803683370190505b508152602001896001600160401b03811115610a2b57610a2b615ba2565b604051908082528060200260200182016040528015610a54578160200160208202803683370190505b509052600454909150610a80813660011981013560f01c90038035606090811c9160140135901c61228d565b9650610a9083338d8d8d8c6122cd565b610a9d8a8a898e86612306565b9550610ab3610aac88886120eb565b829061243b565b600455604080830151835191519096506001600160a01b038d169160009133917f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb91610b0191908b90615bb8565b60405180910390a4815160208301516040516001600160a01b038e169233927f87f1f9dcf5e8089a3e00811b6a008d8f30293a3da878cb1fe8c90ca376402f8a92610b4d929190615bb8565b60405180910390a38515610b7f57610b7f863660011981013560f01c90038035606090811c9160140135901c8b612475565b610b966001600080516020615f9283398151915255565b610bad83338d8d8d610ba88d8d6120eb565b6124d3565b505050509450945094915050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c0457604051632486085360e01b815260040160405180910390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015610c495750825b90506000826001600160401b03166001148015610c655750303b155b905081158015610c73575080155b15610c915760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610cc057845468ff00000000000000001916600160401b1785555b610cc8612504565b610cf4610ce8610ce38860035461251490919063ffffffff16565b61254d565b8e8e8e8e8e8e8e612578565b8315610d3f57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b60006106e4610d7260003660011981013560f090811c90910360280135901c905090565b62ffffff8416906126ff565b60608382808214610da2576040516340311ffd60e11b815260040160405180910390fd5b856001600160401b03811115610dba57610dba615ba2565b604051908082528060200260200182016040528015610de3578160200160208202803683370190505b50925060005b86811015610e5e57610e39888883818110610e0657610e06615bdd565b9050602002016020810190610e1b9190615bf3565b878784818110610e2d57610e2d615bdd565b905060200201356106c1565b848281518110610e4b57610e4b615bdd565b6020908102919091010152600101610de9565b505050949350505050565b6000610e73612125565b6201000954600454600554600086610ea757610ea23660011981013560f01c90036014013560601c8490612736565b610ec1565b610ec13660011981013560f01c90033560601c849061276b565b90506000819003610ee557604051637df801c760e01b815260040160405180910390fd5b86610ef3853389848661279a565b610efd848361243b565b6003549094506000610f2460003660011981013560f090811c90910360280135901c905090565b90506000610f31836127ed565b9050610f3d83426127f9565b92505b62ffffff8116600090815260066020526040902054610f6081861561286f565b61108457610f6e848361289e565b935060008080610f828488888b898e612926565b91945092509050821561108057610f9989846120eb565b9850610fa58d8361243b565b9c506000610fc0610fb589612b4c565b839061ffff16612b5c565b90508015610fe157610fd28b8261243b565b9a50610fde84826120eb565b93505b610ff583610fef878761243b565b906120eb565b62ffffff87166000908152600660205260409020556001600160a01b038f16337fad7d6f97abf51ce18e17a38f4d70e975be9c0708474987bb3e26ad21bd93ca708887876110428e612bd2565b6040805162ffffff9586168152602081019490945283019190915290911660608201526080810186905260a0810185905260c00160405180910390a3505b5050505b600086900361109357506110dd565b600061109f8684612be3565b905062ffffff811615806110b7575062ffffff818116145b156110d557604051631a6d7fb160e31b815260040160405180910390fd5b915050610f40565b60008990036110ff5760405163439df85d60e11b815260040160405180910390fd5b611109878a6120eb565b600455600586905561111d600a8483612c05565b92506111298382612514565b6003558315611155576111503660011981013560f01c90036014013560601c8a908c612d20565b611170565b6111703660011981013560f01c90033560601c8a908c612d60565b6111876001600080516020615f9283398151915255565b61119488338c878d612d71565b505050505050505092915050565b600354607890600090819081908190816111bb82612dbd565b905061ffff8116156112435760006111d4600a83612dcd565b965090506111e181612e2b565b96506111ec81612e37565b94508464ffffffffff1660000361120257600095505b61ffff861615611241576112338661ffff168361ffff168161122657611226615c10565b600a916001910601612e58565b905061123e81612e37565b93505b505b50509091929394565b611254612125565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461129d57604051632486085360e01b815260040160405180910390fd5b6112af60035488888888888888612578565b61079c6001600080516020615f9283398151915255565b60008060008060008060008060035490506112e081612e2b565b97506112eb81612ea4565b96506112f681612eb4565b955061130181612ec4565b945061130c81612ed4565b935061131781612b4c565b925061132281612ee5565b91505090919293949596565b6000806000806003549050600061134482612dbd565b905061ffff8116158061135d5750428664ffffffffff16115b156113745760008060009450945094505050611426565b6000611382600a8389612ef6565b91985096509450905064ffffffffff8088169082161015611422576113b86113a9846127ed565b849064ffffffffff8a16613019565b925060006113c68289615c3c565b90508064ffffffffff166113d9856127ed565b62ffffff166113e89190615c5a565b6113f29088615c85565b96508064ffffffffff1661140585612bd2565b62ffffff166114149190615c5a565b61141e9087615c85565b9550505b5050505b9193909250565b6000806000806000600354905061144381612bd2565b945061144e81613031565b935061145981613042565b925061146481613053565b91505090919293565b6000611477612125565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634ccb20c06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f99190615b85565b6001600160a01b0316336001600160a01b03161461152a57604051630f8937c560e21b815260040160405180910390fd5b6005546001600160801b038116608082901c60006115858261154d576000611550565b60015b60ff166000856001600160801b03161161156b57600061156e565b60015b60ff169060801b6001600160801b03919091161790565b905061159184826120eb565b945084156116075760058190556004546115ab90866120eb565b60045560405185815233907f3f41a5ddc53701cc7db577ade4f1fca9838a8ec0b5ea50b9f0f5d17bc4554e329060200160405180910390a2611607853660011981013560f01c90038035606090811c9160140135901c33612475565b505050506116226001600080516020615f9283398151915255565b90565b60006116318383612be3565b9392505050565b600354600090839082908161166260003660011981013560f090811c90910360280135901c905090565b9050600061166f836127ed565b905061167b83426127f9565b92505b62ffffff811660009081526006602052604081205461169e908915613066565b90506001600160801b038116156117845760006116c062ffffff8416856126ff565b90506000876001600160801b0316836001600160801b0316116116e357826116e5565b875b90506116f1868561289e565b955060008a6117145761170f6001600160801b038316846080613086565b611729565b6117296001600160801b0383166080856130bc565b9050600061173788886130f2565b9050600061174e6001600160801b03841683613111565b905061175a8184615ca5565b611764908d615ca5565b9b50611770848c615cc5565b9a5061177c818b615ca5565b995050505050505b856001600160801b031660000361179b57506117d3565b60006117a78984612be3565b905062ffffff811615806117bf575062ffffff818116145b156117cb5750506117d3565b91505061167e565b5050509250925092565b6117e5612125565b60035460006117f382612dbd565b90508061ffff166000036118125750600161180e8282613183565b6003555b61181e600a82856131a0565b60405161ffff8416815233907fc975541e72d695746a43ba65745d79963a23082637c8f4609354d9bcf70194d69060200160405180910390a250506118706001600080516020615f9283398151915255565b50565b606086336118818282613289565b6118b65760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044015b60405180910390fd5b6118be612125565b8515806118cb5750858414155b156118e957604051633ab7870760e01b815260040160405180910390fd5b62010009546118fe81338c8c8c8c8c8c6132d5565b89876001600160401b0381111561191757611917615ba2565b604051908082528060200260200182016040528015611940578160200160208202803683370190505b5094506000805b89811015611a945760006119728c8c8481811061196657611966615bdd565b9050602002013561330a565b905060008a8a8481811061198857611988615bdd565b905060200201359050806000036119b957604051636996a92560e01b815262ffffff831660048201526024016118ad565b62ffffff821660008181526006602090815260408083205460019092529091205490916119e890889085613335565b60006119f58385846133b6565b90506000819003611a205760405163fd44792960e01b815262ffffff861660048201526024016118ad565b611a2a83826120eb565b9250838203611a4057611a3e60078661342e565b505b62ffffff851660009081526006602052604090208390558b5181908d9088908110611a6d57611a6d615bdd565b6020908102919091010152611a82878261243b565b96508560010195505050505050611947565b50600454611aa290826120eb565b6004556040516000906001600160a01b0384169033907f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb90611aeb908f908f908f908f90615d17565b60405180910390a48a6001600160a01b0316336001600160a01b03167fa32e146844d6144a22e94c586715a1317d58a8aa3581ec33d040113ddcb243508c8c8a604051611b3a93929190615d3e565b60405180910390a3611b66813660011981013560f01c90038035606090811c9160140135901c8e612475565b611b7d6001600080516020615f9283398151915255565b611b8d8333848e8e8e8e8e6134d0565b50505050509695505050505050565b611ba4612125565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611bed57604051632486085360e01b815260040160405180910390fd5b600354611c01611bfc8261254d565b613505565b600355337f282afaeeae84c1d85ad1424a3aa2ddbedaeefca3b1e53d889d15265fe44db7fc611c2f83613042565b611c3884613031565b6040805162ffffff93841681529290911660208301520160405180910390a250611c6f6001600080516020615f9283398151915255565b565b60055460009081906001600160801b0381169060801c610720565b60006106ea6003546127ed565b611ca433838361353a565b5050565b6000808080611cc06001600160801b03871686613622565b6003549091506000611ce760003660011981013560f090811c90910360280135901c905090565b90506000611cf4836127ed565b9050611d0083426127f9565b92505b62ffffff8116600090815260066020526040902054611d23818a1561286f565b611d9057611d31848361289e565b935060008080611d458488888f898d612926565b919450925090508215611d8c57611d5c88846120eb565b9750611d69828d15613066565b611d73908b615ca5565b9950611d7f818d613066565b611d89908a615ca5565b98505b5050505b6000859003611d9f5750611dd7565b6000611dab8a84612be3565b905062ffffff81161580611dc3575062ffffff818116145b15611dcf575050611dd7565b915050611d03565b611de18489613066565b9650505050509250925092565b60006116318383613289565b611e02612125565b6000839003611e245760405163727ab02160e11b815260040160405180910390fd5b62010009546004546000611e3786613633565b9050611e458333898961373b565b611e69863660011981013560f01c90038035606090811c9160140135901c8a612475565b6000806001600160a01b038916631faa6b8760e01b333660011981013560f01c90033560601c3660011981013560f01c90036014013560601c8c888d8d604051602401611ebc9796959493929190615d8d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611efa9190615dce565b6000604051808303816000865af19150503d8060008114611f37576040519150601f19603f3d011682016040523d82523d6000602084013e611f3c565b606091505b5091509150811580611f5057508051602014155b80611f8e57507fab5c473bce5960a8292e9c8db82f9272504caab4e9ef09553fc112f82b62a3c281806020019051810190611f8b9190615dea565b14155b15611fac57604051634a57011360e01b815260040160405180910390fd5b6000611fd1813660011981013560f01c90038035606090811c9160140135901c61228d565b9050611fe7611fe0868661243b565b829061377f565b1561200557604051630e3c717160e11b815260040160405180910390fd5b600061201182876120eb565b6004839055600554909150612026908261243b565b6005556003546001600160a01b038c169033907fd126bd9d94daca8e55ffd8283fac05394aec8326c6b1639e1e8a445fbe8bbc7d90612064906127ed565b6040805162ffffff9092168252602082018f905281018590526060810185905260800160405180910390a36120a66001600080516020615f9283398151915255565b6120b387338d88856137c3565b5050505050505050505050565b60006106e46120e460003660011981013560f090811c90910360280135901c905090565b839061380e565b8082038281118061210757506001600160801b03808416908216115b156106e45760405163e599af5560e01b815260040160405180910390fd5b600080516020615f9283398151915280546001190161215757604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b600160a81b8816156121c9576121c9886379c8ccf760e01b898989898989896040516024016121929796959493929190615e03565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613853565b5050505050505050565b85336121df8282613289565b61220f5760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044016118ad565b6121c98888888888886138b7565b6001600080516020615f9283398151915255565b600160a91b8816156121c9576121c9886367ddb27860e01b898989898989896040516024016121929796959493929190615e03565b82156109005761090083634e430b5a60e01b85858560405160240161219293929190615e59565b60006122c584610fef61229f85613a63565b6122a887613a63565b6001600160801b03169060801b6001600160801b03919091161790565b949350505050565b600160a41b8616156122fe576122fe86630d4abdb360e01b8787878787604051602401612192959493929190615e73565b505050505050565b60008061232860003660011981013560f090811c90910360280135901c905090565b6003549091506000612339826127ed565b905086935060005b8881101561242e576000806123778a8d8d8681811061236257612362615bdd565b90506020020135613ad290919063ffffffff16565b91509150600080600061238d898886888c613b1b565b9194509250905061239e8a836120eb565b99508362ffffff168b6000015187815181106123bc576123bc615bdd565b602002602001018181525050808b6020015187815181106123df576123df615bdd565b602002602001018181525050828b60400151878151811061240257612402615bdd565b60200260200101818152505061241e8c8562ffffff1685613d1b565b8560010195505050505050612341565b5050505095945050505050565b8181018281108061245757506001600160801b03808416908216105b156106e4576040516316692de960e01b815260040160405180910390fd5b6001600160801b038416608085901c81156124a7576124a76001600160a01b038616846001600160801b038516613d68565b6001600160801b038116156122fe576122fe6001600160a01b038516846001600160801b038416613d68565b600160a51b8616156122fe576122fe86637a51a4c660e01b8787878787604051602401612192959493929190615e73565b61250c613dba565b611c6f613e03565b60006001600160e81b0383167fffffff000000000000000000000000000000000000000000000000000000000060e884901b1617611631565b600080612559836127ed565b905062ffffff60981b19831662ffffff60981b609883901b1617611631565b61ffff871615801561258c575061ffff8616155b801561259a575061ffff8516155b80156125a8575061ffff8416155b80156125b7575062ffffff8316155b80156125c5575061ffff8216155b80156125d4575062ffffff8116155b156125f2576040516331375e0360e11b815260040160405180910390fd5b6126028888888888888888613e0b565b9750600061262560003660011981013560f090811c90910360280135901c905090565b905060006126338a84613f24565b905060006126418284613f6c565b61264b8385613fac565b6126559190615eb2565b905067016345785d8a0000811115612680576040516372db9c0b60e11b815260040160405180910390fd5b50505060038890556040805161ffff8981168252888116602083015287811682840152868116606083015262ffffff868116608084015290851660a0830152831660c0820152905133917fd09e5ddc721ff14c5c1e66a305cbba1fd70b82c5232bc391aad6f55e62e4b046919081900360e00190a25050505050505050565b600061271061ffff60801b608084901b1604600160801b0162ffffff8416627fffff190161272d8282613fd0565b95945050505050565b6000806127438460801c90565b90506122c58161275285613a63565b61275c9190615cc5565b6001600160801b031660801b90565b60006001600160801b0383166122c58161278485613a63565b61278e9190615cc5565b6001600160801b031690565b600160a01b8516156127e6576040516001600160a01b038086166024830152841660448201528215156064820152608481018290526127e690869063feee373560e01b9060a401612192565b5050505050565b600060e882901c6106e4565b60008061280584613053565b6128169064ffffffffff1684615ec5565b905061282184612ea4565b61ffff168110612865576128348461254d565b935061283f84612eb4565b61ffff16811061285957612854846000614230565b612862565b61286284613505565b93505b6122c58484614278565b60008161288f576128808360801c90565b6001600160801b031615611631565b50506001600160801b03161590565b6000806128aa84613042565b62ffffff169050600080828562ffffff16116128cd578462ffffff1683036128d6565b828562ffffff16035b915061271082026128e687613031565b62ffffff1601905060006128f987612ee5565b62ffffff16905080821161290d578161290f565b805b915061291b8783613f24565b979650505050505050565b600080808061293a62ffffff8716896126ff565b905060006129498b8915613066565b90506000886129745761296f61296a6001600160801b038416856080613086565b6142a7565b61298c565b61298c61296a6001600160801b0384166080866130bc565b9050600061299a8c8c6130f2565b905060006129b16001600160801b03841683613111565b90506129bd8184615ca5565b925060006129cb8a8d613066565b9050600080856001600160801b0316836001600160801b0316106129f6575084915082905085612a6c565b612a096001600160801b038416866142d1565b91506000612a178385615cc5565b6001600160801b031690508e612a3b57612a3661296a8260808c614331565b612a4a565b612a4a61296a828b608061435b565b9150876001600160801b0316826001600160801b03161115612a6a578791505b505b8d612aa657612a84836001600160801b031660801b90565b6001600160801b038216612aa1846001600160801b031660801b90565b612ace565b6001600160801b038316612ac3826001600160801b031660801b90565b6001600160801b0384165b809b50819c50829d50505050505050505050507d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611612b20612b1a85610fef888f61243b90919063ffffffff16565b836143c9565b1115612b3f5760405163a2d7652760e01b815260040160405180910390fd5b5096509650969350505050565b60006106e482604e1c613fff1690565b6000816001600160801b0316600003612b77575060006106e4565b6127106001600160801b038316811015612ba457604051631514a94960e21b815260040160405180910390fd5b6001600160801b03848116840282900490608086811c86028490049183169082901b175b9695505050505050565b60006106e48260701c620fffff1690565b600082612bfa57612bf56007836143e5565b611631565b611631600783614531565b600080612c1184612dbd565b90508061ffff16600003612c285783915050611631565b6000612c348683612e58565b90506000612c4182614661565b90506000612c4e8361466d565b612c5b9060ff1683615ed8565b90508064ffffffffff16612c6e4261467c565b64ffffffffff161115612d14576000806000612cb38464ffffffffff164203612c968c6127ed565b612c9f8d612bd2565b612ca98e8e6146a4565b8a939291906146d4565b9250925092506000612cc487612e2b565b905064ffffffffff861642036078811115612cf4575096879006600101964295506000612cf18c8a613183565b9b505b612d0282868686858c614739565b97505050505050612d148885856147b7565b50949695505050505050565b6000612d2c8460801c90565b90506001600160801b03811615612d5a57612d5a6001600160a01b038416836001600160801b038416613d68565b50505050565b60006001600160801b038416612d2c565b600160a11b8516156127e6576040516001600160a01b038086166024830152841660448201528215156064820152608481018290526127e69086906353e8079b60e01b9060a401612192565b60006106e48260d81c61ffff1690565b600080612dda8484612e58565b9150612de582612e2b565b90508061ffff168361ffff1614612e2457612e08612e038583612e58565b612e2b565b90508061ffff168361ffff1611612e1f5780612e21565b825b90505b9250929050565b600061ffff82166106e4565b6000612e428261466d565b60ff16612e4e83614661565b6106e49190615ed8565b6000818061ffff16600003612e805760405163944e113960e01b815260040160405180910390fd5b8361ffff60001985018116908110612e9a57612e9a615bdd565b0154949350505050565b60006106e48260101c610fff1690565b60006106e482601c1c610fff1690565b60006106e48260281c613fff1690565b60006106e48260361c62ffffff1690565b60006106e482605c1c620fffff1690565b600080600080600080612f098989612dcd565b909250905064ffffffffff8716612f408a612f24848c615ef6565b61ffff1661ffff8110612f3957612f39615bdd565b0154612e37565b64ffffffffff161115612f6657604051631c4e7f9560e21b815260040160405180910390fd5b612f6f82612e37565b95508664ffffffffff168664ffffffffff1611612fb15785612f9083614800565b612f9984614815565b612fa28561482a565b95509550955095505050613010565b869550600080612fc38b8b8b8661483f565b91509150600089612fd383612e37565b612fdd9190615c3c565b90506000612fea84612e37565b612ff4908c615c3c565b905061300284848484614991565b919a50985096505050505050505b93509350935093565b600061302584836127f9565b93506122c5848461289e565b60006106e48260841c620fffff1690565b60006106e48260981c62ffffff1690565b60006106e48260b01c64ffffffffff1690565b60008161307757612bf58360801c90565b6001600160801b038316611631565b600061309384848461435b565b9050600160ff83161b806130a9576130a9615c10565b83850915611631576122c5600182615eb2565b60006130c9848484614331565b905081806130d9576130d9615c10565b600160ff85161b850915611631576122c5600182615eb2565b60006116316131018484613f6c565b61310b8585613fac565b016142a7565b60008167016345785d8a0000816001600160801b0316111561314657604051638630bc6b60e01b815260040160405180910390fd5b6001600160801b03808416670de0b6b3a7640000819003918291600019918816028201018161317757613177615c10565b049250505b5092915050565b600061ffff60d81b19831661ffff60d81b60d884901b1617611631565b60006131ac8484612e58565b905060006131b982612e2b565b90508261ffff168161ffff16106131e357604051632c57574160e21b815260040160405180910390fd5b60008461ffff168261ffff16146132145761ffff82161561320d576132088683612e58565b613216565b6000613216565b825b9050600061322382612e2b565b61ffff169050808661ffff161161323a5780613240565b8561ffff165b905061ffff83165b8561ffff168110156132725781888261ffff811061326857613268615bdd565b0155600101613248565b5061079c878761ffff8681168818908916176147b7565b6000816001600160a01b0316836001600160a01b031614806116315750506001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b600160a61b8816156121c9576121c98863ea9db9d360e01b898989898989896040516024016121929796959493929190615e03565b8062ffffff8116811461333057604051639b63641560e01b815260040160405180910390fd5b919050565b6001600160a01b03831660009081526020818152604080832085845291829052909120548281101561339257604051625b387760e31b81526001600160a01b038616600482015260248101859052604481018490526064016118ad565b60009384526001602090815260408086208054869003905592905292209103905550565b60006001600160801b038416608085901c828083156133e9576133e661296a886001600160801b03871689614aef565b91505b6001600160801b038316156134125761340f61296a886001600160801b03861689614aef565b90505b608081901b6001600160801b0383161798975050505050505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b1981168082146134c457600083815260028701602052604081208290558190036134b85750600882901c6000818152600187810160205260408220805460ff87169290921b199091169081905591908290036134b6578654600160ff83161b191687555b505b600193505050506106e4565b50600095945050505050565b600160a71b8816156121c9576121c98863fdc2725760e01b898989898989896040516024016121929796959493929190615e03565b60008061351183612bd2565b62ffffff169050600061352384612ec4565b61ffff1690506127108282020461272d8582614230565b826001600160a01b038116158061355957506001600160a01b03811630145b15613577576040516345c210e760e11b815260040160405180910390fd5b826001600160a01b0316846001600160a01b0316036135b45760405163782ee70760e01b81526001600160a01b03851660048201526024016118ad565b6001600160a01b03848116600081815260026020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b60008161307757612bf58360801b90565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd90c2be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136b89190615dea565b90506001600160801b0383811690608085901c90670de0b6b3a763ffff906136f190670de0b6b3a7640000908616850283015b046142a7565b9250613723670de0b6b3a764000082866001600160801b0316856001600160801b03160201816136eb576136eb615c10565b915050608081901b6001600160801b0383161761272d565b600160a21b841615612d5a576040516001600160a01b0380851660248301528316604482015260648101829052612d5a90859063505d903360e01b90608401612192565b60006001600160801b0383811690608085811c9185169085901c8184108061291b5750806001600160801b0316836001600160801b03161094505050505092915050565b600160a31b8516156127e6576040516001600160a01b0380861660248301528416604482015260648101839052608481018290526127e69086906360bed5f360e01b9060a401612192565b600061271061ffff60801b608084901b1604600160801b018161383082614b0f565b61383986614b0f565b6138439190615f17565b905061272d81628000000161330a565b600080839050602083015160e01c602060008551602087016000865af192503d151583151615613887573d6000803e3d6000fd5b8060005160e01c14601f3d1116831692505081612d5a57604051636c93cb9b60e01b815260040160405180910390fd5b82818082146138d9576040516340311ffd60e11b815260040160405180910390fd5b866001600160a01b03811615806138f857506001600160a01b03811630145b15613916576040516345c210e760e11b815260040160405180910390fd5b6001600160a01b03808a16600090815260208190526040808220928b1682528120905b888110156139fa5760008a8a8381811061395557613955615bdd565b905060200201359050600089898481811061397257613972615bdd565b6000858152602089815260409091205491029290920135925050818110156139cf578e83836040516358b4690f60e01b81526004016118ad939291906001600160a01b039390931683526020830191909152604082015260600190565b6000928352602086815260408085209284900390925585905290912080549091019055600101613939565b50896001600160a01b03168b6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8c8c8c8c604051613a4e9493929190615d17565b60405180910390a45050505050505050505050565b6040516370a0823160e01b81523060048201526000906106e4906001600160a01b038416906370a0823190602401602060405180830381865afa158015613aae573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296a9190615dea565b6000806000806000613ae387614beb565b670de0b6b3a764000060808a811c90930281900490921b6001600160801b03998a169093029190910490971617979596505050505050565b62ffffff83166000818152600660205260408120549091829182918290613b42908b6126ff565b90506000613b618962ffffff1660009081526001602052604090205490565b9050613b6f83898484614c8a565b909650945084935062ffffff808b16908a1603613c9957613b91878a42613019565b96506000613ba384898e89868c614e10565b90508015613c93576000613bc184613bbb89856120eb565b906143c9565b90506000613bcf86866143c9565b9050613bdc828583614aef565b98506000613bf7613bec8c612b4c565b859061ffff16612b5c565b90508015613c1d57613c0988826120eb565b600554909850613c19908261243b565b6005555b613c29600a8c8f612c05565b9a508a600381905550336001600160a01b03167f3f0b46725027bb418b2005f4683538eccdbcdf1de2b8649a29dbd9c507d16ff48e8684604051613c879392919062ffffff9390931683526020830191909152604082015260600190565b60405180910390a25050505b50613ca4565b613ca4858b8b614f05565b851580613caf575083155b15613cd457604051634c98d35760e11b815262ffffff8a1660048201526024016118ad565b80600003613ce957613ce760078a614f6b565b505b613cf3838561243b565b62ffffff90991660009081526006602052604090209890985550929891975095509350505050565b60008281526001602052604081208054839290613d39908490615eb2565b90915550506001600160a01b039092166000908152602081815260408083209383529290522080549091019055565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610900908490614ff8565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16611c6f57604051631afcd79f60e31b815260040160405180910390fd5b61221d613dba565b60008561ffff168761ffff161180613e285750610fff8661ffff16115b80613e3857506127108561ffff16115b80613e4857506109c48361ffff16115b80613e5a5750620fffff8262ffffff16115b15613e7857604051631c07203f60e01b815260040160405180910390fd5b605c82901b6dfffff00000000000000000000000166dffffffffffffffffffffffffffff1991821661ffff8a1690811760108a901b630fff000016908117601c8a901b64fff00000001690811760288a901b663fff00000000001690811760368a901b693fffffc000000000000016908117604e8a901b6b0fffc000000000000000000016908117871797949095179092171717909117909117908a16175b9998505050505050505050565b6000620fffff8262ffffff161115613f4f57604051631c07203f60e01b815260040160405180910390fd5b620fffff60701b198316620fffff60701b607084901b1617611631565b600080613f7884612ed4565b62ffffff169050801561317c5760008361ffff16613f9586612bd2565b62ffffff1602905060646063828002840201613177565b60008161ffff16613fbc84612e2b565b61ffff16026402540be40002905092915050565b600080600083600003613fec5750600160801b91506106e49050565b50826000811215613ffe579015906000035b621000008110156141ee57600160801b9250846001600160801b0381111561402857911591600019045b60018216156140395792830260801c925b800260801c600282161561404f5792830260801c925b800260801c60048216156140655792830260801c925b800260801c600882161561407b5792830260801c925b800260801c60108216156140915792830260801c925b800260801c60208216156140a75792830260801c925b800260801c60408216156140bd5792830260801c925b8002608090811c908216156140d45792830260801c925b800260801c6101008216156140eb5792830260801c925b800260801c6102008216156141025792830260801c925b800260801c6104008216156141195792830260801c925b800260801c6108008216156141305792830260801c925b800260801c6110008216156141475792830260801c925b800260801c61200082161561415e5792830260801c925b800260801c6140008216156141755792830260801c925b800260801c61800082161561418c5792830260801c925b800260801c620100008216156141a45792830260801c925b800260801c620200008216156141bc5792830260801c925b800260801c620400008216156141d45792830260801c925b800260801c620800008216156141ec5792830260801c925b505b8260000361421957604051631dba598d60e11b815260048101869052602481018590526044016118ad565b81614224578261272d565b61272d83600019615f45565b6000620fffff8262ffffff16111561425b57604051631c07203f60e01b815260040160405180910390fd5b620fffff60841b198316620fffff60841b608484901b1617611631565b6000806142848361467c565b905064ffffffffff60b01b19841664ffffffffff60b01b60b083901b16176122c5565b806001600160801b038116811461333057604051632f45db3d60e21b815260040160405180910390fd5b60008167016345785d8a0000816001600160801b0316111561430657604051638630bc6b60e01b815260040160405180910390fd5b670de0b6b3a7640000670de0b6b3a763ffff6001600160801b03868116908616020104949350505050565b600060ff831684811b9061ffff6101008290031686901c90612bc89087906001901b86858561505b565b600080600061436a8686615107565b9150915081600014614380578360ff1682901c92505b80156143c057600160ff85161b81106143ac57604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b60006001600160801b038316608084901c61272d828286615126565b60008061ffff600884901c1660ff80851690811461443a576000828152600287016020526040812054935061441a8483615191565b905060001981146144385760089290921b90911792506106e4915050565b505b5060ff80821690600883901c9082146144b4576000818152600188016020526040812054945061446a8584615191565b905060001981146144b257600882901b8117600081815260028a0160205260409020549550935061449a856151a0565b60ff1660088560001c901b17955050505050506106e4565b505b60ff81811692508214614524578654935060006144d18584615191565b9050600019811461452257600081815260018901602052604090205494509050806144fb856151a0565b60ff16600883901b17600081815260028a0160205260409020549550935061449a856151a0565b505b5060009695505050505050565b60008061ffff600884901c1660ff841680156145845760008281526002870160205260408120549350614564848361523c565b905060001981146145825760089290921b90911792506106e4915050565b505b5060ff8116600882901c81156145e357600081815260018801602052604081205494506145b1858461523c565b905060001981146145e157600882901b8117600081815260028a0160205260409020549550935061449a8561524c565b505b60ff811691508115614652578654935060006145ff858461523c565b9050600019811461465057600081815260018901602052604090205494509050806146298561524c565b60ff16600883901b17600081815260028a0160205260409020549550935061449a8561524c565b505b5062ffffff9695505050505050565b600060d882901c6106e4565b60006106e48260d01c60ff1690565b8064ffffffffff81168114613330576040516350dbfc8760e11b815260040160405180910390fd5b6000806146b0846127ed565b90508062ffffff168362ffffff16116146cb578281036122c5565b90910392915050565b64ffffffffff841662ffffff8481168202918482168102918416026146f888614800565b6147029084615c85565b925061470d88614815565b6147179083615c85565b91506147228861482a565b61472c9082615c85565b9050955095509592505050565b60d082901b60ff60d01b16609084901b67ffffffffffffffff60901b16605086901b71ffffffffffffffff0000000000000000000016601088901b69ffffffffffffffff00001661ffff8a161717171760d882901b7fffffffffff00000000000000000000000000000000000000000000000000000016811761291b565b818061ffff166000036147dd5760405163944e113960e01b815260040160405180910390fd5b818461ffff600019860181169081106147f8576147f8615bdd565b015550505050565b60006106e48260101c6001600160401b031690565b60006106e48260501c6001600160401b031690565b60006106e48260901c6001600160401b031690565b600080808061484f600186615f59565b61ffff16905060008060008961ffff1690505b83851161490d57600060016148778688615eb2565b901c9050888183089a508b61ffff808d1690811061489757614897615bdd565b015493506148a484612e37565b92508964ffffffffff168364ffffffffff1611156148ce576148c7600182615ec5565b9450614907565b8964ffffffffff168364ffffffffff1610156148f6576148ef816001615eb2565b9550614907565b838497509750505050505050614988565b50614862565b8164ffffffffff168964ffffffffff16101561495e578961ffff16600003614933578799505b8a61ffff6000198c01811690811061494d5761494d615bdd565b015483965096505050505050614988565b8760018b089950828b61ffff808d1690811061497c5761497c615bdd565b01549650965050505050505b94509492505050565b6000806000806149a088614800565b6001600160401b0316905060006149b689614815565b6001600160401b0316905060006149cc8a61482a565b6001600160401b031690508664ffffffffff166000036149f25791945092509050614ae5565b60006149fd8a614800565b6001600160401b031690506000614a138b614815565b6001600160401b031690506000614a298c61482a565b6001600160401b031690508a64ffffffffff16600003614a535791975095509350614ae592505050565b6000614a6964ffffffffff808d16908e16615eb2565b9050808b64ffffffffff1685028d64ffffffffff1689020181614a8e57614a8e615c10565b049950808b64ffffffffff1684028d64ffffffffff1688020181614ab457614ab4615c10565b049850808b64ffffffffff1683028d64ffffffffff1687020181614ada57614ada615c10565b049750505050505050505b9450945094915050565b6000806000614afe8686615107565b91509150612bc8868686858561505b565b600081600103614b225750607f19919050565b81600003614b435760405163125946cf60e11b815260040160405180910390fd5b60019190911c9060006001607f1b8310614b5f57506001614b7a565b5060001982600160fe1b81614b7657614b76615c10565b0492505b6000614b89607f85901c61524c565b60ff607f1b607f82901b16935060ff16905083811c6001607f1b8114614be0576001607e1b5b6000811315614bde57908002607f1c90600160801b8210614bd6579384019360019190911c905b60011d614baf565b505b50500260011b919050565b60008080614c028460581c6001600160401b031690565b9250614c178460181c6001600160401b031690565b91505062ffffff831672ffffffffffffffffffffffffffffffffffffff841180614c515750670de0b6b3a7640000836001600160401b0316115b80614c6c5750670de0b6b3a7640000826001600160401b0316115b1561142657604051637752557f60e11b815260040160405180910390fd5b6000806001600160801b038516608086901c82614ca8838389615126565b905080600003614cc2575060009350839250614988915050565b6000614cce8a896143c9565b9050801580614cdb575086155b15614cf757614ce9826152e6565b899550955050505050614988565b614d02828883614aef565b95506000614d1187838a615358565b905080831115614db0576000614d278285615ec5565b9050600160801b8110614d6857608081901c858111614d465780614d48565b855b9050614d548187615ec5565b9550614d64608082901b83615ec5565b9150505b898110614d9c576000614d7b8b83615f45565b9050868111614d8a5780614d8c565b865b9050614d988188615ec5565b9650505b608085901b6001600160801b038716179a50505b7d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611614de2614ddc8d8d61243b565b8b6143c9565b1115614e015760405163a2d7652760e01b815260040160405180910390fd5b50949997985050505050505050565b600081600003614e2257506000612bc8565b6001600160801b038416608085901c600080614e5461070c614e448d8b61243b565b88614e4f818c615eb2565b6133b6565b91509150836001600160801b0316826001600160801b03161115614eb7576000614e9a614e818c8c6130f2565b614e8b8487615cc5565b6001600160801b031690615375565b9050614eaf816001600160801b031660801b90565b955050614ef7565b826001600160801b0316816001600160801b03161115614ef7576000614eea614ee08c8c6130f2565b614e8b8588615cc5565b6001600160801b03169550505b505050509695505050505050565b8162ffffff168162ffffff16108015614f215750608083901b15155b80614f4657508162ffffff168162ffffff16118015614f4657506001600160801b0383115b156109005760405163227bd87160e21b815262ffffff821660048201526024016118ad565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b81178082146134c457600083815260028701602052604081208290558290036134b857600883901c6000818152600180890160205260408220805460ff88169290921b8217905593508390036134b6578654600160ff83161b17875550600193505050506106e4565b600061500d6001600160a01b038416836153df565b905080516000141580156150325750808060200190518101906150309190615f74565b155b1561090057604051635274afe760e01b81526001600160a01b03841660048201526024016118ad565b60008160000361507c5783838161507457615074615c10565b04905061272d565b83821061509c576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b6000831561516157508083028184828161514257615142615c10565b0414615161576040516363f1e01f60e01b815260040160405180910390fd5b82156116315760809290921b91820182811015611631576040516363f1e01f60e01b815260040160405180910390fd5b600061163183600184016153ed565b60008160801b80156151b457608091508092505b8260401b905080156151c7579150604001815b8260201b905080156151da579150602001815b8260101b905080156151ed579150601001815b8260081b90508015615200579150600801815b8260041b90508015615213579150600401815b8260021b90508015615226579150600201815b508160011b15615234576001015b60ff03919050565b600061163183600019840161541b565b60006001600160801b038211156152655750608090811c905b6001600160401b0382111561527c57604091821c91015b63ffffffff82111561529057602091821c91015b61ffff8211156152a257601091821c91015b60ff8211156152b357600891821c91015b600f8211156152c457600491821c91015b60038211156152d557600291821c91015b600182111561333057600101919050565b6000816000036152f857506000919050565b60006153038361524c565b600181811c607f1681811b9086901c01811c80860401811c80860401811c80860401811c80860401811c80860401811c80860401901c9384900493925060ff1690508282106153525782611631565b50919050565b6000615365848484614aef565b905081806130a9576130a9615c10565b60008167016345785d8a0000816001600160801b031611156153aa57604051638630bc6b60e01b815260040160405180910390fd5b6ec097ce7bc90715b34b9f1000000000806001600160801b03868116908616908102670de0b6b3a76400009190910102613177565b60606116318383600061544c565b60ff81169190911c90600082156154115781615408846151a0565b0160ff16611631565b5060001992915050565b60ff8181031691821b91600090831561544157806154388561524c565b60ff16036122c5565b506000199392505050565b6060814710156154715760405163cd78605960e01b81523060048201526024016118ad565b600080856001600160a01b0316848660405161548d9190615dce565b60006040518083038185875af1925050503d80600081146154ca576040519150601f19603f3d011682016040523d82523d6000602084013e6154cf565b606091505b5091509150612bc88683836060826154ea57612bf582615531565b815115801561550157506001600160a01b0384163b155b1561552a57604051639996b31560e01b81526001600160a01b03851660048201526024016118ad565b5080611631565b8051156155415780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b038116811461187057600080fd5b6000806040838503121561558257600080fd5b823561558d8161555a565b946020939093013593505050565b60005b838110156155b657818101518382015260200161559e565b50506000910152565b60208152600082518060208401526155de81604085016020870161559b565b601f01601f19169190910160400192915050565b803562ffffff8116811461333057600080fd5b60006020828403121561561757600080fd5b611631826155f2565b60008083601f84011261563257600080fd5b5081356001600160401b0381111561564957600080fd5b6020830191508360208260051b8501011115612e2457600080fd5b6000806000806000806080878903121561567d57600080fd5b86356156888161555a565b955060208701356156988161555a565b945060408701356001600160401b03808211156156b457600080fd5b6156c08a838b01615620565b909650945060608901359150808211156156d957600080fd5b506156e689828a01615620565b979a9699509497509295939492505050565b60008083601f84011261570a57600080fd5b5081356001600160401b0381111561572157600080fd5b602083019150836020828501011115612e2457600080fd5b60008060006040848603121561574e57600080fd5b8335925060208401356001600160401b0381111561576b57600080fd5b615777868287016156f8565b9497909650939450505050565b6000806000806060858703121561579a57600080fd5b84356157a58161555a565b935060208501356001600160401b038111156157c057600080fd5b6157cc87828801615620565b90945092505060408501356157e08161555a565b939692955090935050565b600081518084526020808501945080840160005b8381101561581b578151875295820195908201906001016157ff565b509495945050505050565b83815282602082015260606040820152600061272d60608301846157eb565b803561ffff8116811461333057600080fd5b600080600080600080600080610100898b03121561587457600080fd5b61587d89615845565b975061588b60208a01615845565b965061589960408a01615845565b95506158a760608a01615845565b94506158b560808a016155f2565b93506158c360a08a01615845565b92506158d160c08a016155f2565b91506158df60e08a016155f2565b90509295985092959890939650565b6000806000806040858703121561590457600080fd5b84356001600160401b038082111561591b57600080fd5b61592788838901615620565b9096509450602087013591508082111561594057600080fd5b5061594d87828801615620565b95989497509550505050565b60208152600061163160208301846157eb565b801515811461187057600080fd5b6000806040838503121561598d57600080fd5b82356159988161596c565b915060208301356159a88161555a565b809150509250929050565b600080600080600080600060e0888a0312156159ce57600080fd5b6159d788615845565b96506159e560208901615845565b95506159f360408901615845565b9450615a0160608901615845565b9350615a0f608089016155f2565b9250615a1d60a08901615845565b9150615a2b60c089016155f2565b905092959891949750929550565b600060208284031215615a4b57600080fd5b813564ffffffffff8116811461163157600080fd5b60008060408385031215615a7357600080fd5b8235615a7e8161596c565b9150612e21602084016155f2565b60008060408385031215615a9f57600080fd5b82356001600160801b0381168114615ab657600080fd5b915060208301356159a88161596c565b600060208284031215615ad857600080fd5b5035919050565b600060208284031215615af157600080fd5b61163182615845565b60008060408385031215615b0d57600080fd5b8235615ab68161555a565b60008060408385031215615b2b57600080fd5b82356159988161555a565b60008060008060608587031215615b4c57600080fd5b8435615b578161555a565b93506020850135925060408501356001600160401b03811115615b7957600080fd5b61594d878288016156f8565b600060208284031215615b9757600080fd5b81516116318161555a565b634e487b7160e01b600052604160045260246000fd5b604081526000615bcb60408301856157eb565b828103602084015261272d81856157eb565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615c0557600080fd5b81356116318161555a565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff82811682821603908082111561317c5761317c615c26565b6001600160401b03818116838216028082169190828114615c7d57615c7d615c26565b505092915050565b6001600160401b0381811683821601908082111561317c5761317c615c26565b6001600160801b0381811683821601908082111561317c5761317c615c26565b6001600160801b0382811682821603908082111561317c5761317c615c26565b81835260006001600160fb1b03831115615cfe57600080fd5b8260051b80836020870137939093016020019392505050565b604081526000615d2b604083018688615ce5565b828103602084015261291b818587615ce5565b604081526000615d52604083018587615ce5565b8281036020840152612bc881856157eb565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006001600160a01b03808a168352808916602084015280881660408401525085606083015284608083015260c060a0830152613f1760c083018486615d64565b60008251615de081846020870161559b565b9190910192915050565b600060208284031215615dfc57600080fd5b5051919050565b60006001600160a01b03808a168352808916602084015280881660408401525060a06060830152615e3860a083018688615ce5565b8281036080840152615e4b818587615ce5565b9a9950505050505050505050565b83815260406020820152600061272d604083018486615d64565b60006001600160a01b03808816835280871660208401525060806040830152615ea0608083018587615ce5565b90508260608301529695505050505050565b808201808211156106e4576106e4615c26565b818103818111156106e4576106e4615c26565b64ffffffffff81811683821601908082111561317c5761317c615c26565b600061ffff80841680615f0b57615f0b615c10565b92169190910692915050565b600082615f2657615f26615c10565b600160ff1b821460001984141615615f4057615f40615c26565b500590565b600082615f5457615f54615c10565b500490565b61ffff82811682821603908082111561317c5761317c615c26565b600060208284031215615f8657600080fd5b81516116318161596c56fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220c578733990c0ce2394ac911f6b78a121fc864cad37c6c98520a68744eb5270ef64736f6c6343000814003300000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee43
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102265760003560e01c806388cc58e411610130578063c9939f5e116100b8578063e584b6541161007c578063e584b65414610652578063e77366f814610665578063e985e9c514610678578063ea3446bf1461069b578063f5e29329146106ae57600080fd5b8063c9939f5e1461060f578063d3b9fbe414610622578063d8dfcea01461062a578063da10610c14610632578063dbe65edc1461064a57600080fd5b8063a1af5b9a116100ff578063a1af5b9a14610570578063a41a01fb14610578578063abcd78301461059f578063bd85b039146105dc578063c7bd6586146105fc57600080fd5b806388cc58e4146104b35780638940a16a146104d95780638d7024e51461051657806395d89b411461055157600080fd5b806347973bff116101b3578063551828941161018257806355182894146103d85780635c60da1b1461041d5780636653851a14610444578063781a8915146104575780637ca0de301461046157600080fd5b806347973bff1461037f5780634c7cffbd146103925780634e1273f4146103a557806353c059a0146103c557600080fd5b80630abe9688116101fa5780630abe9688146102e457806317f11ecc1461031a57806317fad7fc146103355780631b057f6d1461034a578063383d15c51461035d57600080fd5b8062fdd58e1461022b57806305e8746d1461025157806306fdde031461027a5780630902f1ac146102bc575b600080fd5b61023e61023936600461556f565b6106c1565b6040519081526020015b60405180910390f35b3660011981013560f01c90033560601c5b6040516001600160a01b039091168152602001610248565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b60405161024891906155bf565b6102c46106ef565b604080516001600160801b03938416815292909116602083015201610248565b6102c46102f2366004615605565b62ffffff166000908152600660205260409020546001600160801b0381169160809190911c90565b610322610729565b60405161ffff9091168152602001610248565b610348610343366004615664565b61074a565b005b610348610358366004615739565b6107a5565b61037061036b366004615784565b610905565b60405161024893929190615826565b61034861038d366004615857565b610bbb565b61023e6103a0366004615605565b610d4e565b6103b86103b33660046158ee565b610d7e565b6040516102489190615959565b61023e6103d336600461597a565b610e69565b6103e06111a2565b6040805160ff909616865261ffff9485166020870152939092169284019290925264ffffffffff918216606084015216608082015260a001610248565b6102627f000000000000000000000000ed06734629e22277d395d8eb8b67cc75c27cb6a281565b6103486104523660046159b3565b61124c565b620100095461023e565b6104696112c6565b6040805161ffff9889168152968816602088015294871694860194909452918516606085015262ffffff9081166080850152931660a08301529190911660c082015260e001610248565b7f00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee43610262565b6104ec6104e7366004615a39565b61132e565b604080516001600160401b0394851681529284166020840152921691810191909152606001610248565b61051e61142d565b6040805162ffffff95861681529385166020850152919093169082015264ffffffffff9091166060820152608001610248565b60408051808201909152600381526213109560ea1b60208201526102af565b61023e61146d565b61058b610586366004615a60565b611625565b60405162ffffff9091168152602001610248565b6105b26105ad366004615a8c565b611638565b604080516001600160801b0394851681529284166020840152921691810191909152606001610248565b61023e6105ea366004615ac6565b60009081526001602052604090205490565b61034861060a366004615adf565b6117dd565b6103b861061d366004615664565b611873565b610348611b9c565b6102c4611c71565b3660011981013560f01c90036014013560601c610262565b61058b611c8c565b610348610660366004615afa565b611c99565b6105b2610673366004615a8c565b611ca8565b61068b610686366004615b18565b611dee565b6040519015158152602001610248565b6103486106a9366004615b36565b611dfa565b61058b6106bc366004615ac6565b6120c0565b6001600160a01b0382166000908152602081815260408083208484529091529020545b92915050565b905090565b60008061072061070c6005546004546120eb90919063ffffffff16565b6001600160801b0381169160809190911c90565b90939092509050565b60006106ea60003660011981013560f090811c90910360280135901c905090565b610752612125565b6201000954610767813389898989898961215d565b6107758787878787876121d3565b61078c6001600080516020615f9283398151915255565b61079c8133898989898989612231565b50505050505050565b6107ad612125565b336001600160a01b037f00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee4316146107f657604051632486085360e01b815260040160405180910390fd5b62010009839055604080518481529051849133917f4d24d81a1a6e09c37497747b4c9d67d858ce80438b54df0fa361a24dc742d9349181900360200190a26001600160a01b038116158015906108bf5750306001600160a01b0316816001600160a01b0316635f9c01b16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561088f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b39190615b85565b6001600160a01b031614155b156108dd57604051631b5de6eb60e01b815260040160405180910390fd5b6108e8848484612266565b506109006001600080516020615f9283398151915255565b505050565b6000806060866001600160a01b038116158061092957506001600160a01b03811630145b15610947576040516345c210e760e11b815260040160405180910390fd5b61094f612125565b60008690036109715760405163296db0d960e01b815260040160405180910390fd5b6201000954604080516060810190915260009080896001600160401b0381111561099d5761099d615ba2565b6040519080825280602002602001820160405280156109c6578160200160208202803683370190505b508152602001896001600160401b038111156109e4576109e4615ba2565b604051908082528060200260200182016040528015610a0d578160200160208202803683370190505b508152602001896001600160401b03811115610a2b57610a2b615ba2565b604051908082528060200260200182016040528015610a54578160200160208202803683370190505b509052600454909150610a80813660011981013560f01c90038035606090811c9160140135901c61228d565b9650610a9083338d8d8d8c6122cd565b610a9d8a8a898e86612306565b9550610ab3610aac88886120eb565b829061243b565b600455604080830151835191519096506001600160a01b038d169160009133917f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb91610b0191908b90615bb8565b60405180910390a4815160208301516040516001600160a01b038e169233927f87f1f9dcf5e8089a3e00811b6a008d8f30293a3da878cb1fe8c90ca376402f8a92610b4d929190615bb8565b60405180910390a38515610b7f57610b7f863660011981013560f01c90038035606090811c9160140135901c8b612475565b610b966001600080516020615f9283398151915255565b610bad83338d8d8d610ba88d8d6120eb565b6124d3565b505050509450945094915050565b336001600160a01b037f00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee431614610c0457604051632486085360e01b815260040160405180910390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015610c495750825b90506000826001600160401b03166001148015610c655750303b155b905081158015610c73575080155b15610c915760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610cc057845468ff00000000000000001916600160401b1785555b610cc8612504565b610cf4610ce8610ce38860035461251490919063ffffffff16565b61254d565b8e8e8e8e8e8e8e612578565b8315610d3f57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b60006106e4610d7260003660011981013560f090811c90910360280135901c905090565b62ffffff8416906126ff565b60608382808214610da2576040516340311ffd60e11b815260040160405180910390fd5b856001600160401b03811115610dba57610dba615ba2565b604051908082528060200260200182016040528015610de3578160200160208202803683370190505b50925060005b86811015610e5e57610e39888883818110610e0657610e06615bdd565b9050602002016020810190610e1b9190615bf3565b878784818110610e2d57610e2d615bdd565b905060200201356106c1565b848281518110610e4b57610e4b615bdd565b6020908102919091010152600101610de9565b505050949350505050565b6000610e73612125565b6201000954600454600554600086610ea757610ea23660011981013560f01c90036014013560601c8490612736565b610ec1565b610ec13660011981013560f01c90033560601c849061276b565b90506000819003610ee557604051637df801c760e01b815260040160405180910390fd5b86610ef3853389848661279a565b610efd848361243b565b6003549094506000610f2460003660011981013560f090811c90910360280135901c905090565b90506000610f31836127ed565b9050610f3d83426127f9565b92505b62ffffff8116600090815260066020526040902054610f6081861561286f565b61108457610f6e848361289e565b935060008080610f828488888b898e612926565b91945092509050821561108057610f9989846120eb565b9850610fa58d8361243b565b9c506000610fc0610fb589612b4c565b839061ffff16612b5c565b90508015610fe157610fd28b8261243b565b9a50610fde84826120eb565b93505b610ff583610fef878761243b565b906120eb565b62ffffff87166000908152600660205260409020556001600160a01b038f16337fad7d6f97abf51ce18e17a38f4d70e975be9c0708474987bb3e26ad21bd93ca708887876110428e612bd2565b6040805162ffffff9586168152602081019490945283019190915290911660608201526080810186905260a0810185905260c00160405180910390a3505b5050505b600086900361109357506110dd565b600061109f8684612be3565b905062ffffff811615806110b7575062ffffff818116145b156110d557604051631a6d7fb160e31b815260040160405180910390fd5b915050610f40565b60008990036110ff5760405163439df85d60e11b815260040160405180910390fd5b611109878a6120eb565b600455600586905561111d600a8483612c05565b92506111298382612514565b6003558315611155576111503660011981013560f01c90036014013560601c8a908c612d20565b611170565b6111703660011981013560f01c90033560601c8a908c612d60565b6111876001600080516020615f9283398151915255565b61119488338c878d612d71565b505050505050505092915050565b600354607890600090819081908190816111bb82612dbd565b905061ffff8116156112435760006111d4600a83612dcd565b965090506111e181612e2b565b96506111ec81612e37565b94508464ffffffffff1660000361120257600095505b61ffff861615611241576112338661ffff168361ffff168161122657611226615c10565b600a916001910601612e58565b905061123e81612e37565b93505b505b50509091929394565b611254612125565b336001600160a01b037f00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee43161461129d57604051632486085360e01b815260040160405180910390fd5b6112af60035488888888888888612578565b61079c6001600080516020615f9283398151915255565b60008060008060008060008060035490506112e081612e2b565b97506112eb81612ea4565b96506112f681612eb4565b955061130181612ec4565b945061130c81612ed4565b935061131781612b4c565b925061132281612ee5565b91505090919293949596565b6000806000806003549050600061134482612dbd565b905061ffff8116158061135d5750428664ffffffffff16115b156113745760008060009450945094505050611426565b6000611382600a8389612ef6565b91985096509450905064ffffffffff8088169082161015611422576113b86113a9846127ed565b849064ffffffffff8a16613019565b925060006113c68289615c3c565b90508064ffffffffff166113d9856127ed565b62ffffff166113e89190615c5a565b6113f29088615c85565b96508064ffffffffff1661140585612bd2565b62ffffff166114149190615c5a565b61141e9087615c85565b9550505b5050505b9193909250565b6000806000806000600354905061144381612bd2565b945061144e81613031565b935061145981613042565b925061146481613053565b91505090919293565b6000611477612125565b7f00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee436001600160a01b0316634ccb20c06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f99190615b85565b6001600160a01b0316336001600160a01b03161461152a57604051630f8937c560e21b815260040160405180910390fd5b6005546001600160801b038116608082901c60006115858261154d576000611550565b60015b60ff166000856001600160801b03161161156b57600061156e565b60015b60ff169060801b6001600160801b03919091161790565b905061159184826120eb565b945084156116075760058190556004546115ab90866120eb565b60045560405185815233907f3f41a5ddc53701cc7db577ade4f1fca9838a8ec0b5ea50b9f0f5d17bc4554e329060200160405180910390a2611607853660011981013560f01c90038035606090811c9160140135901c33612475565b505050506116226001600080516020615f9283398151915255565b90565b60006116318383612be3565b9392505050565b600354600090839082908161166260003660011981013560f090811c90910360280135901c905090565b9050600061166f836127ed565b905061167b83426127f9565b92505b62ffffff811660009081526006602052604081205461169e908915613066565b90506001600160801b038116156117845760006116c062ffffff8416856126ff565b90506000876001600160801b0316836001600160801b0316116116e357826116e5565b875b90506116f1868561289e565b955060008a6117145761170f6001600160801b038316846080613086565b611729565b6117296001600160801b0383166080856130bc565b9050600061173788886130f2565b9050600061174e6001600160801b03841683613111565b905061175a8184615ca5565b611764908d615ca5565b9b50611770848c615cc5565b9a5061177c818b615ca5565b995050505050505b856001600160801b031660000361179b57506117d3565b60006117a78984612be3565b905062ffffff811615806117bf575062ffffff818116145b156117cb5750506117d3565b91505061167e565b5050509250925092565b6117e5612125565b60035460006117f382612dbd565b90508061ffff166000036118125750600161180e8282613183565b6003555b61181e600a82856131a0565b60405161ffff8416815233907fc975541e72d695746a43ba65745d79963a23082637c8f4609354d9bcf70194d69060200160405180910390a250506118706001600080516020615f9283398151915255565b50565b606086336118818282613289565b6118b65760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044015b60405180910390fd5b6118be612125565b8515806118cb5750858414155b156118e957604051633ab7870760e01b815260040160405180910390fd5b62010009546118fe81338c8c8c8c8c8c6132d5565b89876001600160401b0381111561191757611917615ba2565b604051908082528060200260200182016040528015611940578160200160208202803683370190505b5094506000805b89811015611a945760006119728c8c8481811061196657611966615bdd565b9050602002013561330a565b905060008a8a8481811061198857611988615bdd565b905060200201359050806000036119b957604051636996a92560e01b815262ffffff831660048201526024016118ad565b62ffffff821660008181526006602090815260408083205460019092529091205490916119e890889085613335565b60006119f58385846133b6565b90506000819003611a205760405163fd44792960e01b815262ffffff861660048201526024016118ad565b611a2a83826120eb565b9250838203611a4057611a3e60078661342e565b505b62ffffff851660009081526006602052604090208390558b5181908d9088908110611a6d57611a6d615bdd565b6020908102919091010152611a82878261243b565b96508560010195505050505050611947565b50600454611aa290826120eb565b6004556040516000906001600160a01b0384169033907f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb90611aeb908f908f908f908f90615d17565b60405180910390a48a6001600160a01b0316336001600160a01b03167fa32e146844d6144a22e94c586715a1317d58a8aa3581ec33d040113ddcb243508c8c8a604051611b3a93929190615d3e565b60405180910390a3611b66813660011981013560f01c90038035606090811c9160140135901c8e612475565b611b7d6001600080516020615f9283398151915255565b611b8d8333848e8e8e8e8e6134d0565b50505050509695505050505050565b611ba4612125565b336001600160a01b037f00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee431614611bed57604051632486085360e01b815260040160405180910390fd5b600354611c01611bfc8261254d565b613505565b600355337f282afaeeae84c1d85ad1424a3aa2ddbedaeefca3b1e53d889d15265fe44db7fc611c2f83613042565b611c3884613031565b6040805162ffffff93841681529290911660208301520160405180910390a250611c6f6001600080516020615f9283398151915255565b565b60055460009081906001600160801b0381169060801c610720565b60006106ea6003546127ed565b611ca433838361353a565b5050565b6000808080611cc06001600160801b03871686613622565b6003549091506000611ce760003660011981013560f090811c90910360280135901c905090565b90506000611cf4836127ed565b9050611d0083426127f9565b92505b62ffffff8116600090815260066020526040902054611d23818a1561286f565b611d9057611d31848361289e565b935060008080611d458488888f898d612926565b919450925090508215611d8c57611d5c88846120eb565b9750611d69828d15613066565b611d73908b615ca5565b9950611d7f818d613066565b611d89908a615ca5565b98505b5050505b6000859003611d9f5750611dd7565b6000611dab8a84612be3565b905062ffffff81161580611dc3575062ffffff818116145b15611dcf575050611dd7565b915050611d03565b611de18489613066565b9650505050509250925092565b60006116318383613289565b611e02612125565b6000839003611e245760405163727ab02160e11b815260040160405180910390fd5b62010009546004546000611e3786613633565b9050611e458333898961373b565b611e69863660011981013560f01c90038035606090811c9160140135901c8a612475565b6000806001600160a01b038916631faa6b8760e01b333660011981013560f01c90033560601c3660011981013560f01c90036014013560601c8c888d8d604051602401611ebc9796959493929190615d8d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611efa9190615dce565b6000604051808303816000865af19150503d8060008114611f37576040519150601f19603f3d011682016040523d82523d6000602084013e611f3c565b606091505b5091509150811580611f5057508051602014155b80611f8e57507fab5c473bce5960a8292e9c8db82f9272504caab4e9ef09553fc112f82b62a3c281806020019051810190611f8b9190615dea565b14155b15611fac57604051634a57011360e01b815260040160405180910390fd5b6000611fd1813660011981013560f01c90038035606090811c9160140135901c61228d565b9050611fe7611fe0868661243b565b829061377f565b1561200557604051630e3c717160e11b815260040160405180910390fd5b600061201182876120eb565b6004839055600554909150612026908261243b565b6005556003546001600160a01b038c169033907fd126bd9d94daca8e55ffd8283fac05394aec8326c6b1639e1e8a445fbe8bbc7d90612064906127ed565b6040805162ffffff9092168252602082018f905281018590526060810185905260800160405180910390a36120a66001600080516020615f9283398151915255565b6120b387338d88856137c3565b5050505050505050505050565b60006106e46120e460003660011981013560f090811c90910360280135901c905090565b839061380e565b8082038281118061210757506001600160801b03808416908216115b156106e45760405163e599af5560e01b815260040160405180910390fd5b600080516020615f9283398151915280546001190161215757604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b600160a81b8816156121c9576121c9886379c8ccf760e01b898989898989896040516024016121929796959493929190615e03565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613853565b5050505050505050565b85336121df8282613289565b61220f5760405163548f773d60e01b81526001600160a01b038084166004830152821660248201526044016118ad565b6121c98888888888886138b7565b6001600080516020615f9283398151915255565b600160a91b8816156121c9576121c9886367ddb27860e01b898989898989896040516024016121929796959493929190615e03565b82156109005761090083634e430b5a60e01b85858560405160240161219293929190615e59565b60006122c584610fef61229f85613a63565b6122a887613a63565b6001600160801b03169060801b6001600160801b03919091161790565b949350505050565b600160a41b8616156122fe576122fe86630d4abdb360e01b8787878787604051602401612192959493929190615e73565b505050505050565b60008061232860003660011981013560f090811c90910360280135901c905090565b6003549091506000612339826127ed565b905086935060005b8881101561242e576000806123778a8d8d8681811061236257612362615bdd565b90506020020135613ad290919063ffffffff16565b91509150600080600061238d898886888c613b1b565b9194509250905061239e8a836120eb565b99508362ffffff168b6000015187815181106123bc576123bc615bdd565b602002602001018181525050808b6020015187815181106123df576123df615bdd565b602002602001018181525050828b60400151878151811061240257612402615bdd565b60200260200101818152505061241e8c8562ffffff1685613d1b565b8560010195505050505050612341565b5050505095945050505050565b8181018281108061245757506001600160801b03808416908216105b156106e4576040516316692de960e01b815260040160405180910390fd5b6001600160801b038416608085901c81156124a7576124a76001600160a01b038616846001600160801b038516613d68565b6001600160801b038116156122fe576122fe6001600160a01b038516846001600160801b038416613d68565b600160a51b8616156122fe576122fe86637a51a4c660e01b8787878787604051602401612192959493929190615e73565b61250c613dba565b611c6f613e03565b60006001600160e81b0383167fffffff000000000000000000000000000000000000000000000000000000000060e884901b1617611631565b600080612559836127ed565b905062ffffff60981b19831662ffffff60981b609883901b1617611631565b61ffff871615801561258c575061ffff8616155b801561259a575061ffff8516155b80156125a8575061ffff8416155b80156125b7575062ffffff8316155b80156125c5575061ffff8216155b80156125d4575062ffffff8116155b156125f2576040516331375e0360e11b815260040160405180910390fd5b6126028888888888888888613e0b565b9750600061262560003660011981013560f090811c90910360280135901c905090565b905060006126338a84613f24565b905060006126418284613f6c565b61264b8385613fac565b6126559190615eb2565b905067016345785d8a0000811115612680576040516372db9c0b60e11b815260040160405180910390fd5b50505060038890556040805161ffff8981168252888116602083015287811682840152868116606083015262ffffff868116608084015290851660a0830152831660c0820152905133917fd09e5ddc721ff14c5c1e66a305cbba1fd70b82c5232bc391aad6f55e62e4b046919081900360e00190a25050505050505050565b600061271061ffff60801b608084901b1604600160801b0162ffffff8416627fffff190161272d8282613fd0565b95945050505050565b6000806127438460801c90565b90506122c58161275285613a63565b61275c9190615cc5565b6001600160801b031660801b90565b60006001600160801b0383166122c58161278485613a63565b61278e9190615cc5565b6001600160801b031690565b600160a01b8516156127e6576040516001600160a01b038086166024830152841660448201528215156064820152608481018290526127e690869063feee373560e01b9060a401612192565b5050505050565b600060e882901c6106e4565b60008061280584613053565b6128169064ffffffffff1684615ec5565b905061282184612ea4565b61ffff168110612865576128348461254d565b935061283f84612eb4565b61ffff16811061285957612854846000614230565b612862565b61286284613505565b93505b6122c58484614278565b60008161288f576128808360801c90565b6001600160801b031615611631565b50506001600160801b03161590565b6000806128aa84613042565b62ffffff169050600080828562ffffff16116128cd578462ffffff1683036128d6565b828562ffffff16035b915061271082026128e687613031565b62ffffff1601905060006128f987612ee5565b62ffffff16905080821161290d578161290f565b805b915061291b8783613f24565b979650505050505050565b600080808061293a62ffffff8716896126ff565b905060006129498b8915613066565b90506000886129745761296f61296a6001600160801b038416856080613086565b6142a7565b61298c565b61298c61296a6001600160801b0384166080866130bc565b9050600061299a8c8c6130f2565b905060006129b16001600160801b03841683613111565b90506129bd8184615ca5565b925060006129cb8a8d613066565b9050600080856001600160801b0316836001600160801b0316106129f6575084915082905085612a6c565b612a096001600160801b038416866142d1565b91506000612a178385615cc5565b6001600160801b031690508e612a3b57612a3661296a8260808c614331565b612a4a565b612a4a61296a828b608061435b565b9150876001600160801b0316826001600160801b03161115612a6a578791505b505b8d612aa657612a84836001600160801b031660801b90565b6001600160801b038216612aa1846001600160801b031660801b90565b612ace565b6001600160801b038316612ac3826001600160801b031660801b90565b6001600160801b0384165b809b50819c50829d50505050505050505050507d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611612b20612b1a85610fef888f61243b90919063ffffffff16565b836143c9565b1115612b3f5760405163a2d7652760e01b815260040160405180910390fd5b5096509650969350505050565b60006106e482604e1c613fff1690565b6000816001600160801b0316600003612b77575060006106e4565b6127106001600160801b038316811015612ba457604051631514a94960e21b815260040160405180910390fd5b6001600160801b03848116840282900490608086811c86028490049183169082901b175b9695505050505050565b60006106e48260701c620fffff1690565b600082612bfa57612bf56007836143e5565b611631565b611631600783614531565b600080612c1184612dbd565b90508061ffff16600003612c285783915050611631565b6000612c348683612e58565b90506000612c4182614661565b90506000612c4e8361466d565b612c5b9060ff1683615ed8565b90508064ffffffffff16612c6e4261467c565b64ffffffffff161115612d14576000806000612cb38464ffffffffff164203612c968c6127ed565b612c9f8d612bd2565b612ca98e8e6146a4565b8a939291906146d4565b9250925092506000612cc487612e2b565b905064ffffffffff861642036078811115612cf4575096879006600101964295506000612cf18c8a613183565b9b505b612d0282868686858c614739565b97505050505050612d148885856147b7565b50949695505050505050565b6000612d2c8460801c90565b90506001600160801b03811615612d5a57612d5a6001600160a01b038416836001600160801b038416613d68565b50505050565b60006001600160801b038416612d2c565b600160a11b8516156127e6576040516001600160a01b038086166024830152841660448201528215156064820152608481018290526127e69086906353e8079b60e01b9060a401612192565b60006106e48260d81c61ffff1690565b600080612dda8484612e58565b9150612de582612e2b565b90508061ffff168361ffff1614612e2457612e08612e038583612e58565b612e2b565b90508061ffff168361ffff1611612e1f5780612e21565b825b90505b9250929050565b600061ffff82166106e4565b6000612e428261466d565b60ff16612e4e83614661565b6106e49190615ed8565b6000818061ffff16600003612e805760405163944e113960e01b815260040160405180910390fd5b8361ffff60001985018116908110612e9a57612e9a615bdd565b0154949350505050565b60006106e48260101c610fff1690565b60006106e482601c1c610fff1690565b60006106e48260281c613fff1690565b60006106e48260361c62ffffff1690565b60006106e482605c1c620fffff1690565b600080600080600080612f098989612dcd565b909250905064ffffffffff8716612f408a612f24848c615ef6565b61ffff1661ffff8110612f3957612f39615bdd565b0154612e37565b64ffffffffff161115612f6657604051631c4e7f9560e21b815260040160405180910390fd5b612f6f82612e37565b95508664ffffffffff168664ffffffffff1611612fb15785612f9083614800565b612f9984614815565b612fa28561482a565b95509550955095505050613010565b869550600080612fc38b8b8b8661483f565b91509150600089612fd383612e37565b612fdd9190615c3c565b90506000612fea84612e37565b612ff4908c615c3c565b905061300284848484614991565b919a50985096505050505050505b93509350935093565b600061302584836127f9565b93506122c5848461289e565b60006106e48260841c620fffff1690565b60006106e48260981c62ffffff1690565b60006106e48260b01c64ffffffffff1690565b60008161307757612bf58360801c90565b6001600160801b038316611631565b600061309384848461435b565b9050600160ff83161b806130a9576130a9615c10565b83850915611631576122c5600182615eb2565b60006130c9848484614331565b905081806130d9576130d9615c10565b600160ff85161b850915611631576122c5600182615eb2565b60006116316131018484613f6c565b61310b8585613fac565b016142a7565b60008167016345785d8a0000816001600160801b0316111561314657604051638630bc6b60e01b815260040160405180910390fd5b6001600160801b03808416670de0b6b3a7640000819003918291600019918816028201018161317757613177615c10565b049250505b5092915050565b600061ffff60d81b19831661ffff60d81b60d884901b1617611631565b60006131ac8484612e58565b905060006131b982612e2b565b90508261ffff168161ffff16106131e357604051632c57574160e21b815260040160405180910390fd5b60008461ffff168261ffff16146132145761ffff82161561320d576132088683612e58565b613216565b6000613216565b825b9050600061322382612e2b565b61ffff169050808661ffff161161323a5780613240565b8561ffff165b905061ffff83165b8561ffff168110156132725781888261ffff811061326857613268615bdd565b0155600101613248565b5061079c878761ffff8681168818908916176147b7565b6000816001600160a01b0316836001600160a01b031614806116315750506001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b600160a61b8816156121c9576121c98863ea9db9d360e01b898989898989896040516024016121929796959493929190615e03565b8062ffffff8116811461333057604051639b63641560e01b815260040160405180910390fd5b919050565b6001600160a01b03831660009081526020818152604080832085845291829052909120548281101561339257604051625b387760e31b81526001600160a01b038616600482015260248101859052604481018490526064016118ad565b60009384526001602090815260408086208054869003905592905292209103905550565b60006001600160801b038416608085901c828083156133e9576133e661296a886001600160801b03871689614aef565b91505b6001600160801b038316156134125761340f61296a886001600160801b03861689614aef565b90505b608081901b6001600160801b0383161798975050505050505050565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b1981168082146134c457600083815260028701602052604081208290558190036134b85750600882901c6000818152600187810160205260408220805460ff87169290921b199091169081905591908290036134b6578654600160ff83161b191687555b505b600193505050506106e4565b50600095945050505050565b600160a71b8816156121c9576121c98863fdc2725760e01b898989898989896040516024016121929796959493929190615e03565b60008061351183612bd2565b62ffffff169050600061352384612ec4565b61ffff1690506127108282020461272d8582614230565b826001600160a01b038116158061355957506001600160a01b03811630145b15613577576040516345c210e760e11b815260040160405180910390fd5b826001600160a01b0316846001600160a01b0316036135b45760405163782ee70760e01b81526001600160a01b03851660048201526024016118ad565b6001600160a01b03848116600081815260026020908152604080832094881680845294825291829020805460ff191687151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b60008161307757612bf58360801b90565b6000807f00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee436001600160a01b031663fd90c2be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136b89190615dea565b90506001600160801b0383811690608085901c90670de0b6b3a763ffff906136f190670de0b6b3a7640000908616850283015b046142a7565b9250613723670de0b6b3a764000082866001600160801b0316856001600160801b03160201816136eb576136eb615c10565b915050608081901b6001600160801b0383161761272d565b600160a21b841615612d5a576040516001600160a01b0380851660248301528316604482015260648101829052612d5a90859063505d903360e01b90608401612192565b60006001600160801b0383811690608085811c9185169085901c8184108061291b5750806001600160801b0316836001600160801b03161094505050505092915050565b600160a31b8516156127e6576040516001600160a01b0380861660248301528416604482015260648101839052608481018290526127e69086906360bed5f360e01b9060a401612192565b600061271061ffff60801b608084901b1604600160801b018161383082614b0f565b61383986614b0f565b6138439190615f17565b905061272d81628000000161330a565b600080839050602083015160e01c602060008551602087016000865af192503d151583151615613887573d6000803e3d6000fd5b8060005160e01c14601f3d1116831692505081612d5a57604051636c93cb9b60e01b815260040160405180910390fd5b82818082146138d9576040516340311ffd60e11b815260040160405180910390fd5b866001600160a01b03811615806138f857506001600160a01b03811630145b15613916576040516345c210e760e11b815260040160405180910390fd5b6001600160a01b03808a16600090815260208190526040808220928b1682528120905b888110156139fa5760008a8a8381811061395557613955615bdd565b905060200201359050600089898481811061397257613972615bdd565b6000858152602089815260409091205491029290920135925050818110156139cf578e83836040516358b4690f60e01b81526004016118ad939291906001600160a01b039390931683526020830191909152604082015260600190565b6000928352602086815260408085209284900390925585905290912080549091019055600101613939565b50896001600160a01b03168b6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8c8c8c8c604051613a4e9493929190615d17565b60405180910390a45050505050505050505050565b6040516370a0823160e01b81523060048201526000906106e4906001600160a01b038416906370a0823190602401602060405180830381865afa158015613aae573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296a9190615dea565b6000806000806000613ae387614beb565b670de0b6b3a764000060808a811c90930281900490921b6001600160801b03998a169093029190910490971617979596505050505050565b62ffffff83166000818152600660205260408120549091829182918290613b42908b6126ff565b90506000613b618962ffffff1660009081526001602052604090205490565b9050613b6f83898484614c8a565b909650945084935062ffffff808b16908a1603613c9957613b91878a42613019565b96506000613ba384898e89868c614e10565b90508015613c93576000613bc184613bbb89856120eb565b906143c9565b90506000613bcf86866143c9565b9050613bdc828583614aef565b98506000613bf7613bec8c612b4c565b859061ffff16612b5c565b90508015613c1d57613c0988826120eb565b600554909850613c19908261243b565b6005555b613c29600a8c8f612c05565b9a508a600381905550336001600160a01b03167f3f0b46725027bb418b2005f4683538eccdbcdf1de2b8649a29dbd9c507d16ff48e8684604051613c879392919062ffffff9390931683526020830191909152604082015260600190565b60405180910390a25050505b50613ca4565b613ca4858b8b614f05565b851580613caf575083155b15613cd457604051634c98d35760e11b815262ffffff8a1660048201526024016118ad565b80600003613ce957613ce760078a614f6b565b505b613cf3838561243b565b62ffffff90991660009081526006602052604090209890985550929891975095509350505050565b60008281526001602052604081208054839290613d39908490615eb2565b90915550506001600160a01b039092166000908152602081815260408083209383529290522080549091019055565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610900908490614ff8565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16611c6f57604051631afcd79f60e31b815260040160405180910390fd5b61221d613dba565b60008561ffff168761ffff161180613e285750610fff8661ffff16115b80613e3857506127108561ffff16115b80613e4857506109c48361ffff16115b80613e5a5750620fffff8262ffffff16115b15613e7857604051631c07203f60e01b815260040160405180910390fd5b605c82901b6dfffff00000000000000000000000166dffffffffffffffffffffffffffff1991821661ffff8a1690811760108a901b630fff000016908117601c8a901b64fff00000001690811760288a901b663fff00000000001690811760368a901b693fffffc000000000000016908117604e8a901b6b0fffc000000000000000000016908117871797949095179092171717909117909117908a16175b9998505050505050505050565b6000620fffff8262ffffff161115613f4f57604051631c07203f60e01b815260040160405180910390fd5b620fffff60701b198316620fffff60701b607084901b1617611631565b600080613f7884612ed4565b62ffffff169050801561317c5760008361ffff16613f9586612bd2565b62ffffff1602905060646063828002840201613177565b60008161ffff16613fbc84612e2b565b61ffff16026402540be40002905092915050565b600080600083600003613fec5750600160801b91506106e49050565b50826000811215613ffe579015906000035b621000008110156141ee57600160801b9250846001600160801b0381111561402857911591600019045b60018216156140395792830260801c925b800260801c600282161561404f5792830260801c925b800260801c60048216156140655792830260801c925b800260801c600882161561407b5792830260801c925b800260801c60108216156140915792830260801c925b800260801c60208216156140a75792830260801c925b800260801c60408216156140bd5792830260801c925b8002608090811c908216156140d45792830260801c925b800260801c6101008216156140eb5792830260801c925b800260801c6102008216156141025792830260801c925b800260801c6104008216156141195792830260801c925b800260801c6108008216156141305792830260801c925b800260801c6110008216156141475792830260801c925b800260801c61200082161561415e5792830260801c925b800260801c6140008216156141755792830260801c925b800260801c61800082161561418c5792830260801c925b800260801c620100008216156141a45792830260801c925b800260801c620200008216156141bc5792830260801c925b800260801c620400008216156141d45792830260801c925b800260801c620800008216156141ec5792830260801c925b505b8260000361421957604051631dba598d60e11b815260048101869052602481018590526044016118ad565b81614224578261272d565b61272d83600019615f45565b6000620fffff8262ffffff16111561425b57604051631c07203f60e01b815260040160405180910390fd5b620fffff60841b198316620fffff60841b608484901b1617611631565b6000806142848361467c565b905064ffffffffff60b01b19841664ffffffffff60b01b60b083901b16176122c5565b806001600160801b038116811461333057604051632f45db3d60e21b815260040160405180910390fd5b60008167016345785d8a0000816001600160801b0316111561430657604051638630bc6b60e01b815260040160405180910390fd5b670de0b6b3a7640000670de0b6b3a763ffff6001600160801b03868116908616020104949350505050565b600060ff831684811b9061ffff6101008290031686901c90612bc89087906001901b86858561505b565b600080600061436a8686615107565b9150915081600014614380578360ff1682901c92505b80156143c057600160ff85161b81106143ac57604051638e471a8960e01b815260040160405180910390fd5b8360ff166101000361ffff1681901b830192505b50509392505050565b60006001600160801b038316608084901c61272d828286615126565b60008061ffff600884901c1660ff80851690811461443a576000828152600287016020526040812054935061441a8483615191565b905060001981146144385760089290921b90911792506106e4915050565b505b5060ff80821690600883901c9082146144b4576000818152600188016020526040812054945061446a8584615191565b905060001981146144b257600882901b8117600081815260028a0160205260409020549550935061449a856151a0565b60ff1660088560001c901b17955050505050506106e4565b505b60ff81811692508214614524578654935060006144d18584615191565b9050600019811461452257600081815260018901602052604090205494509050806144fb856151a0565b60ff16600883901b17600081815260028a0160205260409020549550935061449a856151a0565b505b5060009695505050505050565b60008061ffff600884901c1660ff841680156145845760008281526002870160205260408120549350614564848361523c565b905060001981146145825760089290921b90911792506106e4915050565b505b5060ff8116600882901c81156145e357600081815260018801602052604081205494506145b1858461523c565b905060001981146145e157600882901b8117600081815260028a0160205260409020549550935061449a8561524c565b505b60ff811691508115614652578654935060006145ff858461523c565b9050600019811461465057600081815260018901602052604090205494509050806146298561524c565b60ff16600883901b17600081815260028a0160205260409020549550935061449a8561524c565b505b5062ffffff9695505050505050565b600060d882901c6106e4565b60006106e48260d01c60ff1690565b8064ffffffffff81168114613330576040516350dbfc8760e11b815260040160405180910390fd5b6000806146b0846127ed565b90508062ffffff168362ffffff16116146cb578281036122c5565b90910392915050565b64ffffffffff841662ffffff8481168202918482168102918416026146f888614800565b6147029084615c85565b925061470d88614815565b6147179083615c85565b91506147228861482a565b61472c9082615c85565b9050955095509592505050565b60d082901b60ff60d01b16609084901b67ffffffffffffffff60901b16605086901b71ffffffffffffffff0000000000000000000016601088901b69ffffffffffffffff00001661ffff8a161717171760d882901b7fffffffffff00000000000000000000000000000000000000000000000000000016811761291b565b818061ffff166000036147dd5760405163944e113960e01b815260040160405180910390fd5b818461ffff600019860181169081106147f8576147f8615bdd565b015550505050565b60006106e48260101c6001600160401b031690565b60006106e48260501c6001600160401b031690565b60006106e48260901c6001600160401b031690565b600080808061484f600186615f59565b61ffff16905060008060008961ffff1690505b83851161490d57600060016148778688615eb2565b901c9050888183089a508b61ffff808d1690811061489757614897615bdd565b015493506148a484612e37565b92508964ffffffffff168364ffffffffff1611156148ce576148c7600182615ec5565b9450614907565b8964ffffffffff168364ffffffffff1610156148f6576148ef816001615eb2565b9550614907565b838497509750505050505050614988565b50614862565b8164ffffffffff168964ffffffffff16101561495e578961ffff16600003614933578799505b8a61ffff6000198c01811690811061494d5761494d615bdd565b015483965096505050505050614988565b8760018b089950828b61ffff808d1690811061497c5761497c615bdd565b01549650965050505050505b94509492505050565b6000806000806149a088614800565b6001600160401b0316905060006149b689614815565b6001600160401b0316905060006149cc8a61482a565b6001600160401b031690508664ffffffffff166000036149f25791945092509050614ae5565b60006149fd8a614800565b6001600160401b031690506000614a138b614815565b6001600160401b031690506000614a298c61482a565b6001600160401b031690508a64ffffffffff16600003614a535791975095509350614ae592505050565b6000614a6964ffffffffff808d16908e16615eb2565b9050808b64ffffffffff1685028d64ffffffffff1689020181614a8e57614a8e615c10565b049950808b64ffffffffff1684028d64ffffffffff1688020181614ab457614ab4615c10565b049850808b64ffffffffff1683028d64ffffffffff1687020181614ada57614ada615c10565b049750505050505050505b9450945094915050565b6000806000614afe8686615107565b91509150612bc8868686858561505b565b600081600103614b225750607f19919050565b81600003614b435760405163125946cf60e11b815260040160405180910390fd5b60019190911c9060006001607f1b8310614b5f57506001614b7a565b5060001982600160fe1b81614b7657614b76615c10565b0492505b6000614b89607f85901c61524c565b60ff607f1b607f82901b16935060ff16905083811c6001607f1b8114614be0576001607e1b5b6000811315614bde57908002607f1c90600160801b8210614bd6579384019360019190911c905b60011d614baf565b505b50500260011b919050565b60008080614c028460581c6001600160401b031690565b9250614c178460181c6001600160401b031690565b91505062ffffff831672ffffffffffffffffffffffffffffffffffffff841180614c515750670de0b6b3a7640000836001600160401b0316115b80614c6c5750670de0b6b3a7640000826001600160401b0316115b1561142657604051637752557f60e11b815260040160405180910390fd5b6000806001600160801b038516608086901c82614ca8838389615126565b905080600003614cc2575060009350839250614988915050565b6000614cce8a896143c9565b9050801580614cdb575086155b15614cf757614ce9826152e6565b899550955050505050614988565b614d02828883614aef565b95506000614d1187838a615358565b905080831115614db0576000614d278285615ec5565b9050600160801b8110614d6857608081901c858111614d465780614d48565b855b9050614d548187615ec5565b9550614d64608082901b83615ec5565b9150505b898110614d9c576000614d7b8b83615f45565b9050868111614d8a5780614d8c565b865b9050614d988188615ec5565b9650505b608085901b6001600160801b038716179a50505b7d09745258e83de0d0f4e400fce79aca44773dd596d31a6d336ca2a775b611614de2614ddc8d8d61243b565b8b6143c9565b1115614e015760405163a2d7652760e01b815260040160405180910390fd5b50949997985050505050505050565b600081600003614e2257506000612bc8565b6001600160801b038416608085901c600080614e5461070c614e448d8b61243b565b88614e4f818c615eb2565b6133b6565b91509150836001600160801b0316826001600160801b03161115614eb7576000614e9a614e818c8c6130f2565b614e8b8487615cc5565b6001600160801b031690615375565b9050614eaf816001600160801b031660801b90565b955050614ef7565b826001600160801b0316816001600160801b03161115614ef7576000614eea614ee08c8c6130f2565b614e8b8588615cc5565b6001600160801b03169550505b505050509695505050505050565b8162ffffff168162ffffff16108015614f215750608083901b15155b80614f4657508162ffffff168162ffffff16118015614f4657506001600160801b0383115b156109005760405163227bd87160e21b815262ffffff821660048201526024016118ad565b61ffff600882901c166000818152600284016020526040812054909190600160ff85161b81178082146134c457600083815260028701602052604081208290558290036134b857600883901c6000818152600180890160205260408220805460ff88169290921b8217905593508390036134b6578654600160ff83161b17875550600193505050506106e4565b600061500d6001600160a01b038416836153df565b905080516000141580156150325750808060200190518101906150309190615f74565b155b1561090057604051635274afe760e01b81526001600160a01b03841660048201526024016118ad565b60008160000361507c5783838161507457615074615c10565b04905061272d565b83821061509c576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b6000831561516157508083028184828161514257615142615c10565b0414615161576040516363f1e01f60e01b815260040160405180910390fd5b82156116315760809290921b91820182811015611631576040516363f1e01f60e01b815260040160405180910390fd5b600061163183600184016153ed565b60008160801b80156151b457608091508092505b8260401b905080156151c7579150604001815b8260201b905080156151da579150602001815b8260101b905080156151ed579150601001815b8260081b90508015615200579150600801815b8260041b90508015615213579150600401815b8260021b90508015615226579150600201815b508160011b15615234576001015b60ff03919050565b600061163183600019840161541b565b60006001600160801b038211156152655750608090811c905b6001600160401b0382111561527c57604091821c91015b63ffffffff82111561529057602091821c91015b61ffff8211156152a257601091821c91015b60ff8211156152b357600891821c91015b600f8211156152c457600491821c91015b60038211156152d557600291821c91015b600182111561333057600101919050565b6000816000036152f857506000919050565b60006153038361524c565b600181811c607f1681811b9086901c01811c80860401811c80860401811c80860401811c80860401811c80860401811c80860401901c9384900493925060ff1690508282106153525782611631565b50919050565b6000615365848484614aef565b905081806130a9576130a9615c10565b60008167016345785d8a0000816001600160801b031611156153aa57604051638630bc6b60e01b815260040160405180910390fd5b6ec097ce7bc90715b34b9f1000000000806001600160801b03868116908616908102670de0b6b3a76400009190910102613177565b60606116318383600061544c565b60ff81169190911c90600082156154115781615408846151a0565b0160ff16611631565b5060001992915050565b60ff8181031691821b91600090831561544157806154388561524c565b60ff16036122c5565b506000199392505050565b6060814710156154715760405163cd78605960e01b81523060048201526024016118ad565b600080856001600160a01b0316848660405161548d9190615dce565b60006040518083038185875af1925050503d80600081146154ca576040519150601f19603f3d011682016040523d82523d6000602084013e6154cf565b606091505b5091509150612bc88683836060826154ea57612bf582615531565b815115801561550157506001600160a01b0384163b155b1561552a57604051639996b31560e01b81526001600160a01b03851660048201526024016118ad565b5080611631565b8051156155415780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b038116811461187057600080fd5b6000806040838503121561558257600080fd5b823561558d8161555a565b946020939093013593505050565b60005b838110156155b657818101518382015260200161559e565b50506000910152565b60208152600082518060208401526155de81604085016020870161559b565b601f01601f19169190910160400192915050565b803562ffffff8116811461333057600080fd5b60006020828403121561561757600080fd5b611631826155f2565b60008083601f84011261563257600080fd5b5081356001600160401b0381111561564957600080fd5b6020830191508360208260051b8501011115612e2457600080fd5b6000806000806000806080878903121561567d57600080fd5b86356156888161555a565b955060208701356156988161555a565b945060408701356001600160401b03808211156156b457600080fd5b6156c08a838b01615620565b909650945060608901359150808211156156d957600080fd5b506156e689828a01615620565b979a9699509497509295939492505050565b60008083601f84011261570a57600080fd5b5081356001600160401b0381111561572157600080fd5b602083019150836020828501011115612e2457600080fd5b60008060006040848603121561574e57600080fd5b8335925060208401356001600160401b0381111561576b57600080fd5b615777868287016156f8565b9497909650939450505050565b6000806000806060858703121561579a57600080fd5b84356157a58161555a565b935060208501356001600160401b038111156157c057600080fd5b6157cc87828801615620565b90945092505060408501356157e08161555a565b939692955090935050565b600081518084526020808501945080840160005b8381101561581b578151875295820195908201906001016157ff565b509495945050505050565b83815282602082015260606040820152600061272d60608301846157eb565b803561ffff8116811461333057600080fd5b600080600080600080600080610100898b03121561587457600080fd5b61587d89615845565b975061588b60208a01615845565b965061589960408a01615845565b95506158a760608a01615845565b94506158b560808a016155f2565b93506158c360a08a01615845565b92506158d160c08a016155f2565b91506158df60e08a016155f2565b90509295985092959890939650565b6000806000806040858703121561590457600080fd5b84356001600160401b038082111561591b57600080fd5b61592788838901615620565b9096509450602087013591508082111561594057600080fd5b5061594d87828801615620565b95989497509550505050565b60208152600061163160208301846157eb565b801515811461187057600080fd5b6000806040838503121561598d57600080fd5b82356159988161596c565b915060208301356159a88161555a565b809150509250929050565b600080600080600080600060e0888a0312156159ce57600080fd5b6159d788615845565b96506159e560208901615845565b95506159f360408901615845565b9450615a0160608901615845565b9350615a0f608089016155f2565b9250615a1d60a08901615845565b9150615a2b60c089016155f2565b905092959891949750929550565b600060208284031215615a4b57600080fd5b813564ffffffffff8116811461163157600080fd5b60008060408385031215615a7357600080fd5b8235615a7e8161596c565b9150612e21602084016155f2565b60008060408385031215615a9f57600080fd5b82356001600160801b0381168114615ab657600080fd5b915060208301356159a88161596c565b600060208284031215615ad857600080fd5b5035919050565b600060208284031215615af157600080fd5b61163182615845565b60008060408385031215615b0d57600080fd5b8235615ab68161555a565b60008060408385031215615b2b57600080fd5b82356159988161555a565b60008060008060608587031215615b4c57600080fd5b8435615b578161555a565b93506020850135925060408501356001600160401b03811115615b7957600080fd5b61594d878288016156f8565b600060208284031215615b9757600080fd5b81516116318161555a565b634e487b7160e01b600052604160045260246000fd5b604081526000615bcb60408301856157eb565b828103602084015261272d81856157eb565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615c0557600080fd5b81356116318161555a565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b64ffffffffff82811682821603908082111561317c5761317c615c26565b6001600160401b03818116838216028082169190828114615c7d57615c7d615c26565b505092915050565b6001600160401b0381811683821601908082111561317c5761317c615c26565b6001600160801b0381811683821601908082111561317c5761317c615c26565b6001600160801b0382811682821603908082111561317c5761317c615c26565b81835260006001600160fb1b03831115615cfe57600080fd5b8260051b80836020870137939093016020019392505050565b604081526000615d2b604083018688615ce5565b828103602084015261291b818587615ce5565b604081526000615d52604083018587615ce5565b8281036020840152612bc881856157eb565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60006001600160a01b03808a168352808916602084015280881660408401525085606083015284608083015260c060a0830152613f1760c083018486615d64565b60008251615de081846020870161559b565b9190910192915050565b600060208284031215615dfc57600080fd5b5051919050565b60006001600160a01b03808a168352808916602084015280881660408401525060a06060830152615e3860a083018688615ce5565b8281036080840152615e4b818587615ce5565b9a9950505050505050505050565b83815260406020820152600061272d604083018486615d64565b60006001600160a01b03808816835280871660208401525060806040830152615ea0608083018587615ce5565b90508260608301529695505050505050565b808201808211156106e4576106e4615c26565b818103818111156106e4576106e4615c26565b64ffffffffff81811683821601908082111561317c5761317c615c26565b600061ffff80841680615f0b57615f0b615c10565b92169190910692915050565b600082615f2657615f26615c10565b600160ff1b821460001984141615615f4057615f40615c26565b500590565b600082615f5457615f54615c10565b500490565b61ffff82811682821603908082111561317c5761317c615c26565b600060208284031215615f8657600080fd5b81516116318161596c56fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220c578733990c0ce2394ac911f6b78a121fc864cad37c6c98520a68744eb5270ef64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee43
-----Decoded View---------------
Arg [0] : factory_ (address): 0x39D966c1BaFe7D3F1F53dA4845805E15f7D6EE43
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000039d966c1bafe7d3f1f53da4845805e15f7d6ee43
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.