Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 72086654 | 972 days ago | 0 ETH | ||||
| 72086654 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086621 | 972 days ago | 0 ETH | ||||
| 72086599 | 972 days ago | 0 ETH | ||||
| 72086587 | 972 days ago | 0 ETH | ||||
| 72086467 | 972 days ago | 0 ETH | ||||
| 72086467 | 972 days ago | 0 ETH | ||||
| 72086467 | 972 days ago | 0 ETH | ||||
| 72086467 | 972 days ago | 0 ETH | ||||
| 72086467 | 972 days ago | 0 ETH | ||||
| 72086432 | 972 days ago | 0 ETH | ||||
| 72086420 | 972 days ago | 0 ETH | ||||
| 72086412 | 972 days ago | 0 ETH | ||||
| 72086388 | 972 days ago | 0 ETH | ||||
| 72086346 | 972 days ago | 0 ETH | ||||
| 72086283 | 972 days ago | 0 ETH | ||||
| 72086225 | 972 days ago | 0 ETH | ||||
| 72086225 | 972 days ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
LBPair
Compiler Version
v0.8.10+commit.fc410830
Optimization Enabled:
Yes with 800 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/** Imports **/
import "./LBErrors.sol";
import "./LBToken.sol";
import "./libraries/BinHelper.sol";
import "./libraries/Constants.sol";
import "./libraries/Decoder.sol";
import "./libraries/FeeDistributionHelper.sol";
import "./libraries/Math512Bits.sol";
import "./libraries/Oracle.sol";
import "./libraries/ReentrancyGuardUpgradeable.sol";
import "./libraries/SafeCast.sol";
import "./libraries/SafeMath.sol";
import "./libraries/SwapHelper.sol";
import "./libraries/TokenHelper.sol";
import "./libraries/TreeMath.sol";
import "./interfaces/ILBPair.sol";
/// @title Liquidity Book Pair
/// @author Trader Joe
/// @notice This contract is the implementation of Liquidity Book Pair that also acts as the receipt token for liquidity positions
contract LBPair is LBToken, ReentrancyGuardUpgradeable, ILBPair {
/** Libraries **/
using Math512Bits for uint256;
using TreeMath for mapping(uint256 => uint256)[3];
using SafeCast for uint256;
using SafeMath for uint256;
using TokenHelper for IERC20;
using FeeHelper for FeeHelper.FeeParameters;
using SwapHelper for Bin;
using Decoder for bytes32;
using FeeDistributionHelper for FeeHelper.FeesDistribution;
using Oracle for bytes32[65_535];
/** Modifiers **/
/// @notice Checks if the caller is the factory
modifier onlyFactory() {
if (msg.sender != address(factory)) revert LBPair__OnlyFactory();
_;
}
/** Public immutable variables **/
/// @notice The factory contract that created this pair
ILBFactory public immutable override factory;
/** Public variables **/
/// @notice The token that is used as the base currency for the pair
IERC20 public override tokenX;
/// @notice The token that is used as the quote currency for the pair
IERC20 public override tokenY;
/** Private variables **/
/// @dev The pair information that is used to track reserves, active ids,
/// fees and oracle parameters
PairInformation private _pairInformation;
/// @dev The fee parameters that are used to calculate fees
FeeHelper.FeeParameters private _feeParameters;
/// @dev The reserves of tokens for every bin. This is the amount
/// of tokenY if `id < _pairInformation.activeId`; of tokenX if `id > _pairInformation.activeId`
/// and a mix of both if `id == _pairInformation.activeId`
mapping(uint256 => Bin) private _bins;
/// @dev Tree to find bins with non zero liquidity
/// @dev The tree that is used to find the first bin with non zero liquidity
mapping(uint256 => uint256)[3] private _tree;
/// @dev The mapping from account to user's unclaimed fees. The first 128 bits are tokenX and the last are for tokenY
mapping(address => bytes32) private _unclaimedFees;
/// @dev The mapping from account to id to user's accruedDebt
mapping(address => mapping(uint256 => Debts)) private _accruedDebts;
/// @dev The oracle samples that are used to calculate the time weighted average data
bytes32[65_535] private _oracle;
/** OffSets */
uint256 private constant _OFFSET_PAIR_RESERVE_X = 24;
uint256 private constant _OFFSET_PROTOCOL_FEE = 128;
uint256 private constant _OFFSET_BIN_RESERVE_Y = 112;
uint256 private constant _OFFSET_VARIABLE_FEE_PARAMETERS = 144;
uint256 private constant _OFFSET_ORACLE_SAMPLE_LIFETIME = 136;
uint256 private constant _OFFSET_ORACLE_SIZE = 152;
uint256 private constant _OFFSET_ORACLE_ACTIVE_SIZE = 168;
uint256 private constant _OFFSET_ORACLE_LAST_TIMESTAMP = 184;
uint256 private constant _OFFSET_ORACLE_ID = 224;
/** Constructor **/
/// @notice Set the factory address
/// @param _factory The address of the factory
constructor(ILBFactory _factory) LBToken() {
if (address(_factory) == address(0)) revert LBPair__AddressZero();
factory = _factory;
}
/// @notice Initialize the parameters of the LBPair
/// @dev The different parameters needs to be validated very cautiously
/// It is highly recommended to never call this function directly, use the factory
/// as it validates the different parameters
/// @param _tokenX The address of the tokenX. Can't be address 0
/// @param _tokenY The address of the tokenY. Can't be address 0
/// @param _activeId The active id of the pair
/// @param _sampleLifetime The lifetime of a sample. It's the min time between 2 oracle's sample
/// @param _packedFeeParameters The fee parameters packed in a single 256 bits slot
function initialize(
IERC20 _tokenX,
IERC20 _tokenY,
uint24 _activeId,
uint16 _sampleLifetime,
bytes32 _packedFeeParameters
) external override onlyFactory {
if (address(_tokenX) == address(0) || address(_tokenY) == address(0)) revert LBPair__AddressZero();
if (address(tokenX) != address(0)) revert LBPair__AlreadyInitialized();
__ReentrancyGuard_init();
tokenX = _tokenX;
tokenY = _tokenY;
_pairInformation.activeId = _activeId;
_pairInformation.oracleSampleLifetime = _sampleLifetime;
_setFeesParameters(_packedFeeParameters);
_increaseOracle(2);
}
/** External View Functions **/
/// @notice View function to get the reserves and active id
/// @return reserveX The reserve of asset X
/// @return reserveY The reserve of asset Y
/// @return activeId The active id of the pair
function getReservesAndId()
external
view
override
returns (
uint256 reserveX,
uint256 reserveY,
uint256 activeId
)
{
return _getReservesAndId();
}
/// @notice View function to get the total fees and the protocol fees of each tokens
/// @return feesXTotal The total fees of tokenX
/// @return feesYTotal The total fees of tokenY
/// @return feesXProtocol The protocol fees of tokenX
/// @return feesYProtocol The protocol fees of tokenY
function getGlobalFees()
external
view
override
returns (
uint128 feesXTotal,
uint128 feesYTotal,
uint128 feesXProtocol,
uint128 feesYProtocol
)
{
return _getGlobalFees();
}
/// @notice View function to get the oracle parameters
/// @return oracleSampleLifetime The lifetime of a sample, it accumulates information for up to this timestamp
/// @return oracleSize The size of the oracle (last ids can be empty)
/// @return oracleActiveSize The active size of the oracle (no empty data)
/// @return oracleLastTimestamp The timestamp of the creation of the oracle's latest sample
/// @return oracleId The index of the oracle's latest sample
/// @return min The min delta time of two samples
/// @return max The safe max delta time of two samples
function getOracleParameters()
external
view
override
returns (
uint256 oracleSampleLifetime,
uint256 oracleSize,
uint256 oracleActiveSize,
uint256 oracleLastTimestamp,
uint256 oracleId,
uint256 min,
uint256 max
)
{
(oracleSampleLifetime, oracleSize, oracleActiveSize, oracleLastTimestamp, oracleId) = _getOracleParameters();
min = oracleActiveSize == 0 ? 0 : oracleSampleLifetime;
max = oracleSampleLifetime * oracleActiveSize;
}
/// @notice View function to get the oracle's sample at `_timeDelta` seconds
/// @dev Return a linearized sample, the weighted average of 2 neighboring samples
/// @param _timeDelta The number of seconds before the current timestamp
/// @return cumulativeId The weighted average cumulative id
/// @return cumulativeVolatilityAccumulated The weighted average cumulative volatility accumulated
/// @return cumulativeBinCrossed The weighted average cumulative bin crossed
function getOracleSampleFrom(uint256 _timeDelta)
external
view
override
returns (
uint256 cumulativeId,
uint256 cumulativeVolatilityAccumulated,
uint256 cumulativeBinCrossed
)
{
uint256 _lookUpTimestamp = block.timestamp - _timeDelta;
(, , uint256 _oracleActiveSize, , uint256 _oracleId) = _getOracleParameters();
uint256 timestamp;
(timestamp, cumulativeId, cumulativeVolatilityAccumulated, cumulativeBinCrossed) = _oracle.getSampleAt(
_oracleActiveSize,
_oracleId,
_lookUpTimestamp
);
if (timestamp < _lookUpTimestamp) {
FeeHelper.FeeParameters memory _fp = _feeParameters;
uint256 _activeId = _pairInformation.activeId;
_fp.updateVariableFeeParameters(_activeId);
unchecked {
uint256 _deltaT = _lookUpTimestamp - timestamp;
cumulativeId += _activeId * _deltaT;
cumulativeVolatilityAccumulated += uint256(_fp.volatilityAccumulated) * _deltaT;
}
}
}
/// @notice View function to get the fee parameters
/// @return The fee parameters
function feeParameters() external view override returns (FeeHelper.FeeParameters memory) {
return _feeParameters;
}
/// @notice View function to get the first bin that isn't empty, will not be `_id` itself
/// @param _id The bin id
/// @param _swapForY Whether you've swapping token X for token Y (true) or token Y for token X (false)
/// @return The id of the non empty bin
function findFirstNonEmptyBinId(uint24 _id, bool _swapForY) external view override returns (uint24) {
return _tree.findFirstBin(_id, _swapForY);
}
/// @notice View function to get the bin at `id`
/// @param _id The bin id
/// @return reserveX The reserve of tokenX of the bin
/// @return reserveY The reserve of tokenY of the bin
function getBin(uint24 _id) external view override returns (uint256 reserveX, uint256 reserveY) {
return _getBin(_id);
}
/// @notice View function to get the pending fees of a user
/// @dev The array must be strictly increasing to ensure uniqueness
/// @param _account The address of the user
/// @param _ids The list of ids
/// @return amountX The amount of tokenX pending
/// @return amountY The amount of tokenY pending
function pendingFees(address _account, uint256[] calldata _ids)
external
view
override
returns (uint256 amountX, uint256 amountY)
{
if (_account == address(this) || _account == address(0)) return (0, 0);
bytes32 _unclaimedData = _unclaimedFees[_account];
amountX = _unclaimedData.decode(type(uint128).max, 0);
amountY = _unclaimedData.decode(type(uint128).max, 128);
uint256 _lastId;
// Iterate over the ids to get the pending fees of the user for each bin
unchecked {
for (uint256 i; i < _ids.length; ++i) {
uint256 _id = _ids[i];
// Ensures uniqueness of ids
if (_lastId >= _id && i != 0) revert LBPair__OnlyStrictlyIncreasingId();
uint256 _balance = balanceOf(_account, _id);
if (_balance != 0) {
Bin memory _bin = _bins[_id];
(uint128 _amountX, uint128 _amountY) = _getPendingFees(_bin, _account, _id, _balance);
amountX += _amountX;
amountY += _amountY;
}
_lastId = _id;
}
}
}
/// @notice Returns whether this contract implements the interface defined by
/// `interfaceId` (true) or not (false)
/// @param _interfaceId The interface identifier
/// @return Whether the interface is supported (true) or not (false)
function supportsInterface(bytes4 _interfaceId) public view override returns (bool) {
return super.supportsInterface(_interfaceId) || _interfaceId == type(ILBPair).interfaceId;
}
/** External Functions **/
/// @notice Swap tokens iterating over the bins until the entire amount is swapped.
/// Will swap token X 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.
/// That is why this function shouldn't be called directly, but through one of the swap functions of the router
/// that will also perform safety checks.
///
/// The variable fee is updated throughout the swap, it increases with the number of bins crossed.
/// @param _swapForY Whether you've swapping token X for token Y (true) or token Y for token X (false)
/// @param _to The address to send the tokens to
/// @return amountXOut The amount of token X sent to `_to`
/// @return amountYOut The amount of token Y sent to `_to`
function swap(bool _swapForY, address _to)
external
override
nonReentrant
returns (uint256 amountXOut, uint256 amountYOut)
{
PairInformation memory _pair = _pairInformation;
uint256 _amountIn = _swapForY
? tokenX.received(_pair.reserveX, _pair.feesX.total)
: tokenY.received(_pair.reserveY, _pair.feesY.total);
if (_amountIn == 0) revert LBPair__InsufficientAmounts();
FeeHelper.FeeParameters memory _fp = _feeParameters;
uint256 _startId = _pair.activeId;
_fp.updateVariableFeeParameters(_startId);
uint256 _amountOut;
/// Performs the actual swap, iterating over the bins until the entire amount is swapped.
/// It uses the tree to find the next bin to have a non zero reserve of the token we're swapping for.
/// It will also update the variable fee parameters.
while (true) {
Bin memory _bin = _bins[_pair.activeId];
if ((!_swapForY && _bin.reserveX != 0) || (_swapForY && _bin.reserveY != 0)) {
(uint256 _amountInToBin, uint256 _amountOutOfBin, FeeHelper.FeesDistribution memory _fees) = _bin
.getAmounts(_fp, _pair.activeId, _swapForY, _amountIn);
_bin.updateFees(_swapForY ? _pair.feesX : _pair.feesY, _fees, _swapForY, totalSupply(_pair.activeId));
_bin.updateReserves(_pair, _swapForY, _amountInToBin.safe112(), _amountOutOfBin.safe112());
_amountIn -= _amountInToBin + _fees.total;
_amountOut += _amountOutOfBin;
_bins[_pair.activeId] = _bin;
// Avoids stack too deep error
_emitSwap(
_to,
_pair.activeId,
_swapForY,
_amountInToBin,
_amountOutOfBin,
_fp.volatilityAccumulated,
_fees.total
);
}
/// If the amount in is not 0, it means that we haven't swapped the entire amount yet.
/// We need to find the next bin to swap for.
if (_amountIn != 0) {
_pair.activeId = _tree.findFirstBin(_pair.activeId, _swapForY);
} else {
break;
}
}
// Update the oracle and return the updated oracle id. It uses the oracle size to start filling the new slots.
uint256 _updatedOracleId = _oracle.update(
_pair.oracleSize,
_pair.oracleSampleLifetime,
_pair.oracleLastTimestamp,
_pair.oracleId,
_pair.activeId,
_fp.volatilityAccumulated,
_startId.absSub(_pair.activeId)
);
// Update the oracleId and lastTimestamp if the sample write on another slot
if (_updatedOracleId != _pair.oracleId || _pair.oracleLastTimestamp == 0) {
// Can't overflow as the updatedOracleId < oracleSize
_pair.oracleId = uint16(_updatedOracleId);
_pair.oracleLastTimestamp = block.timestamp.safe40();
// Increase the activeSize if the updated sample is written in a new slot
// Can't overflow as _updatedOracleId < maxSize = 2**16-1
unchecked {
if (_updatedOracleId == _pair.oracleActiveSize) ++_pair.oracleActiveSize;
}
}
/// Update the fee parameters and the pair information
_feeParameters = _fp;
_pairInformation = _pair;
if (_swapForY) {
amountYOut = _amountOut;
tokenY.safeTransfer(_to, _amountOut);
} else {
amountXOut = _amountOut;
tokenX.safeTransfer(_to, _amountOut);
}
}
/// @notice Perform a flashloan on one of the tokens of the pair. The flashloan will call the `_receiver` contract
/// to perform the desired operations. The `_receiver` contract is expected to transfer the `amount + fee` of the
/// token to this contract.
/// @param _receiver The contract that will receive the flashloan and execute the callback
/// @param _token The address of the token to flashloan
/// @param _amount The amount of token to flashloan
/// @param _data The call data that will be forwarded to the `_receiver` contract during the callback
function flashLoan(
ILBFlashLoanCallback _receiver,
IERC20 _token,
uint256 _amount,
bytes calldata _data
) external override nonReentrant {
IERC20 _tokenX = tokenX;
if ((_token != _tokenX && _token != tokenY)) revert LBPair__FlashLoanInvalidToken();
uint256 _totalFee = _getFlashLoanFee(_amount);
FeeHelper.FeesDistribution memory _fees = FeeHelper.FeesDistribution({
total: _totalFee.safe128(),
protocol: uint128((_totalFee * _feeParameters.protocolShare) / Constants.BASIS_POINT_MAX)
});
uint256 _balanceBefore = _token.balanceOf(address(this));
_token.safeTransfer(address(_receiver), _amount);
if (
_receiver.LBFlashLoanCallback(msg.sender, _token, _amount, _fees.total, _data) != Constants.CALLBACK_SUCCESS
) revert LBPair__FlashLoanCallbackFailed();
uint256 _balanceAfter = _token.balanceOf(address(this));
if (_balanceAfter != _balanceBefore + _fees.total) revert LBPair__FlashLoanInvalidBalance();
uint256 _activeId = _pairInformation.activeId;
uint256 _totalSupply = totalSupply(_activeId);
if (_totalFee > 0) {
if (_token == _tokenX) {
(uint128 _feesXTotal, , uint128 _feesXProtocol, ) = _getGlobalFees();
_setFees(_pairInformation.feesX, _feesXTotal + _fees.total, _feesXProtocol + _fees.protocol);
_bins[_activeId].accTokenXPerShare += _fees.getTokenPerShare(_totalSupply);
} else {
(, uint128 _feesYTotal, , uint128 _feesYProtocol) = _getGlobalFees();
_setFees(_pairInformation.feesY, _feesYTotal + _fees.total, _feesYProtocol + _fees.protocol);
_bins[_activeId].accTokenYPerShare += _fees.getTokenPerShare(_totalSupply);
}
}
emit FlashLoan(msg.sender, _receiver, _token, _amount, _fees.total);
}
/// @notice Mint new LB tokens for each bins 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.
/// That is why this function shouldn't be called directly, but through one of the add liquidity functions of the
/// router that will also perform safety checks.
/// @dev Any excess amount of token will be sent to the `to` address. The lengths of the arrays must be the same.
/// @param _ids The ids of the bins where the liquidity will be added. It will mint LB tokens for each of these bins.
/// @param _distributionX The percentage of token X to add to each bin. The sum of all the values must not exceed 100%,
/// that is 1e18.
/// @param _distributionY The percentage of token Y to add to each bin. The sum of all the values must not exceed 100%,
/// that is 1e18.
/// @param _to The address that will receive the LB tokens and the excess amount of tokens.
/// @return The amount of token X added to the pair
/// @return The amount of token Y added to the pair
/// @return liquidityMinted The amounts of LB tokens minted for each bin
function mint(
uint256[] calldata _ids,
uint256[] calldata _distributionX,
uint256[] calldata _distributionY,
address _to
)
external
override
nonReentrant
returns (
uint256,
uint256,
uint256[] memory liquidityMinted
)
{
if (_ids.length == 0 || _ids.length != _distributionX.length || _ids.length != _distributionY.length)
revert LBPair__WrongLengths();
PairInformation memory _pair = _pairInformation;
FeeHelper.FeeParameters memory _fp = _feeParameters;
MintInfo memory _mintInfo;
_mintInfo.amountXIn = tokenX.received(_pair.reserveX, _pair.feesX.total).safe112();
_mintInfo.amountYIn = tokenY.received(_pair.reserveY, _pair.feesY.total).safe112();
liquidityMinted = new uint256[](_ids.length);
// Iterate over the ids to calculate the amount of LB tokens to mint for each bin
for (uint256 i; i < _ids.length; ) {
_mintInfo.id = _ids[i].safe24();
Bin memory _bin = _bins[_mintInfo.id];
if (_bin.reserveX == 0 && _bin.reserveY == 0) _tree.addToTree(_mintInfo.id);
_mintInfo.totalDistributionX += _distributionX[i];
_mintInfo.totalDistributionY += _distributionY[i];
// Can't overflow as amounts are uint112 and total distributions will be checked to be smaller or equal than 1e18
unchecked {
_mintInfo.amountX = (_mintInfo.amountXIn * _distributionX[i]) / Constants.PRECISION;
_mintInfo.amountY = (_mintInfo.amountYIn * _distributionY[i]) / Constants.PRECISION;
}
uint256 _price = BinHelper.getPriceFromId(_mintInfo.id, _fp.binStep);
if (_mintInfo.id >= _pair.activeId) {
// The active bin is the only bin that can have a non-zero reserve of the two tokens. When adding liquidity
// with a different ratio than the active bin, the user would actually perform a swap without paying any
// fees. This is why we calculate the fees for the active bin here.
if (_mintInfo.id == _pair.activeId) {
if (_bin.reserveX != 0 || _bin.reserveY != 0) {
uint256 _totalSupply = totalSupply(_mintInfo.id);
uint256 _receivedX;
uint256 _receivedY;
{
uint256 _userL = _price.mulShiftRoundDown(_mintInfo.amountX, Constants.SCALE_OFFSET) +
_mintInfo.amountY;
uint256 _supply = _totalSupply + _userL;
// Calculate the amounts received by the user if he were to burn its liquidity directly after adding
// it. These amounts will be used to calculate the fees.
_receivedX = _userL.mulDivRoundDown(uint256(_bin.reserveX) + _mintInfo.amountX, _supply);
_receivedY = _userL.mulDivRoundDown(uint256(_bin.reserveY) + _mintInfo.amountY, _supply);
}
_fp.updateVariableFeeParameters(_mintInfo.id);
FeeHelper.FeesDistribution memory _fees;
// Checks if the amount of tokens received after burning its liquidity is greater than the amount of
// tokens sent by the user. If it is, we add a composition fee of the difference between the two amounts.
if (_mintInfo.amountX > _receivedX) {
unchecked {
_fees = _fp.getFeeAmountDistribution(
_fp.getFeeAmountForC(_mintInfo.amountX - _receivedX)
);
}
_mintInfo.amountX -= _fees.total;
_mintInfo.activeFeeX += _fees.total;
_bin.updateFees(_pair.feesX, _fees, true, _totalSupply);
}
if (_mintInfo.amountY > _receivedY) {
unchecked {
_fees = _fp.getFeeAmountDistribution(
_fp.getFeeAmountForC(_mintInfo.amountY - _receivedY)
);
}
_mintInfo.amountY -= _fees.total;
_mintInfo.activeFeeY += _fees.total;
_bin.updateFees(_pair.feesY, _fees, false, _totalSupply);
}
if (_mintInfo.activeFeeX > 0 || _mintInfo.activeFeeY > 0)
emit CompositionFee(
msg.sender,
_to,
_mintInfo.id,
_mintInfo.activeFeeX,
_mintInfo.activeFeeY
);
}
} else if (_mintInfo.amountY != 0) revert LBPair__CompositionFactorFlawed(_mintInfo.id);
} else if (_mintInfo.amountX != 0) revert LBPair__CompositionFactorFlawed(_mintInfo.id);
// Calculate the amount of LB tokens to mint for this bin
uint256 _liquidity = _price.mulShiftRoundDown(_mintInfo.amountX, Constants.SCALE_OFFSET) +
_mintInfo.amountY;
if (_liquidity == 0) revert LBPair__InsufficientLiquidityMinted(_mintInfo.id);
liquidityMinted[i] = _liquidity;
// Cast can't overflow as amounts are smaller than amountsIn as totalDistribution will be checked to be smaller than 1e18
_bin.reserveX += uint112(_mintInfo.amountX);
_bin.reserveY += uint112(_mintInfo.amountY);
// The addition or the cast can't overflow as it would have reverted during the previous 2 lines if
// amounts were greater than uint112
unchecked {
_pair.reserveX += uint112(_mintInfo.amountX);
_pair.reserveY += uint112(_mintInfo.amountY);
_mintInfo.amountXAddedToPair += _mintInfo.amountX;
_mintInfo.amountYAddedToPair += _mintInfo.amountY;
}
_bins[_mintInfo.id] = _bin;
_mint(_to, _mintInfo.id, _liquidity);
emit DepositedToBin(msg.sender, _to, _mintInfo.id, _mintInfo.amountX, _mintInfo.amountY);
unchecked {
++i;
}
}
// Assert that the distributions don't exceed 100%
if (_mintInfo.totalDistributionX > Constants.PRECISION || _mintInfo.totalDistributionY > Constants.PRECISION)
revert LBPair__DistributionsOverflow();
_pairInformation = _pair;
// Send back the excess of tokens to `_to`
unchecked {
uint256 _amountXAddedPlusFee = _mintInfo.amountXAddedToPair + _mintInfo.activeFeeX;
if (_mintInfo.amountXIn > _amountXAddedPlusFee) {
tokenX.safeTransfer(_to, _mintInfo.amountXIn - _amountXAddedPlusFee);
}
uint256 _amountYAddedPlusFee = _mintInfo.amountYAddedToPair + _mintInfo.activeFeeY;
if (_mintInfo.amountYIn > _amountYAddedPlusFee) {
tokenY.safeTransfer(_to, _mintInfo.amountYIn - _amountYAddedPlusFee);
}
}
return (_mintInfo.amountXAddedToPair, _mintInfo.amountYAddedToPair, liquidityMinted);
}
/// @notice Burns LB tokens and sends the corresponding amounts of tokens to `_to`. The amount of tokens sent is
/// determined by the ratio of the amount of LB tokens burned to the total supply of LB tokens in the bin.
/// This function will not transfer the LB Tokens from the caller, it is expected that the tokens have already been
/// transferred to this contract through another contract.
/// That is why this function shouldn't be called directly, but through one of the remove liquidity functions of the router
/// that will also perform safety checks.
/// @param _ids The ids of the bins from which to remove liquidity
/// @param _amounts The amounts of LB tokens to burn
/// @param _to The address that will receive the tokens
/// @return amountX The amount of token X sent to `_to`
/// @return amountY The amount of token Y sent to `_to`
function burn(
uint256[] calldata _ids,
uint256[] calldata _amounts,
address _to
) external override nonReentrant returns (uint256 amountX, uint256 amountY) {
if (_ids.length == 0 || _ids.length != _amounts.length) revert LBPair__WrongLengths();
(uint256 _pairReserveX, uint256 _pairReserveY, uint256 _activeId) = _getReservesAndId();
// Iterate over the ids to burn the LB tokens
unchecked {
for (uint256 i; i < _ids.length; ++i) {
uint24 _id = _ids[i].safe24();
uint256 _amountToBurn = _amounts[i];
if (_amountToBurn == 0) revert LBPair__InsufficientLiquidityBurned(_id);
(uint256 _reserveX, uint256 _reserveY) = _getBin(_id);
uint256 _totalSupply = totalSupply(_id);
uint256 _amountX;
uint256 _amountY;
if (_id <= _activeId) {
_amountY = _amountToBurn.mulDivRoundDown(_reserveY, _totalSupply);
amountY += _amountY;
_reserveY -= _amountY;
_pairReserveY -= _amountY;
}
if (_id >= _activeId) {
_amountX = _amountToBurn.mulDivRoundDown(_reserveX, _totalSupply);
amountX += _amountX;
_reserveX -= _amountX;
_pairReserveX -= _amountX;
}
if (_reserveX == 0 && _reserveY == 0) _tree.removeFromTree(_id);
// Optimized `_bins[_id] = _bin` to do only 1 sstore
assembly {
mstore(0, _id)
mstore(32, _bins.slot)
let slot := keccak256(0, 64)
let reserves := add(shl(_OFFSET_BIN_RESERVE_Y, _reserveY), _reserveX)
sstore(slot, reserves)
}
_burn(address(this), _id, _amountToBurn);
emit WithdrawnFromBin(msg.sender, _to, _id, _amountX, _amountY);
}
}
// Optimization to do only 2 sstore
_pairInformation.reserveX = uint136(_pairReserveX);
_pairInformation.reserveY = uint136(_pairReserveY);
tokenX.safeTransfer(_to, amountX);
tokenY.safeTransfer(_to, amountY);
}
/// @notice Increases the length of the oracle to the given `_newLength` by adding empty samples to the end of the oracle.
/// The samples are however initialized to reduce the gas cost of the updates during a swap.
/// @param _newLength The new length of the oracle
function increaseOracleLength(uint16 _newLength) external override {
_increaseOracle(_newLength);
}
/// @notice Collect the fees accumulated by a user.
/// @param _account The address of the user
/// @param _ids The ids of the bins for which to collect the fees
/// @return amountX The amount of token X collected and sent to `_account`
/// @return amountY The amount of token Y collected and sent to `_account`
function collectFees(address _account, uint256[] calldata _ids)
external
override
nonReentrant
returns (uint256 amountX, uint256 amountY)
{
if (_account == address(0) || _account == address(this)) revert LBPair__AddressZeroOrThis();
bytes32 _unclaimedData = _unclaimedFees[_account];
delete _unclaimedFees[_account];
amountX = _unclaimedData.decode(type(uint128).max, 0);
amountY = _unclaimedData.decode(type(uint128).max, 128);
// Iterate over the ids to collect the fees
for (uint256 i; i < _ids.length; ) {
uint256 _id = _ids[i];
uint256 _balance = balanceOf(_account, _id);
if (_balance != 0) {
Bin memory _bin = _bins[_id];
(uint256 _amountX, uint256 _amountY) = _getPendingFees(_bin, _account, _id, _balance);
_updateUserDebts(_bin, _account, _id, _balance);
amountX += _amountX;
amountY += _amountY;
}
unchecked {
++i;
}
}
if (amountX != 0) {
_pairInformation.feesX.total -= uint128(amountX);
}
if (amountY != 0) {
_pairInformation.feesY.total -= uint128(amountY);
}
tokenX.safeTransfer(_account, amountX);
tokenY.safeTransfer(_account, amountY);
emit FeesCollected(msg.sender, _account, amountX, amountY);
}
/// @notice Collect the protocol fees and send them to the fee recipient.
/// @dev The protocol fees are not set to zero to save gas by not resetting the storage slot.
/// @return amountX The amount of token X collected and sent to the fee recipient
/// @return amountY The amount of token Y collected and sent to the fee recipient
function collectProtocolFees() external override nonReentrant returns (uint128 amountX, uint128 amountY) {
address _feeRecipient = factory.feeRecipient();
if (msg.sender != _feeRecipient) revert LBPair__OnlyFeeRecipient(_feeRecipient, msg.sender);
(uint128 _feesXTotal, uint128 _feesYTotal, uint128 _feesXProtocol, uint128 _feesYProtocol) = _getGlobalFees();
// The protocol fees are not set to 0 to reduce the gas cost during a swap
if (_feesXProtocol > 1) {
amountX = _feesXProtocol - 1;
_feesXTotal -= amountX;
_setFees(_pairInformation.feesX, _feesXTotal, 1);
tokenX.safeTransfer(_feeRecipient, amountX);
}
if (_feesYProtocol > 1) {
amountY = _feesYProtocol - 1;
_feesYTotal -= amountY;
_setFees(_pairInformation.feesY, _feesYTotal, 1);
tokenY.safeTransfer(_feeRecipient, amountY);
}
emit ProtocolFeesCollected(msg.sender, _feeRecipient, amountX, amountY);
}
/// @notice Set the fees parameters
/// @dev Needs to be called by the factory that will validate the values
/// The bin step will not change
/// Only callable by the factory
/// @param _packedFeeParameters The packed fee parameters
function setFeesParameters(bytes32 _packedFeeParameters) external override onlyFactory {
_setFeesParameters(_packedFeeParameters);
}
/// @notice Force the decaying of the references for volatility and index
/// @dev Only callable by the factory
function forceDecay() external override onlyFactory {
_feeParameters.volatilityReference = uint24(
(uint256(_feeParameters.reductionFactor) * _feeParameters.volatilityReference) / Constants.BASIS_POINT_MAX
);
_feeParameters.indexRef = _pairInformation.activeId;
}
/** Internal Functions **/
/// @notice Cache the accrued fees for a user before any transfer, mint or burn of LB tokens.
/// The tokens are not transferred to reduce the gas cost and to avoid reentrancy.
/// @param _from The address of the sender of the tokens
/// @param _to The address of the receiver of the tokens
/// @param _id The id of the bin
/// @param _amount The amount of LB tokens transferred
function _beforeTokenTransfer(
address _from,
address _to,
uint256 _id,
uint256 _amount
) internal override(LBToken) {
super._beforeTokenTransfer(_from, _to, _id, _amount);
if (_from != _to) {
Bin memory _bin = _bins[_id];
if (_from != address(0) && _from != address(this)) {
uint256 _balanceFrom = balanceOf(_from, _id);
_cacheFees(_bin, _from, _id, _balanceFrom, _balanceFrom - _amount);
}
if (_to != address(0) && _to != address(this)) {
uint256 _balanceTo = balanceOf(_to, _id);
_cacheFees(_bin, _to, _id, _balanceTo, _balanceTo + _amount);
}
}
}
/** Private Functions **/
/// @notice View function to get the pending fees of an account on a given bin
/// @param _bin The bin data where the user is collecting fees
/// @param _account The address of the user
/// @param _id The id where the user is collecting fees
/// @param _balance The previous balance of the user
/// @return amountX The amount of token X not collected yet by `_account`
/// @return amountY The amount of token Y not collected yet by `_account`
function _getPendingFees(
Bin memory _bin,
address _account,
uint256 _id,
uint256 _balance
) private view returns (uint128 amountX, uint128 amountY) {
Debts memory _debts = _accruedDebts[_account][_id];
amountX = (_bin.accTokenXPerShare.mulShiftRoundDown(_balance, Constants.SCALE_OFFSET) - _debts.debtX).safe128();
amountY = (_bin.accTokenYPerShare.mulShiftRoundDown(_balance, Constants.SCALE_OFFSET) - _debts.debtY).safe128();
}
/// @notice Update the user debts of a user on a given bin
/// @param _bin The bin data where the user has collected fees
/// @param _account The address of the user
/// @param _id The id where the user has collected fees
/// @param _balance The new balance of the user
function _updateUserDebts(
Bin memory _bin,
address _account,
uint256 _id,
uint256 _balance
) private {
uint256 _debtX = _bin.accTokenXPerShare.mulShiftRoundDown(_balance, Constants.SCALE_OFFSET);
uint256 _debtY = _bin.accTokenYPerShare.mulShiftRoundDown(_balance, Constants.SCALE_OFFSET);
_accruedDebts[_account][_id].debtX = _debtX;
_accruedDebts[_account][_id].debtY = _debtY;
}
/// @notice Cache the accrued fees for a user.
/// @param _bin The bin data where the user is receiving LB tokens
/// @param _user The address of the user
/// @param _id The id where the user is receiving LB tokens
/// @param _previousBalance The previous balance of the user
/// @param _newBalance The new balance of the user
function _cacheFees(
Bin memory _bin,
address _user,
uint256 _id,
uint256 _previousBalance,
uint256 _newBalance
) private {
bytes32 _unclaimedData = _unclaimedFees[_user];
uint128 amountX = uint128(_unclaimedData.decode(type(uint128).max, 0));
uint128 amountY = uint128(_unclaimedData.decode(type(uint128).max, 128));
(uint128 _amountX, uint128 _amountY) = _getPendingFees(_bin, _user, _id, _previousBalance);
_updateUserDebts(_bin, _user, _id, _newBalance);
amountX += _amountX;
amountY += _amountY;
_unclaimedFees[_user] = bytes32(uint256((uint256(amountY) << 128) | amountX));
}
/// @notice Set the fee parameters of the pair.
/// @dev Only the first 112 bits can be set, as the last 144 bits are reserved for the variables parameters
/// @param _packedFeeParameters The packed fee parameters
function _setFeesParameters(bytes32 _packedFeeParameters) private {
bytes32 _feeStorageSlot;
assembly {
_feeStorageSlot := sload(_feeParameters.slot)
}
uint256 _varParameters = _feeStorageSlot.decode(type(uint112).max, _OFFSET_VARIABLE_FEE_PARAMETERS);
uint256 _newFeeParameters = _packedFeeParameters.decode(type(uint144).max, 0);
assembly {
sstore(_feeParameters.slot, or(_newFeeParameters, shl(_OFFSET_VARIABLE_FEE_PARAMETERS, _varParameters)))
}
}
/// @notice Increases the length of the oracle to the given `_newSize` by adding empty samples to the end of the oracle.
/// The samples are however initialized to reduce the gas cost of the updates during a swap.
/// @param _newSize The new size of the oracle. Needs to be bigger than current one
function _increaseOracle(uint16 _newSize) private {
uint256 _oracleSize = _pairInformation.oracleSize;
if (_oracleSize >= _newSize) revert LBPair__OracleNewSizeTooSmall(_newSize, _oracleSize);
_pairInformation.oracleSize = _newSize;
// Iterate over the uninitialized oracle samples and initialize them
for (uint256 _id = _oracleSize; _id < _newSize; ) {
_oracle.initialize(_id);
unchecked {
++_id;
}
}
emit OracleSizeIncreased(_oracleSize, _newSize);
}
/// @notice Return the oracle's parameters
/// @return oracleSampleLifetime The lifetime of a sample, it accumulates information for up to this timestamp
/// @return oracleSize The size of the oracle (last ids can be empty)
/// @return oracleActiveSize The active size of the oracle (no empty data)
/// @return oracleLastTimestamp The timestamp of the creation of the oracle's latest sample
/// @return oracleId The index of the oracle's latest sample
function _getOracleParameters()
private
view
returns (
uint256 oracleSampleLifetime,
uint256 oracleSize,
uint256 oracleActiveSize,
uint256 oracleLastTimestamp,
uint256 oracleId
)
{
bytes32 _slot;
assembly {
_slot := sload(add(_pairInformation.slot, 1))
}
oracleSampleLifetime = _slot.decode(type(uint16).max, _OFFSET_ORACLE_SAMPLE_LIFETIME);
oracleSize = _slot.decode(type(uint16).max, _OFFSET_ORACLE_SIZE);
oracleActiveSize = _slot.decode(type(uint16).max, _OFFSET_ORACLE_ACTIVE_SIZE);
oracleLastTimestamp = _slot.decode(type(uint40).max, _OFFSET_ORACLE_LAST_TIMESTAMP);
oracleId = _slot.decode(type(uint16).max, _OFFSET_ORACLE_ID);
}
/// @notice Return the reserves and the active id of the pair
/// @return reserveX The reserve of token X
/// @return reserveY The reserve of token Y
/// @return activeId The active id of the pair
function _getReservesAndId()
private
view
returns (
uint256 reserveX,
uint256 reserveY,
uint256 activeId
)
{
uint256 _mask24 = type(uint24).max;
uint256 _mask136 = type(uint136).max;
assembly {
let slot := sload(add(_pairInformation.slot, 1))
reserveY := and(slot, _mask136)
slot := sload(_pairInformation.slot)
activeId := and(slot, _mask24)
reserveX := and(shr(_OFFSET_PAIR_RESERVE_X, slot), _mask136)
}
}
/// @notice Return the reserves of the bin at index `_id`
/// @param _id The id of the bin
/// @return reserveX The reserve of token X in the bin
/// @return reserveY The reserve of token Y in the bin
function _getBin(uint24 _id) private view returns (uint256 reserveX, uint256 reserveY) {
bytes32 _data;
uint256 _mask112 = type(uint112).max;
// low level read of mapping to only load 1 storage slot
assembly {
mstore(0, _id)
mstore(32, _bins.slot)
_data := sload(keccak256(0, 64))
reserveX := and(_data, _mask112)
reserveY := shr(_OFFSET_BIN_RESERVE_Y, _data)
}
return (reserveX.safe112(), reserveY.safe112());
}
/// @notice Return the total fees and the protocol fees of the pair
/// @dev The fees for users can be computed by subtracting the protocol fees from the total fees
/// @return feesXTotal The total fees of token X
/// @return feesYTotal The total fees of token Y
/// @return feesXProtocol The protocol fees of token X
/// @return feesYProtocol The protocol fees of token Y
function _getGlobalFees()
private
view
returns (
uint128 feesXTotal,
uint128 feesYTotal,
uint128 feesXProtocol,
uint128 feesYProtocol
)
{
bytes32 _slotX;
bytes32 _slotY;
assembly {
_slotX := sload(add(_pairInformation.slot, 2))
_slotY := sload(add(_pairInformation.slot, 3))
}
feesXTotal = uint128(_slotX.decode(type(uint128).max, 0));
feesYTotal = uint128(_slotY.decode(type(uint128).max, 0));
feesXProtocol = uint128(_slotX.decode(type(uint128).max, _OFFSET_PROTOCOL_FEE));
feesYProtocol = uint128(_slotY.decode(type(uint128).max, _OFFSET_PROTOCOL_FEE));
}
/// @notice Return the fee added to a flashloan
/// @dev Rounds up the amount of fees
/// @param _amount The amount of the flashloan
/// @return The fee added to the flashloan
function _getFlashLoanFee(uint256 _amount) private view returns (uint256) {
uint256 _fee = factory.flashLoanFee();
return (_amount * _fee + Constants.PRECISION - 1) / Constants.PRECISION;
}
/// @notice Set the total and protocol fees
/// @dev The assembly block does:
/// _pairFees = FeeHelper.FeesDistribution({total: _totalFees, protocol: _protocolFees});
/// @param _pairFees The storage slot of the fees
/// @param _totalFees The new total fees
/// @param _protocolFees The new protocol fees
function _setFees(
FeeHelper.FeesDistribution storage _pairFees,
uint128 _totalFees,
uint128 _protocolFees
) private {
assembly {
sstore(_pairFees.slot, and(shl(_OFFSET_PROTOCOL_FEE, _protocolFees), _totalFees))
}
}
/// @notice Emit the Swap event and avoid stack too deep error
/// if `swapForY` is:
/// - true: tokenIn is tokenX, and tokenOut is tokenY
/// - false: tokenIn is tokenY, and tokenOut is tokenX
/// @param _to The address of the recipient of the swap
/// @param _swapForY Whether the `amountInToBin` is tokenX (true) or tokenY (false),
/// and if `amountOutOfBin` is tokenY (true) or tokenX (false)
/// @param _amountInToBin The amount of tokenIn sent by the user
/// @param _amountOutOfBin The amount of tokenOut received by the user
/// @param _volatilityAccumulated The volatility accumulated number
/// @param _fees The amount of fees, always denominated in tokenIn
function _emitSwap(
address _to,
uint24 _activeId,
bool _swapForY,
uint256 _amountInToBin,
uint256 _amountOutOfBin,
uint256 _volatilityAccumulated,
uint256 _fees
) private {
emit Swap(
msg.sender,
_to,
_activeId,
_swapForY,
_amountInToBin,
_amountOutOfBin,
_volatilityAccumulated,
_fees
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* 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[EIP 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "./interfaces/ILBPair.sol";
/** LBRouter errors */
error LBRouter__SenderIsNotWAVAX();
error LBRouter__PairNotCreated(address tokenX, address tokenY, uint256 binStep);
error LBRouter__WrongAmounts(uint256 amount, uint256 reserve);
error LBRouter__SwapOverflows(uint256 id);
error LBRouter__BrokenSwapSafetyCheck();
error LBRouter__NotFactoryOwner();
error LBRouter__TooMuchTokensIn(uint256 excess);
error LBRouter__BinReserveOverflows(uint256 id);
error LBRouter__IdOverflows(int256 id);
error LBRouter__LengthsMismatch();
error LBRouter__WrongTokenOrder();
error LBRouter__IdSlippageCaught(uint256 activeIdDesired, uint256 idSlippage, uint256 activeId);
error LBRouter__AmountSlippageCaught(uint256 amountXMin, uint256 amountX, uint256 amountYMin, uint256 amountY);
error LBRouter__IdDesiredOverflows(uint256 idDesired, uint256 idSlippage);
error LBRouter__FailedToSendAVAX(address recipient, uint256 amount);
error LBRouter__DeadlineExceeded(uint256 deadline, uint256 currentTimestamp);
error LBRouter__AmountSlippageBPTooBig(uint256 amountSlippage);
error LBRouter__InsufficientAmountOut(uint256 amountOutMin, uint256 amountOut);
error LBRouter__MaxAmountInExceeded(uint256 amountInMax, uint256 amountIn);
error LBRouter__InvalidTokenPath(address wrongToken);
error LBRouter__InvalidVersion(uint256 version);
error LBRouter__WrongAvaxLiquidityParameters(
address tokenX,
address tokenY,
uint256 amountX,
uint256 amountY,
uint256 msgValue
);
/** LBToken errors */
error LBToken__SpenderNotApproved(address owner, address spender);
error LBToken__TransferFromOrToAddress0();
error LBToken__MintToAddress0();
error LBToken__BurnFromAddress0();
error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount);
error LBToken__LengthMismatch(uint256 accountsLength, uint256 idsLength);
error LBToken__SelfApproval(address owner);
error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount);
error LBToken__TransferToSelf();
/** LBFactory errors */
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__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
error LBFactory__DecreasingPeriods(uint16 filterPeriod, uint16 decayPeriod);
error LBFactory__ReductionFactorOverflows(uint16 reductionFactor, uint256 max);
error LBFactory__VariableFeeControlOverflows(uint16 variableFeeControl, uint256 max);
error LBFactory__BaseFeesBelowMin(uint256 baseFees, uint256 minBaseFees);
error LBFactory__FeesAboveMax(uint256 fees, uint256 maxFees);
error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees);
error LBFactory__BinStepRequirementsBreached(uint256 lowerBound, uint16 binStep, uint256 higherBound);
error LBFactory__ProtocolShareOverflows(uint16 protocolShare, uint256 max);
error LBFactory__FunctionIsLockedForUsers(address user);
error LBFactory__FactoryLockIsAlreadyInTheSameState();
error LBFactory__LBPairIgnoredIsAlreadyInTheSameState();
error LBFactory__BinStepHasNoPreset(uint256 binStep);
error LBFactory__SameFeeRecipient(address feeRecipient);
error LBFactory__SameFlashLoanFee(uint256 flashLoanFee);
error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation);
error LBFactory__SameImplementation(address LBPairImplementation);
error LBFactory__ImplementationNotSet();
/** LBPair errors */
error LBPair__InsufficientAmounts();
error LBPair__AddressZero();
error LBPair__AddressZeroOrThis();
error LBPair__CompositionFactorFlawed(uint256 id);
error LBPair__InsufficientLiquidityMinted(uint256 id);
error LBPair__InsufficientLiquidityBurned(uint256 id);
error LBPair__WrongLengths();
error LBPair__OnlyStrictlyIncreasingId();
error LBPair__OnlyFactory();
error LBPair__DistributionsOverflow();
error LBPair__OnlyFeeRecipient(address feeRecipient, address sender);
error LBPair__OracleNotEnoughSample();
error LBPair__AlreadyInitialized();
error LBPair__OracleNewSizeTooSmall(uint256 newSize, uint256 oracleSize);
error LBPair__FlashLoanCallbackFailed();
error LBPair__FlashLoanInvalidBalance();
error LBPair__FlashLoanInvalidToken();
/** BinHelper errors */
error BinHelper__BinStepOverflows(uint256 bp);
error BinHelper__IdOverflows();
/** Math128x128 errors */
error Math128x128__PowerUnderflow(uint256 x, int256 y);
error Math128x128__LogUnderflow();
/** Math512Bits errors */
error Math512Bits__MulDivOverflow(uint256 prod1, uint256 denominator);
error Math512Bits__ShiftDivOverflow(uint256 prod1, uint256 denominator);
error Math512Bits__MulShiftOverflow(uint256 prod1, uint256 offset);
error Math512Bits__OffsetOverflows(uint256 offset);
/** Oracle errors */
error Oracle__AlreadyInitialized(uint256 _index);
error Oracle__LookUpTimestampTooOld(uint256 _minTimestamp, uint256 _lookUpTimestamp);
error Oracle__NotInitialized();
/** PendingOwnable errors */
error PendingOwnable__NotOwner();
error PendingOwnable__NotPendingOwner();
error PendingOwnable__PendingOwnerAlreadySet();
error PendingOwnable__NoPendingOwner();
error PendingOwnable__AddressZero();
/** ReentrancyGuardUpgradeable errors */
error ReentrancyGuardUpgradeable__ReentrantCall();
error ReentrancyGuardUpgradeable__AlreadyInitialized();
/** SafeCast errors */
error SafeCast__Exceeds256Bits(uint256 x);
error SafeCast__Exceeds248Bits(uint256 x);
error SafeCast__Exceeds240Bits(uint256 x);
error SafeCast__Exceeds232Bits(uint256 x);
error SafeCast__Exceeds224Bits(uint256 x);
error SafeCast__Exceeds216Bits(uint256 x);
error SafeCast__Exceeds208Bits(uint256 x);
error SafeCast__Exceeds200Bits(uint256 x);
error SafeCast__Exceeds192Bits(uint256 x);
error SafeCast__Exceeds184Bits(uint256 x);
error SafeCast__Exceeds176Bits(uint256 x);
error SafeCast__Exceeds168Bits(uint256 x);
error SafeCast__Exceeds160Bits(uint256 x);
error SafeCast__Exceeds152Bits(uint256 x);
error SafeCast__Exceeds144Bits(uint256 x);
error SafeCast__Exceeds136Bits(uint256 x);
error SafeCast__Exceeds128Bits(uint256 x);
error SafeCast__Exceeds120Bits(uint256 x);
error SafeCast__Exceeds112Bits(uint256 x);
error SafeCast__Exceeds104Bits(uint256 x);
error SafeCast__Exceeds96Bits(uint256 x);
error SafeCast__Exceeds88Bits(uint256 x);
error SafeCast__Exceeds80Bits(uint256 x);
error SafeCast__Exceeds72Bits(uint256 x);
error SafeCast__Exceeds64Bits(uint256 x);
error SafeCast__Exceeds56Bits(uint256 x);
error SafeCast__Exceeds48Bits(uint256 x);
error SafeCast__Exceeds40Bits(uint256 x);
error SafeCast__Exceeds32Bits(uint256 x);
error SafeCast__Exceeds24Bits(uint256 x);
error SafeCast__Exceeds16Bits(uint256 x);
error SafeCast__Exceeds8Bits(uint256 x);
/** TreeMath errors */
error TreeMath__ErrorDepthSearch();
/** JoeLibrary errors */
error JoeLibrary__IdenticalAddresses();
error JoeLibrary__AddressZero();
error JoeLibrary__InsufficientAmount();
error JoeLibrary__InsufficientLiquidity();
/** TokenHelper errors */
error TokenHelper__NonContract();
error TokenHelper__CallFailed();
error TokenHelper__TransferFailed();
/** LBQuoter errors */
error LBQuoter_InvalidLength();// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "openzeppelin/utils/structs/EnumerableSet.sol";
import "./LBErrors.sol";
import "./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
contract LBToken is ILBToken {
using EnumerableSet for EnumerableSet.UintSet;
/// @dev Mapping from token id to account balances
mapping(uint256 => mapping(address => uint256)) private _balances;
/// @dev Mapping from account to spender approvals
mapping(address => mapping(address => bool)) private _spenderApprovals;
/// @dev Mapping from token id to total supplies
mapping(uint256 => uint256) private _totalSupplies;
string private constant _NAME = "Liquidity Book Token";
string private constant _SYMBOL = "LBT";
modifier checkApproval(address _from, address _spender) {
if (!_isApprovedForAll(_from, _spender)) revert LBToken__SpenderNotApproved(_from, _spender);
_;
}
modifier checkAddresses(address _from, address _to) {
if (_from == address(0) || _to == address(0)) revert LBToken__TransferFromOrToAddress0();
if (_from == _to) revert LBToken__TransferToSelf();
_;
}
modifier checkLength(uint256 _lengthA, uint256 _lengthB) {
if (_lengthA != _lengthB) revert LBToken__LengthMismatch(_lengthA, _lengthB);
_;
}
/// @notice Returns the name of the token
/// @return The name of the token
function name() public pure virtual override returns (string memory) {
return _NAME;
}
/// @notice Returns the symbol of the token, usually a shorter version of the name
/// @return The symbol of the token
function symbol() public pure virtual override returns (string memory) {
return _SYMBOL;
}
/// @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[_id][_account];
}
/// @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 `_account`'s tokens
/// @param _owner The address of the owner
/// @param _spender The address of the spender
/// @return True if `spender` is approved to transfer `_account`'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 tokens, according to `approved`
/// @param _spender The address of the spender
/// @param _approved The boolean value to grant or revoke permission
function setApprovalForAll(address _spender, bool _approved) public virtual override {
_setApprovalForAll(msg.sender, _spender, _approved);
}
/// @notice Transfers `_amount` token of type `_id` from `_from` to `_to`
/// @param _from The address of the owner of the token
/// @param _to The address of the recipient
/// @param _id The token id
/// @param _amount The amount to send
function safeTransferFrom(
address _from,
address _to,
uint256 _id,
uint256 _amount
) public virtual override checkAddresses(_from, _to) checkApproval(_from, msg.sender) {
address _spender = msg.sender;
_transfer(_from, _to, _id, _amount);
emit TransferSingle(_spender, _from, _to, _id, _amount);
}
/// @notice Batch transfers `_amount` tokens of type `_id` from `_from` to `_to`
/// @param _from The address of the owner of the tokens
/// @param _to The address of the recipient
/// @param _ids The list of token ids
/// @param _amounts The list of amounts to send
function safeBatchTransferFrom(
address _from,
address _to,
uint256[] calldata _ids,
uint256[] calldata _amounts
)
public
virtual
override
checkLength(_ids.length, _amounts.length)
checkAddresses(_from, _to)
checkApproval(_from, msg.sender)
{
unchecked {
for (uint256 i; i < _ids.length; ++i) {
_transfer(_from, _to, _ids[i], _amounts[i]);
}
}
emit TransferBatch(msg.sender, _from, _to, _ids, _amounts);
}
/// @notice Returns whether this contract implements the interface defined by
/// `interfaceId` (true) or not (false)
/// @param _interfaceId The interface identifier
/// @return Whether the interface is supported (true) or not (false)
function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool) {
return _interfaceId == type(ILBToken).interfaceId || _interfaceId == type(IERC165).interfaceId;
}
/// @notice Internal function to transfer `_amount` tokens of type `_id` from `_from` to `_to`
/// @param _from The address of the owner of the token
/// @param _to The address of the recipient
/// @param _id The token id
/// @param _amount The amount to send
function _transfer(
address _from,
address _to,
uint256 _id,
uint256 _amount
) internal virtual {
uint256 _fromBalance = _balances[_id][_from];
if (_fromBalance < _amount) revert LBToken__TransferExceedsBalance(_from, _id, _amount);
_beforeTokenTransfer(_from, _to, _id, _amount);
unchecked {
_balances[_id][_from] = _fromBalance - _amount;
_balances[_id][_to] += _amount;
}
}
/// @dev Creates `_amount` tokens of type `_id`, and assigns them to `_account`
/// @param _account The address of the recipient
/// @param _id The token id
/// @param _amount The amount to mint
function _mint(
address _account,
uint256 _id,
uint256 _amount
) internal virtual {
if (_account == address(0)) revert LBToken__MintToAddress0();
_beforeTokenTransfer(address(0), _account, _id, _amount);
_totalSupplies[_id] += _amount;
unchecked {
_balances[_id][_account] += _amount;
}
emit TransferSingle(msg.sender, address(0), _account, _id, _amount);
}
/// @dev Destroys `_amount` tokens of type `_id` from `_account`
/// @param _account The address of the owner
/// @param _id The token id
/// @param _amount The amount to destroy
function _burn(
address _account,
uint256 _id,
uint256 _amount
) internal virtual {
if (_account == address(0)) revert LBToken__BurnFromAddress0();
uint256 _accountBalance = _balances[_id][_account];
if (_accountBalance < _amount) revert LBToken__BurnExceedsBalance(_account, _id, _amount);
_beforeTokenTransfer(_account, address(0), _id, _amount);
unchecked {
_balances[_id][_account] = _accountBalance - _amount;
_totalSupplies[_id] -= _amount;
}
emit TransferSingle(msg.sender, _account, address(0), _id, _amount);
}
/// @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 _setApprovalForAll(
address _owner,
address _spender,
bool _approved
) internal virtual {
if (_owner == _spender) revert LBToken__SelfApproval(_owner);
_spenderApprovals[_owner][_spender] = _approved;
emit ApprovalForAll(_owner, _spender, _approved);
}
/// @notice Returns true if `spender` is approved to transfer `owner`'s tokens
/// or if `sender` 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 virtual returns (bool) {
return _owner == _spender || _spenderApprovals[_owner][_spender];
}
/// @notice Hook that is called before any token transfer. This includes minting
/// and burning.
///
/// Calling conditions (for each `id` and `amount` pair):
///
/// - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
/// of token type `id` will be transferred to `to`.
/// - When `from` is zero, `amount` tokens of token type `id` will be minted
/// for `to`.
/// - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
/// will be burned.
/// - `from` and `to` are never both zero.
/// @param from The address of the owner of the token
/// @param to The address of the recipient of the token
/// @param id The id of the token
/// @param amount The amount of token of type `id`
function _beforeTokenTransfer(
address from,
address to,
uint256 id,
uint256 amount
) internal virtual {}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "openzeppelin/token/ERC20/IERC20.sol";
import "./ILBPair.sol";
import "./IPendingOwnable.sol";
/// @title Liquidity Book Factory Interface
/// @author Trader Joe
/// @notice Required interface of LBFactory contract
interface ILBFactory is IPendingOwnable {
/// @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 FeeParametersSet(
address indexed sender,
ILBPair indexed LBPair,
uint256 binStep,
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxVolatilityAccumulated
);
event FactoryLockedStatusUpdated(bool unlocked);
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 maxVolatilityAccumulated,
uint256 sampleLifetime
);
event PresetRemoved(uint256 indexed binStep);
event QuoteAssetAdded(IERC20 indexed quoteAsset);
event QuoteAssetRemoved(IERC20 indexed quoteAsset);
function MAX_FEE() external pure returns (uint256);
function MIN_BIN_STEP() external pure returns (uint256);
function MAX_BIN_STEP() external pure returns (uint256);
function MAX_PROTOCOL_SHARE() external pure returns (uint256);
function LBPairImplementation() external view returns (address);
function getNumberOfQuoteAssets() external view returns (uint256);
function getQuoteAsset(uint256 index) external view returns (IERC20);
function isQuoteAsset(IERC20 token) external view returns (bool);
function feeRecipient() external view returns (address);
function flashLoanFee() external view returns (uint256);
function creationUnlocked() external view returns (bool);
function allLBPairs(uint256 id) external returns (ILBPair);
function getNumberOfLBPairs() external view returns (uint256);
function getLBPairInformation(
IERC20 tokenX,
IERC20 tokenY,
uint256 binStep
) external view returns (LBPairInformation memory);
function getPreset(uint16 binStep)
external
view
returns (
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxAccumulator,
uint256 sampleLifetime
);
function getAllBinSteps() external view returns (uint256[] memory presetsBinStep);
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,
uint256 binStep,
bool ignored
) external;
function setPreset(
uint16 binStep,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulated,
uint16 sampleLifetime
) 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 maxVolatilityAccumulated
) external;
function setFeeRecipient(address feeRecipient) external;
function setFlashLoanFee(uint256 flashLoanFee) external;
function setFactoryLockedState(bool locked) 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 "openzeppelin/token/ERC20/IERC20.sol";
/// @title Liquidity Book Flashloan Callback Interface
/// @author Trader Joe
/// @notice Required interface to interact with LB flash loans
interface ILBFlashLoanCallback {
function LBFlashLoanCallback(
address sender,
IERC20 token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "openzeppelin/token/ERC20/IERC20.sol";
import "../libraries/FeeHelper.sol";
import "./ILBFactory.sol";
import "./ILBFlashLoanCallback.sol";
/// @title Liquidity Book Pair Interface
/// @author Trader Joe
/// @notice Required interface of LBPair contract
interface ILBPair {
/// @dev Structure to store the reserves of bins:
/// - reserveX: The current reserve of tokenX of the bin
/// - reserveY: The current reserve of tokenY of the bin
struct Bin {
uint112 reserveX;
uint112 reserveY;
uint256 accTokenXPerShare;
uint256 accTokenYPerShare;
}
/// @dev Structure to store the information of the pair such as:
/// slot0:
/// - activeId: The current id used for swaps, this is also linked with the price
/// - reserveX: The sum of amounts of tokenX across all bins
/// slot1:
/// - reserveY: The sum of amounts of tokenY across all bins
/// - oracleSampleLifetime: The lifetime of an oracle sample
/// - oracleSize: The current size of the oracle, can be increase by users
/// - oracleActiveSize: The current active size of the oracle, composed only from non empty data sample
/// - oracleLastTimestamp: The current last timestamp at which a sample was added to the circular buffer
/// - oracleId: The current id of the oracle
/// slot2:
/// - feesX: The current amount of fees to distribute in tokenX (total, protocol)
/// slot3:
/// - feesY: The current amount of fees to distribute in tokenY (total, protocol)
struct PairInformation {
uint24 activeId;
uint136 reserveX;
uint136 reserveY;
uint16 oracleSampleLifetime;
uint16 oracleSize;
uint16 oracleActiveSize;
uint40 oracleLastTimestamp;
uint16 oracleId;
FeeHelper.FeesDistribution feesX;
FeeHelper.FeesDistribution feesY;
}
/// @dev Structure to store the debts of users
/// - debtX: The tokenX's debt
/// - debtY: The tokenY's debt
struct Debts {
uint256 debtX;
uint256 debtY;
}
/// @dev Structure to store fees:
/// - tokenX: The amount of fees of token X
/// - tokenY: The amount of fees of token Y
struct Fees {
uint128 tokenX;
uint128 tokenY;
}
/// @dev Structure to minting informations:
/// - amountXIn: The amount of token X sent
/// - amountYIn: The amount of token Y sent
/// - amountXAddedToPair: The amount of token X that have been actually added to the pair
/// - amountYAddedToPair: The amount of token Y that have been actually added to the pair
/// - activeFeeX: Fees X currently generated
/// - activeFeeY: Fees Y currently generated
/// - totalDistributionX: Total distribution of token X. Should be 1e18 (100%) or 0 (0%)
/// - totalDistributionY: Total distribution of token Y. Should be 1e18 (100%) or 0 (0%)
/// - id: Id of the current working bin when looping on the distribution array
/// - amountX: The amount of token X deposited in the current bin
/// - amountY: The amount of token Y deposited in the current bin
/// - distributionX: Distribution of token X for the current working bin
/// - distributionY: Distribution of token Y for the current working bin
struct MintInfo {
uint256 amountXIn;
uint256 amountYIn;
uint256 amountXAddedToPair;
uint256 amountYAddedToPair;
uint256 activeFeeX;
uint256 activeFeeY;
uint256 totalDistributionX;
uint256 totalDistributionY;
uint256 id;
uint256 amountX;
uint256 amountY;
uint256 distributionX;
uint256 distributionY;
}
event Swap(
address indexed sender,
address indexed recipient,
uint256 indexed id,
bool swapForY,
uint256 amountIn,
uint256 amountOut,
uint256 volatilityAccumulated,
uint256 fees
);
event FlashLoan(
address indexed sender,
ILBFlashLoanCallback indexed receiver,
IERC20 token,
uint256 amount,
uint256 fee
);
event CompositionFee(
address indexed sender,
address indexed recipient,
uint256 indexed id,
uint256 feesX,
uint256 feesY
);
event DepositedToBin(
address indexed sender,
address indexed recipient,
uint256 indexed id,
uint256 amountX,
uint256 amountY
);
event WithdrawnFromBin(
address indexed sender,
address indexed recipient,
uint256 indexed id,
uint256 amountX,
uint256 amountY
);
event FeesCollected(address indexed sender, address indexed recipient, uint256 amountX, uint256 amountY);
event ProtocolFeesCollected(address indexed sender, address indexed recipient, uint256 amountX, uint256 amountY);
event OracleSizeIncreased(uint256 previousSize, uint256 newSize);
function tokenX() external view returns (IERC20);
function tokenY() external view returns (IERC20);
function factory() external view returns (ILBFactory);
function getReservesAndId()
external
view
returns (
uint256 reserveX,
uint256 reserveY,
uint256 activeId
);
function getGlobalFees()
external
view
returns (
uint128 feesXTotal,
uint128 feesYTotal,
uint128 feesXProtocol,
uint128 feesYProtocol
);
function getOracleParameters()
external
view
returns (
uint256 oracleSampleLifetime,
uint256 oracleSize,
uint256 oracleActiveSize,
uint256 oracleLastTimestamp,
uint256 oracleId,
uint256 min,
uint256 max
);
function getOracleSampleFrom(uint256 timeDelta)
external
view
returns (
uint256 cumulativeId,
uint256 cumulativeAccumulator,
uint256 cumulativeBinCrossed
);
function feeParameters() external view returns (FeeHelper.FeeParameters memory);
function findFirstNonEmptyBinId(uint24 id_, bool sentTokenY) external view returns (uint24 id);
function getBin(uint24 id) external view returns (uint256 reserveX, uint256 reserveY);
function pendingFees(address account, uint256[] memory ids)
external
view
returns (uint256 amountX, uint256 amountY);
function swap(bool sentTokenY, address to) external returns (uint256 amountXOut, uint256 amountYOut);
function flashLoan(
ILBFlashLoanCallback receiver,
IERC20 token,
uint256 amount,
bytes calldata data
) external;
function mint(
uint256[] calldata ids,
uint256[] calldata distributionX,
uint256[] calldata distributionY,
address to
)
external
returns (
uint256 amountXAddedToPair,
uint256 amountYAddedToPair,
uint256[] memory liquidityMinted
);
function burn(
uint256[] calldata ids,
uint256[] calldata amounts,
address to
) external returns (uint256 amountX, uint256 amountY);
function increaseOracleLength(uint16 newSize) external;
function collectFees(address account, uint256[] calldata ids) external returns (uint256 amountX, uint256 amountY);
function collectProtocolFees() external returns (uint128 amountX, uint128 amountY);
function setFeesParameters(bytes32 packedFeeParameters) external;
function forceDecay() external;
function initialize(
IERC20 tokenX,
IERC20 tokenY,
uint24 activeId,
uint16 sampleLifetime,
bytes32 packedFeeParameters
) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "openzeppelin/utils/introspection/IERC165.sol";
/// @title Liquidity Book Token Interface
/// @author Trader Joe
/// @notice Required interface of LBToken contract
interface ILBToken is IERC165 {
event TransferSingle(address indexed sender, address indexed from, address indexed to, 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 balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory batchBalances);
function totalSupply(uint256 id) external view returns (uint256);
function isApprovedForAll(address owner, address spender) external view returns (bool);
function setApprovalForAll(address sender, bool approved) external;
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount
) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata id,
uint256[] calldata amount
) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/// @title Liquidity Book Pending Ownable Interface
/// @author Trader Joe
/// @notice Required interface of Pending Ownable contract used for LBFactory
interface IPendingOwnable {
event PendingOwnerSet(address indexed pendingOwner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function setPendingOwner(address pendingOwner) external;
function revokePendingOwner() external;
function becomeOwner() external;
function renounceOwnership() external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
import "./Math128x128.sol";
/// @title Liquidity Book Bin Helper Library
/// @author Trader Joe
/// @notice Contract used to convert bin ID to price and back
library BinHelper {
using Math128x128 for uint256;
int256 private constant REAL_ID_SHIFT = 1 << 23;
/// @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
/// @param _binStep The bin step
/// @return The id corresponding to this price
function getIdFromPrice(uint256 _price, uint256 _binStep) internal pure returns (uint24) {
unchecked {
uint256 _binStepValue = _getBPValue(_binStep);
// can't overflow as `2**23 + log2(price) < 2**23 + 2**128 < max(uint256)`
int256 _id = REAL_ID_SHIFT + _price.log2() / _binStepValue.log2();
if (_id < 0 || uint256(_id) > type(uint24).max) revert BinHelper__IdOverflows();
return uint24(uint256(_id));
}
}
/// @notice Returns the price corresponding to the given ID, as a 128.128-binary fixed-point number
/// @dev This is the trusted function to link id to price, the other way may be inaccurate
/// @param _id The id
/// @param _binStep The bin step
/// @return The price corresponding to this id, as a 128.128-binary fixed-point number
function getPriceFromId(uint256 _id, uint256 _binStep) internal pure returns (uint256) {
if (_id > uint256(type(uint24).max)) revert BinHelper__IdOverflows();
unchecked {
int256 _realId = int256(_id) - REAL_ID_SHIFT;
return _getBPValue(_binStep).power(_realId);
}
}
/// @notice Returns the (1 + bp) value as a 128.128-decimal fixed-point number
/// @param _binStep The bp value in [1; 100] (referring to 0.01% to 1%)
/// @return The (1+bp) value as a 128.128-decimal fixed-point number
function _getBPValue(uint256 _binStep) internal pure returns (uint256) {
if (_binStep == 0 || _binStep > Constants.BASIS_POINT_MAX) revert BinHelper__BinStepOverflows(_binStep);
unchecked {
// can't overflow as `max(result) = 2**128 + 10_000 << 128 / 10_000 < max(uint256)`
return Constants.SCALE + (_binStep << Constants.SCALE_OFFSET) / Constants.BASIS_POINT_MAX;
}
}
}// 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 {
/// @notice Returns the closest non-zero bit of `integer` to the right (of left) of the `bit` bits that is not `bit`
/// @param _integer The integer as a uint256
/// @param _bit The bit index
/// @param _rightSide Whether we're searching in the right side of the tree (true) or the left side (false)
/// @return The index of the closest non-zero bit. If there is no closest bit, it returns max(uint256)
function closestBit(
uint256 _integer,
uint8 _bit,
bool _rightSide
) internal pure returns (uint256) {
return _rightSide ? closestBitRight(_integer, _bit - 1) : closestBitLeft(_integer, _bit + 1);
}
/// @notice Returns the most (or least) significant bit of `_integer`
/// @param _integer The integer
/// @param _isMostSignificant Whether we want the most (true) or the least (false) significant bit
/// @return The index of the most (or least) significant bit
function significantBit(uint256 _integer, bool _isMostSignificant) internal pure returns (uint8) {
return _isMostSignificant ? mostSignificantBit(_integer) : leastSignificantBit(_integer);
}
/// @notice 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;
}
}
/// @notice 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;
}
}
/// @notice Returns the index of the most significant bit of x
/// @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) {
unchecked {
if (x >= 1 << 128) {
x >>= 128;
msb = 128;
}
if (x >= 1 << 64) {
x >>= 64;
msb += 64;
}
if (x >= 1 << 32) {
x >>= 32;
msb += 32;
}
if (x >= 1 << 16) {
x >>= 16;
msb += 16;
}
if (x >= 1 << 8) {
x >>= 8;
msb += 8;
}
if (x >= 1 << 4) {
x >>= 4;
msb += 4;
}
if (x >= 1 << 2) {
x >>= 2;
msb += 2;
}
if (x >= 1 << 1) {
msb += 1;
}
}
}
/// @notice Returns the index of the least significant bit of x
/// @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) {
unchecked {
if (x << 128 != 0) {
x <<= 128;
lsb = 128;
}
if (x << 64 != 0) {
x <<= 64;
lsb += 64;
}
if (x << 32 != 0) {
x <<= 32;
lsb += 32;
}
if (x << 16 != 0) {
x <<= 16;
lsb += 16;
}
if (x << 8 != 0) {
x <<= 8;
lsb += 8;
}
if (x << 4 != 0) {
x <<= 4;
lsb += 4;
}
if (x << 2 != 0) {
x <<= 2;
lsb += 2;
}
if (x << 1 != 0) {
lsb += 1;
}
return 255 - lsb;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/// @title Liquidity Book Buffer Library
/// @author Trader Joe
/// @notice Helper contract used for modulo calculation
library Buffer {
/// @notice Internal function to do positive (x - 1) % n
/// @param x The value
/// @param n The modulo value
/// @return result The result
function before(uint256 x, uint256 n) internal pure returns (uint256 result) {
assembly {
if gt(n, 0) {
switch x
case 0 {
result := sub(n, 1)
}
default {
result := mod(sub(x, 1), n)
}
}
}
}
}// 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 {
uint256 internal constant SCALE_OFFSET = 128;
uint256 internal constant SCALE = 1 << SCALE_OFFSET;
uint256 internal constant PRECISION = 1e18;
uint256 internal constant BASIS_POINT_MAX = 10_000;
/// @dev The expected return after a successful flash loan
bytes32 internal constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/// @title Liquidity Book Decoder Library
/// @author Trader Joe
/// @notice Helper contract used for decoding bytes32 sample
library Decoder {
/// @notice Internal function to decode a bytes32 sample using a mask and offset
/// @dev This function can overflow
/// @param _sample The sample as a bytes32
/// @param _mask The mask
/// @param _offset The offset
/// @return value The decoded value
function decode(
bytes32 _sample,
uint256 _mask,
uint256 _offset
) internal pure returns (uint256 value) {
assembly {
value := and(shr(_offset, _sample), _mask)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/// @title Liquidity Book Encoder Library
/// @author Trader Joe
/// @notice Helper contract used for encoding uint256 value
library Encoder {
/// @notice Internal function to encode a uint256 value using a mask and offset
/// @dev This function can underflow
/// @param _value The value as a uint256
/// @param _mask The mask
/// @param _offset The offset
/// @return sample The encoded bytes32 sample
function encode(
uint256 _value,
uint256 _mask,
uint256 _offset
) internal pure returns (bytes32 sample) {
assembly {
sample := shl(_offset, and(_value, _mask))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
import "./Constants.sol";
import "./FeeHelper.sol";
/// @title Liquidity Book Fee Distribution Helper Library
/// @author Trader Joe
/// @notice Helper contract used for fees distribution calculations
library FeeDistributionHelper {
/// @notice Calculate the tokenPerShare when fees are added
/// @param _fees The fees received by the pair
/// @param _totalSupply the total supply of a specific bin
function getTokenPerShare(FeeHelper.FeesDistribution memory _fees, uint256 _totalSupply)
internal
pure
returns (uint256)
{
unchecked {
// This can't overflow as `totalFees >= protocolFees`,
// shift can't overflow as we shift fees that are a uint128, by 128 bits.
// The result will always be smaller than max(uint256)
return ((uint256(_fees.total) - _fees.protocol) << Constants.SCALE_OFFSET) / _totalSupply;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "./Constants.sol";
import "./SafeCast.sol";
import "./SafeMath.sol";
/// @title Liquidity Book Fee Helper Library
/// @author Trader Joe
/// @notice Helper contract used for fees calculation
library FeeHelper {
using SafeCast for uint256;
using SafeMath for uint256;
/// @dev Structure to store the protocol fees:
/// - binStep: The bin step
/// - baseFactor: The base factor
/// - filterPeriod: The filter period, where the fees stays constant
/// - decayPeriod: The decay period, where the fees are halved
/// - reductionFactor: The reduction factor, used to calculate the reduction of the accumulator
/// - variableFeeControl: The variable fee control, used to control the variable fee, can be 0 to disable them
/// - protocolShare: The share of fees sent to protocol
/// - maxVolatilityAccumulated: The max value of volatility accumulated
/// - volatilityAccumulated: The value of volatility accumulated
/// - volatilityReference: The value of volatility reference
/// - indexRef: The index reference
/// - time: The last time the accumulator was called
struct FeeParameters {
// 144 lowest bits in slot
uint16 binStep;
uint16 baseFactor;
uint16 filterPeriod;
uint16 decayPeriod;
uint16 reductionFactor;
uint24 variableFeeControl;
uint16 protocolShare;
uint24 maxVolatilityAccumulated;
// 112 highest bits in slot
uint24 volatilityAccumulated;
uint24 volatilityReference;
uint24 indexRef;
uint40 time;
}
/// @dev Structure used during swaps to distributes the fees:
/// - total: The total amount of fees
/// - protocol: The amount of fees reserved for protocol
struct FeesDistribution {
uint128 total;
uint128 protocol;
}
/// @notice Update the value of the volatility accumulated
/// @param _fp The current fee parameters
/// @param _activeId The current active id
function updateVariableFeeParameters(FeeParameters memory _fp, uint256 _activeId) internal view {
uint256 _deltaT = block.timestamp - _fp.time;
if (_deltaT >= _fp.filterPeriod || _fp.time == 0) {
_fp.indexRef = uint24(_activeId);
if (_deltaT < _fp.decayPeriod) {
unchecked {
// This can't overflow as `reductionFactor <= BASIS_POINT_MAX`
_fp.volatilityReference = uint24(
(uint256(_fp.reductionFactor) * _fp.volatilityAccumulated) / Constants.BASIS_POINT_MAX
);
}
} else {
_fp.volatilityReference = 0;
}
}
_fp.time = (block.timestamp).safe40();
updateVolatilityAccumulated(_fp, _activeId);
}
/// @notice Update the volatility accumulated
/// @param _fp The fee parameter
/// @param _activeId The current active id
function updateVolatilityAccumulated(FeeParameters memory _fp, uint256 _activeId) internal pure {
uint256 volatilityAccumulated = (_activeId.absSub(_fp.indexRef) * Constants.BASIS_POINT_MAX) +
_fp.volatilityReference;
_fp.volatilityAccumulated = volatilityAccumulated > _fp.maxVolatilityAccumulated
? _fp.maxVolatilityAccumulated
: uint24(volatilityAccumulated);
}
/// @notice Returns the base fee added to a swap, with 18 decimals
/// @param _fp The current fee parameters
/// @return The fee with 18 decimals precision
function getBaseFee(FeeParameters memory _fp) internal pure returns (uint256) {
unchecked {
return uint256(_fp.baseFactor) * _fp.binStep * 1e10;
}
}
/// @notice Returns the variable fee added to a swap, with 18 decimals
/// @param _fp The current fee parameters
/// @return variableFee The variable fee with 18 decimals precision
function getVariableFee(FeeParameters memory _fp) internal pure returns (uint256 variableFee) {
if (_fp.variableFeeControl != 0) {
// Can't overflow as the max value is `max(uint24) * (max(uint24) * max(uint16)) ** 2 < max(uint104)`
// It returns 18 decimals as:
// decimals(variableFeeControl * (volatilityAccumulated * binStep)**2 / 100) = 4 + (4 + 4) * 2 - 2 = 18
unchecked {
uint256 _prod = uint256(_fp.volatilityAccumulated) * _fp.binStep;
variableFee = (_prod * _prod * _fp.variableFeeControl + 99) / 100;
}
}
}
/// @notice Return the amount of fees from an amount
/// @dev Rounds amount up, follows `amount = amountWithFees - getFeeAmountFrom(fp, amountWithFees)`
/// @param _fp The current fee parameter
/// @param _amountWithFees The amount of token sent
/// @return The fee amount from the amount sent
function getFeeAmountFrom(FeeParameters memory _fp, uint256 _amountWithFees) internal pure returns (uint256) {
return (_amountWithFees * getTotalFee(_fp) + Constants.PRECISION - 1) / (Constants.PRECISION);
}
/// @notice Return the fees to add to an amount
/// @dev Rounds amount up, follows `amountWithFees = amount + getFeeAmount(fp, amount)`
/// @param _fp The current fee parameter
/// @param _amount The amount of token sent
/// @return The fee amount to add to the amount
function getFeeAmount(FeeParameters memory _fp, uint256 _amount) internal pure returns (uint256) {
uint256 _fee = getTotalFee(_fp);
uint256 _denominator = Constants.PRECISION - _fee;
return (_amount * _fee + _denominator - 1) / _denominator;
}
/// @notice Return the fees added when an user adds liquidity and change the ratio in the active bin
/// @dev Rounds amount up
/// @param _fp The current fee parameter
/// @param _amountWithFees The amount of token sent
/// @return The fee amount
function getFeeAmountForC(FeeParameters memory _fp, uint256 _amountWithFees) internal pure returns (uint256) {
uint256 _fee = getTotalFee(_fp);
uint256 _denominator = Constants.PRECISION * Constants.PRECISION;
return (_amountWithFees * _fee * (_fee + Constants.PRECISION) + _denominator - 1) / _denominator;
}
/// @notice Return the fees distribution added to an amount
/// @param _fp The current fee parameter
/// @param _fees The fee amount
/// @return fees The fee distribution
function getFeeAmountDistribution(FeeParameters memory _fp, uint256 _fees)
internal
pure
returns (FeesDistribution memory fees)
{
fees.total = _fees.safe128();
// unsafe math is fine because total >= protocol
unchecked {
fees.protocol = uint128((_fees * _fp.protocolShare) / Constants.BASIS_POINT_MAX);
}
}
/// @notice Return the total fee, i.e. baseFee + variableFee
/// @param _fp The current fee parameter
/// @return The total fee, with 18 decimals
function getTotalFee(FeeParameters memory _fp) private pure returns (uint256) {
unchecked {
return getBaseFee(_fp) + getVariableFee(_fp);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
import "./BitMath.sol";
import "./Constants.sol";
import "./Math512Bits.sol";
/// @title Liquidity Book Math Helper Library
/// @author Trader Joe
/// @notice Helper contract used for power and log calculations
library Math128x128 {
using Math512Bits for uint256;
using BitMath for uint256;
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 Math128x128__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^20; 2^20[
/// @return result The result of `x^y`
function power(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 pow := x
if gt(x, 0xffffffffffffffffffffffffffffffff) {
pow := div(not(0), pow)
invert := iszero(invert)
}
if and(absY, 0x1) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x2) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x4) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x8) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x10) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x20) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x40) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x80) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x100) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x200) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x400) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x800) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x1000) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x2000) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x4000) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x8000) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x10000) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x20000) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x40000) {
result := shr(128, mul(result, pow))
}
pow := shr(128, mul(pow, pow))
if and(absY, 0x80000) {
result := shr(128, mul(result, pow))
}
}
}
// revert if y is too big or if x^y underflowed
if (result == 0) revert Math128x128__PowerUnderflow(x, y);
return invert ? type(uint256).max / result : result;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
import "./BitMath.sol";
/// @title Liquidity Book Math Helper Library
/// @author Trader Joe
/// @notice Helper contract used for full precision calculations
library Math512Bits {
using BitMath for uint256;
/// @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 x * y >> 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,
uint256 offset
) internal pure returns (uint256 result) {
if (offset > 255) revert Math512Bits__OffsetOverflows(offset);
(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 Math512Bits__MulShiftOverflow(prod1, offset);
unchecked {
result += prod1 << (256 - offset);
}
}
}
/// @notice Calculates x * y >> offset 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 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,
uint256 offset
) internal pure returns (uint256 result) {
unchecked {
result = mulShiftRoundDown(x, y, offset);
if (mulmod(x, y, 1 << offset) != 0) result += 1;
}
}
/// @notice Calculates 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,
uint256 offset,
uint256 denominator
) internal pure returns (uint256 result) {
if (offset > 255) revert Math512Bits__OffsetOverflows(offset);
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 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,
uint256 offset,
uint256 denominator
) internal pure returns (uint256 result) {
result = shiftDivRoundDown(x, offset, denominator);
unchecked {
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 Math512Bits__MulDivOverflow(prod1, denominator);
// 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;
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
import "./Buffer.sol";
import "./Samples.sol";
/// @title Liquidity Book Oracle Library
/// @author Trader Joe
/// @notice Helper contract for oracle
library Oracle {
using Samples for bytes32;
using Buffer for uint256;
struct Sample {
uint256 timestamp;
uint256 cumulativeId;
uint256 cumulativeVolatilityAccumulated;
uint256 cumulativeBinCrossed;
}
/// @notice View function to get the oracle's sample at `_ago` seconds
/// @dev Return a linearized sample, the weighted average of 2 neighboring samples
/// @param _oracle The oracle storage pointer
/// @param _activeSize The size of the oracle (without empty data)
/// @param _activeId The active index of the oracle
/// @param _lookUpTimestamp The looked up date
/// @return timestamp The timestamp of the sample
/// @return cumulativeId The weighted average cumulative id
/// @return cumulativeVolatilityAccumulated The weighted average cumulative volatility accumulated
/// @return cumulativeBinCrossed The weighted average cumulative bin crossed
function getSampleAt(
bytes32[65_535] storage _oracle,
uint256 _activeSize,
uint256 _activeId,
uint256 _lookUpTimestamp
)
internal
view
returns (
uint256 timestamp,
uint256 cumulativeId,
uint256 cumulativeVolatilityAccumulated,
uint256 cumulativeBinCrossed
)
{
if (_activeSize == 0) revert Oracle__NotInitialized();
// Oldest sample
uint256 _nextId;
assembly {
_nextId := addmod(_activeId, 1, _activeSize)
}
bytes32 _sample = _oracle[_nextId];
timestamp = _sample.timestamp();
if (timestamp > _lookUpTimestamp) revert Oracle__LookUpTimestampTooOld(timestamp, _lookUpTimestamp);
// Most recent sample
if (_activeSize != 1) {
_sample = _oracle[_activeId];
timestamp = _sample.timestamp();
if (timestamp > _lookUpTimestamp) {
bytes32 _next;
(_sample, _next) = binarySearch(_oracle, _activeId, _lookUpTimestamp, _activeSize);
if (_sample != _next) {
uint256 _weightPrev = _next.timestamp() - _lookUpTimestamp; // _next.timestamp() - _sample.timestamp() - (_lookUpTimestamp - _sample.timestamp())
uint256 _weightNext = _lookUpTimestamp - _sample.timestamp(); // _next.timestamp() - _sample.timestamp() - (_next.timestamp() - _lookUpTimestamp)
uint256 _totalWeight = _weightPrev + _weightNext; // _next.timestamp() - _sample.timestamp()
cumulativeId =
(_sample.cumulativeId() * _weightPrev + _next.cumulativeId() * _weightNext) /
_totalWeight;
cumulativeVolatilityAccumulated =
(_sample.cumulativeVolatilityAccumulated() *
_weightPrev +
_next.cumulativeVolatilityAccumulated() *
_weightNext) /
_totalWeight;
cumulativeBinCrossed =
(_sample.cumulativeBinCrossed() * _weightPrev + _next.cumulativeBinCrossed() * _weightNext) /
_totalWeight;
return (_lookUpTimestamp, cumulativeId, cumulativeVolatilityAccumulated, cumulativeBinCrossed);
}
}
}
timestamp = _sample.timestamp();
cumulativeId = _sample.cumulativeId();
cumulativeVolatilityAccumulated = _sample.cumulativeVolatilityAccumulated();
cumulativeBinCrossed = _sample.cumulativeBinCrossed();
}
/// @notice Function to update a sample
/// @param _oracle The oracle storage pointer
/// @param _size The size of the oracle (last ids can be empty)
/// @param _sampleLifetime The lifetime of a sample, it accumulates information for up to this timestamp
/// @param _lastTimestamp The timestamp of the creation of the oracle's latest sample
/// @param _lastIndex The index of the oracle's latest sample
/// @param _activeId The active index of the pair during the latest swap
/// @param _volatilityAccumulated The volatility accumulated of the pair during the latest swap
/// @param _binCrossed The bin crossed during the latest swap
/// @return updatedIndex The oracle updated index, it is either the same as before, or the next one
function update(
bytes32[65_535] storage _oracle,
uint256 _size,
uint256 _sampleLifetime,
uint256 _lastTimestamp,
uint256 _lastIndex,
uint256 _activeId,
uint256 _volatilityAccumulated,
uint256 _binCrossed
) internal returns (uint256 updatedIndex) {
bytes32 _updatedPackedSample = _oracle[_lastIndex].update(_activeId, _volatilityAccumulated, _binCrossed);
if (block.timestamp - _lastTimestamp >= _sampleLifetime && _lastTimestamp != 0) {
assembly {
updatedIndex := addmod(_lastIndex, 1, _size)
}
} else updatedIndex = _lastIndex;
_oracle[updatedIndex] = _updatedPackedSample;
}
/// @notice Initialize the sample
/// @param _oracle The oracle storage pointer
/// @param _index The index to initialize
function initialize(bytes32[65_535] storage _oracle, uint256 _index) internal {
_oracle[_index] |= bytes32(uint256(1));
}
/// @notice Binary search on oracle samples and return the 2 samples (as bytes32) that surrounds the `lookUpTimestamp`
/// @dev The oracle needs to be in increasing order `{_index + 1, _index + 2 ..., _index + _activeSize} % _activeSize`.
/// The sample that aren't initialized yet will be skipped as _activeSize only contains the samples that are initialized.
/// This function works only if `timestamp(_oracle[_index + 1 % _activeSize] <= _lookUpTimestamp <= timestamp(_oracle[_index]`.
/// The edge cases needs to be handled before
/// @param _oracle The oracle storage pointer
/// @param _index The current index of the oracle
/// @param _lookUpTimestamp The looked up timestamp
/// @param _activeSize The size of the oracle (without empty data)
/// @return prev The last sample with a timestamp lower than the lookUpTimestamp
/// @return next The first sample with a timestamp greater than the lookUpTimestamp
function binarySearch(
bytes32[65_535] storage _oracle,
uint256 _index,
uint256 _lookUpTimestamp,
uint256 _activeSize
) private view returns (bytes32 prev, bytes32 next) {
// The sample with the lowest timestamp is the one right after _index
uint256 _low = 1;
uint256 _high = _activeSize;
uint256 _middle;
uint256 _id;
bytes32 _sample;
uint256 _sampleTimestamp;
while (_high >= _low) {
unchecked {
_middle = (_low + _high) >> 1;
assembly {
_id := addmod(_middle, _index, _activeSize)
}
_sample = _oracle[_id];
_sampleTimestamp = _sample.timestamp();
if (_sampleTimestamp < _lookUpTimestamp) {
_low = _middle + 1;
} else if (_sampleTimestamp > _lookUpTimestamp) {
_high = _middle - 1;
} else {
return (_sample, _sample);
}
}
}
if (_sampleTimestamp < _lookUpTimestamp) {
assembly {
_id := addmod(_id, 1, _activeSize)
}
(prev, next) = (_sample, _oracle[_id]);
} else (prev, next) = (_oracle[_id.before(_activeSize)], _sample);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
/// @title Reentrancy Guard
/// @author Trader Joe
/// @notice Contract module that helps prevent reentrant calls to a function
abstract contract ReentrancyGuardUpgradeable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal {
if (_status != 0) revert ReentrancyGuardUpgradeable__AlreadyInitialized();
_status = _NOT_ENTERED;
}
/// @notice Prevents a contract from calling itself, directly or indirectly.
/// Calling a `nonReentrant` function from another `nonReentrant`
/// function is not supported. It is possible to prevent this from happening
/// by making the `nonReentrant` function external, and making it call a
/// `private` function that does the actual work
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
if (_status != _NOT_ENTERED) revert ReentrancyGuardUpgradeable__ReentrantCall();
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
/// @title Liquidity Book Safe Cast Library
/// @author Trader Joe
/// @notice Helper contract used for converting uint values safely
library SafeCast {
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
/// @notice 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(x);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/// @title Liquidity Book Safe Math Helper Library
/// @author Trader Joe
/// @notice Helper contract used for calculating absolute value safely
library SafeMath {
/// @notice absSub, can't underflow or overflow
/// @param x The first value
/// @param y The second value
/// @return The result of abs(x - y)
function absSub(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
return x > y ? x - y : y - x;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "./Decoder.sol";
import "./Encoder.sol";
/// @title Liquidity Book Sample Helper Library
/// @author Trader Joe
/// @notice Helper contract used for oracle samples operations
library Samples {
using Encoder for uint256;
using Decoder for bytes32;
/// [ cumulativeBinCrossed | cumulativeVolatilityAccumulated | cumulativeId | timestamp | initialized ]
/// [ uint87 | uint64 | uint64 | uint40 | bool1 ]
/// MSB LSB
uint256 private constant _OFFSET_INITIALIZED = 0;
uint256 private constant _MASK_INITIALIZED = 1;
uint256 private constant _OFFSET_TIMESTAMP = 1;
uint256 private constant _MASK_TIMESTAMP = type(uint40).max;
uint256 private constant _OFFSET_CUMULATIVE_ID = 41;
uint256 private constant _MASK_CUMULATIVE_ID = type(uint64).max;
uint256 private constant _OFFSET_CUMULATIVE_VolatilityAccumulated = 105;
uint256 private constant _MASK_CUMULATIVE_VolatilityAccumulated = type(uint64).max;
uint256 private constant _OFFSET_CUMULATIVE_BIN_CROSSED = 169;
uint256 private constant _MASK_CUMULATIVE_BIN_CROSSED = 0x7fffffffffffffffffffff;
/// @notice Function to update a sample
/// @param _lastSample The latest sample of the oracle
/// @param _activeId The active index of the pair during the latest swap
/// @param _volatilityAccumulated The volatility accumulated of the pair during the latest swap
/// @param _binCrossed The bin crossed during the latest swap
/// @return packedSample The packed sample as bytes32
function update(
bytes32 _lastSample,
uint256 _activeId,
uint256 _volatilityAccumulated,
uint256 _binCrossed
) internal view returns (bytes32 packedSample) {
uint256 _deltaTime = block.timestamp - timestamp(_lastSample);
// cumulative can overflow without any issue as what matter is the delta cumulative.
// It would be an issue if 2 overflows would happen but way too much time should elapsed for it to happen.
// The delta calculation needs to be unchecked math to allow for it to overflow again.
unchecked {
uint256 _cumulativeId = cumulativeId(_lastSample) + _activeId * _deltaTime;
uint256 _cumulativeVolatilityAccumulated = cumulativeVolatilityAccumulated(_lastSample) +
_volatilityAccumulated *
_deltaTime;
uint256 _cumulativeBinCrossed = cumulativeBinCrossed(_lastSample) + _binCrossed * _deltaTime;
return pack(_cumulativeBinCrossed, _cumulativeVolatilityAccumulated, _cumulativeId, block.timestamp, 1);
}
}
/// @notice Function to pack cumulative values
/// @param _cumulativeBinCrossed The cumulative bin crossed
/// @param _cumulativeVolatilityAccumulated The cumulative volatility accumulated
/// @param _cumulativeId The cumulative index
/// @param _timestamp The timestamp
/// @param _initialized The initialized value
/// @return packedSample The packed sample as bytes32
function pack(
uint256 _cumulativeBinCrossed,
uint256 _cumulativeVolatilityAccumulated,
uint256 _cumulativeId,
uint256 _timestamp,
uint256 _initialized
) internal pure returns (bytes32 packedSample) {
return
_cumulativeBinCrossed.encode(_MASK_CUMULATIVE_BIN_CROSSED, _OFFSET_CUMULATIVE_BIN_CROSSED) |
_cumulativeVolatilityAccumulated.encode(
_MASK_CUMULATIVE_VolatilityAccumulated,
_OFFSET_CUMULATIVE_VolatilityAccumulated
) |
_cumulativeId.encode(_MASK_CUMULATIVE_ID, _OFFSET_CUMULATIVE_ID) |
_timestamp.encode(_MASK_TIMESTAMP, _OFFSET_TIMESTAMP) |
_initialized.encode(_MASK_INITIALIZED, _OFFSET_INITIALIZED);
}
/// @notice View function to return the initialized value
/// @param _packedSample The packed sample
/// @return The initialized value
function initialized(bytes32 _packedSample) internal pure returns (uint256) {
return _packedSample.decode(_MASK_INITIALIZED, _OFFSET_INITIALIZED);
}
/// @notice View function to return the timestamp value
/// @param _packedSample The packed sample
/// @return The timestamp value
function timestamp(bytes32 _packedSample) internal pure returns (uint256) {
return _packedSample.decode(_MASK_TIMESTAMP, _OFFSET_TIMESTAMP);
}
/// @notice View function to return the cumulative id value
/// @param _packedSample The packed sample
/// @return The cumulative id value
function cumulativeId(bytes32 _packedSample) internal pure returns (uint256) {
return _packedSample.decode(_MASK_CUMULATIVE_ID, _OFFSET_CUMULATIVE_ID);
}
/// @notice View function to return the cumulative volatility accumulated value
/// @param _packedSample The packed sample
/// @return The cumulative volatility accumulated value
function cumulativeVolatilityAccumulated(bytes32 _packedSample) internal pure returns (uint256) {
return _packedSample.decode(_MASK_CUMULATIVE_VolatilityAccumulated, _OFFSET_CUMULATIVE_VolatilityAccumulated);
}
/// @notice View function to return the cumulative bin crossed value
/// @param _packedSample The packed sample
/// @return The cumulative bin crossed value
function cumulativeBinCrossed(bytes32 _packedSample) internal pure returns (uint256) {
return _packedSample.decode(_MASK_CUMULATIVE_BIN_CROSSED, _OFFSET_CUMULATIVE_BIN_CROSSED);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "./BinHelper.sol";
import "./Constants.sol";
import "./FeeDistributionHelper.sol";
import "./FeeHelper.sol";
import "./Math512Bits.sol";
import "./SafeMath.sol";
import "../interfaces/ILBPair.sol";
/// @title Liquidity Book Swap Helper Library
/// @author Trader Joe
/// @notice Helper contract used for calculating swaps, fees and reserves changes
library SwapHelper {
using Math512Bits for uint256;
using FeeHelper for FeeHelper.FeeParameters;
using SafeMath for uint256;
using FeeDistributionHelper for FeeHelper.FeesDistribution;
/// @notice Returns the swap amounts in the current bin
/// @param bin The bin information
/// @param fp The fee parameters
/// @param activeId The active id of the pair
/// @param swapForY Whether you've swapping token X for token Y (true) or token Y for token X (false)
/// @param amountIn The amount sent by the user
/// @return amountInToBin The amount of token that is added to the bin without the fees
/// @return amountOutOfBin The amount of token that is removed from the bin
/// @return fees The swap fees
function getAmounts(
ILBPair.Bin memory bin,
FeeHelper.FeeParameters memory fp,
uint256 activeId,
bool swapForY,
uint256 amountIn
)
internal
pure
returns (
uint256 amountInToBin,
uint256 amountOutOfBin,
FeeHelper.FeesDistribution memory fees
)
{
uint256 _price = BinHelper.getPriceFromId(activeId, fp.binStep);
uint256 _reserve;
uint256 _maxAmountInToBin;
if (swapForY) {
_reserve = bin.reserveY;
_maxAmountInToBin = _reserve.shiftDivRoundUp(Constants.SCALE_OFFSET, _price);
} else {
_reserve = bin.reserveX;
_maxAmountInToBin = _price.mulShiftRoundUp(_reserve, Constants.SCALE_OFFSET);
}
fp.updateVolatilityAccumulated(activeId);
fees = fp.getFeeAmountDistribution(fp.getFeeAmount(_maxAmountInToBin));
if (_maxAmountInToBin + fees.total <= amountIn) {
amountInToBin = _maxAmountInToBin;
amountOutOfBin = _reserve;
} else {
fees = fp.getFeeAmountDistribution(fp.getFeeAmountFrom(amountIn));
amountInToBin = amountIn - fees.total;
amountOutOfBin = swapForY
? _price.mulShiftRoundDown(amountInToBin, Constants.SCALE_OFFSET)
: amountInToBin.shiftDivRoundDown(Constants.SCALE_OFFSET, _price);
// Safety check in case rounding returns a higher value than expected
if (amountOutOfBin > _reserve) amountOutOfBin = _reserve;
}
}
/// @notice Update the fees of the pair and accumulated token per share of the bin
/// @param bin The bin information
/// @param pairFees The current fees of the pair information
/// @param fees The fees amounts added to the pairFees
/// @param swapForY whether the token sent was Y (true) or X (false)
/// @param totalSupply The total supply of the token id
function updateFees(
ILBPair.Bin memory bin,
FeeHelper.FeesDistribution memory pairFees,
FeeHelper.FeesDistribution memory fees,
bool swapForY,
uint256 totalSupply
) internal pure {
pairFees.total += fees.total;
// unsafe math is fine because total >= protocol
unchecked {
pairFees.protocol += fees.protocol;
}
if (swapForY) {
bin.accTokenXPerShare += fees.getTokenPerShare(totalSupply);
} else {
bin.accTokenYPerShare += fees.getTokenPerShare(totalSupply);
}
}
/// @notice Update reserves
/// @param bin The bin information
/// @param pair The pair information
/// @param swapForY whether the token sent was Y (true) or X (false)
/// @param amountInToBin The amount of token that is added to the bin without fees
/// @param amountOutOfBin The amount of token that is removed from the bin
function updateReserves(
ILBPair.Bin memory bin,
ILBPair.PairInformation memory pair,
bool swapForY,
uint112 amountInToBin,
uint112 amountOutOfBin
) internal pure {
if (swapForY) {
bin.reserveX += amountInToBin;
unchecked {
bin.reserveY -= amountOutOfBin;
pair.reserveX += uint136(amountInToBin);
pair.reserveY -= uint136(amountOutOfBin);
}
} else {
bin.reserveY += amountInToBin;
unchecked {
bin.reserveX -= amountOutOfBin;
pair.reserveX -= uint136(amountOutOfBin);
pair.reserveY += uint136(amountInToBin);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "openzeppelin/token/ERC20/IERC20.sol";
import "../LBErrors.sol";
/// @title Safe Transfer
/// @author Trader Joe
/// @notice Wrappers around ERC20 operations that throw on failure (when the token
/// contract returns false). Tokens that return no value (and instead revert or
/// throw on failure) are also supported, non-reverting calls are assumed to be
/// successful.
/// To use this library you can add a `using TokenHelper for IERC20;` statement to your contract,
/// which allows you to call the safe operation as `token.safeTransfer(...)`
library TokenHelper {
/// @notice Transfers token only if the amount is greater than zero
/// @param token The address of the token
/// @param owner The owner of the tokens
/// @param recipient The address of the recipient
/// @param amount The amount to send
function safeTransferFrom(
IERC20 token,
address owner,
address recipient,
uint256 amount
) internal {
if (amount != 0) {
bytes memory data = abi.encodeWithSelector(token.transferFrom.selector, owner, recipient, amount);
bytes memory returnData = _callAndCatchError(address(token), data);
if (returnData.length > 0 && !abi.decode(returnData, (bool))) revert TokenHelper__TransferFailed();
}
}
/// @notice Transfers token only if the amount is greater than zero
/// @param token The address of the token
/// @param recipient The address of the recipient
/// @param amount The amount to send
function safeTransfer(
IERC20 token,
address recipient,
uint256 amount
) internal {
if (amount != 0) {
bytes memory data = abi.encodeWithSelector(token.transfer.selector, recipient, amount);
bytes memory returnData = _callAndCatchError(address(token), data);
if (returnData.length > 0 && !abi.decode(returnData, (bool))) revert TokenHelper__TransferFailed();
}
}
/// @notice Returns the amount of token received by the pair
/// @param token The address of the token
/// @param reserve The total reserve of token
/// @param fees The total fees of token
/// @return The amount received by the pair
function received(
IERC20 token,
uint256 reserve,
uint256 fees
) internal view returns (uint256) {
uint256 _internalBalance;
unchecked {
_internalBalance = reserve + fees;
}
return token.balanceOf(address(this)) - _internalBalance;
}
/// @notice Private view function to perform a low level call on `target`
/// @dev Revert if the call doesn't succeed
/// @param target The address of the account
/// @param data The data to execute on `target`
/// @return returnData The data returned by the call
function _callAndCatchError(address target, bytes memory data) private returns (bytes memory) {
(bool success, bytes memory returnData) = target.call(data);
if (success) {
if (returnData.length == 0 && !_isContract(target)) revert TokenHelper__NonContract();
} else {
if (returnData.length == 0) revert TokenHelper__CallFailed();
else {
// Look for revert reason and bubble it up if present
assembly {
revert(add(32, returnData), mload(returnData))
}
}
}
return returnData;
}
/// @notice Private view function to return if an address is a contract
/// @dev It is unsafe to assume that an address for which this function returns
/// false is an externally-owned account (EOA) and not a contract.
///
/// Among others, `isContract` will return false for the following
/// types of addresses:
/// - an externally-owned account
/// - a contract in construction
/// - an address where a contract will be created
/// - an address where a contract lived, but was destroyed
/// @param account The address of the account
/// @return Whether the account is a contract (true) or not (false)
function _isContract(address account) private view returns (bool) {
return account.code.length > 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../LBErrors.sol";
import "./BitMath.sol";
/// @title Liquidity Book Tree Math Library
/// @author Trader Joe
/// @notice Helper contract used for finding closest bin with liquidity
library TreeMath {
using BitMath for uint256;
/// @notice Returns the first id that is non zero, corresponding to a bin with
/// liquidity in it
/// @param _tree The storage slot of the tree
/// @param _binId the binId to start searching
/// @param _rightSide Whether we're searching in the right side of the tree (true) or the left side (false)
/// for the closest non zero bit on the right or the left
/// @return The closest non zero bit on the right (or left) side of the tree
function findFirstBin(
mapping(uint256 => uint256)[3] storage _tree,
uint24 _binId,
bool _rightSide
) internal view returns (uint24) {
unchecked {
uint256 current;
uint256 bit;
(_binId, bit) = _getIdsFromAbove(_binId);
// Search in depth 2
if ((_rightSide && bit != 0) || (!_rightSide && bit != 255)) {
current = _tree[2][_binId];
bit = current.closestBit(uint8(bit), _rightSide);
if (bit != type(uint256).max) {
return _getBottomId(_binId, uint24(bit));
}
}
(_binId, bit) = _getIdsFromAbove(_binId);
// Search in depth 1
if ((_rightSide && bit != 0) || (!_rightSide && bit != 255)) {
current = _tree[1][_binId];
bit = current.closestBit(uint8(bit), _rightSide);
if (bit != type(uint256).max) {
_binId = _getBottomId(_binId, uint24(bit));
current = _tree[2][_binId];
return _getBottomId(_binId, current.significantBit(_rightSide));
}
}
// Search in depth 0
current = _tree[0][0];
bit = current.closestBit(uint8(_binId), _rightSide);
if (bit == type(uint256).max) revert TreeMath__ErrorDepthSearch();
current = _tree[1][bit];
_binId = _getBottomId(uint24(bit), current.significantBit(_rightSide));
current = _tree[2][_binId];
return _getBottomId(_binId, current.significantBit(_rightSide));
}
}
function addToTree(mapping(uint256 => uint256)[3] storage _tree, uint256 _id) internal {
// add 1 at the right indices
uint256 _idDepth2 = _id >> 8;
uint256 _idDepth1 = _id >> 16;
_tree[2][_idDepth2] |= 1 << (_id & 255);
_tree[1][_idDepth1] |= 1 << (_idDepth2 & 255);
_tree[0][0] |= 1 << _idDepth1;
}
function removeFromTree(mapping(uint256 => uint256)[3] storage _tree, uint256 _id) internal {
unchecked {
// removes 1 at the right indices
uint256 _idDepth2 = _id >> 8;
// Optimization of `_tree[2][_idDepth2] & (type(uint256).max - (1 << (_id & 255)))`
uint256 _newLeafValue = _tree[2][_idDepth2] & (type(uint256).max ^ (1 << (_id & 255)));
_tree[2][_idDepth2] = _newLeafValue;
if (_newLeafValue == 0) {
uint256 _idDepth1 = _id >> 16;
// Optimization of `_tree[1][_idDepth1] & (type(uint256).max - (1 << (_idDepth2 & 255)))`
_newLeafValue = _tree[1][_idDepth1] & (type(uint256).max ^ (1 << (_idDepth2 & 255)));
_tree[1][_idDepth1] = _newLeafValue;
if (_newLeafValue == 0) {
// Optimization of `type(uint256).max - (1 << _idDepth1)`
_tree[0][0] &= type(uint256).max ^ (1 << _idDepth1);
}
}
}
}
/// @notice Private pure function to return the ids from above
/// @param _id The current id
/// @return The branch id from above
/// @return The leaf id from above
function _getIdsFromAbove(uint24 _id) private pure returns (uint24, uint24) {
// Optimization of `(_id / 256, _id % 256)`
return (_id >> 8, _id & 255);
}
/// @notice Private pure function to return the bottom id
/// @param _branchId The branch id
/// @param _leafId The leaf id
/// @return The bottom branchId
function _getBottomId(uint24 _branchId, uint24 _leafId) private pure returns (uint24) {
// Optimization of `_branchId * 256 + _leafId`
// Can't overflow as _leafId would fit in uint8, but kept as uint24 to optimize castings
unchecked {
return (_branchId << 8) + _leafId;
}
}
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin-contracts/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 800
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract ILBFactory","name":"_factory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"bp","type":"uint256"}],"name":"BinHelper__BinStepOverflows","type":"error"},{"inputs":[],"name":"BinHelper__IdOverflows","type":"error"},{"inputs":[],"name":"LBPair__AddressZero","type":"error"},{"inputs":[],"name":"LBPair__AddressZeroOrThis","type":"error"},{"inputs":[],"name":"LBPair__AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"LBPair__CompositionFactorFlawed","type":"error"},{"inputs":[],"name":"LBPair__DistributionsOverflow","type":"error"},{"inputs":[],"name":"LBPair__FlashLoanCallbackFailed","type":"error"},{"inputs":[],"name":"LBPair__FlashLoanInvalidBalance","type":"error"},{"inputs":[],"name":"LBPair__FlashLoanInvalidToken","type":"error"},{"inputs":[],"name":"LBPair__InsufficientAmounts","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"LBPair__InsufficientLiquidityBurned","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"LBPair__InsufficientLiquidityMinted","type":"error"},{"inputs":[],"name":"LBPair__OnlyFactory","type":"error"},{"inputs":[{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"sender","type":"address"}],"name":"LBPair__OnlyFeeRecipient","type":"error"},{"inputs":[],"name":"LBPair__OnlyStrictlyIncreasingId","type":"error"},{"inputs":[{"internalType":"uint256","name":"newSize","type":"uint256"},{"internalType":"uint256","name":"oracleSize","type":"uint256"}],"name":"LBPair__OracleNewSizeTooSmall","type":"error"},{"inputs":[],"name":"LBPair__WrongLengths","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__BurnFromAddress0","type":"error"},{"inputs":[{"internalType":"uint256","name":"accountsLength","type":"uint256"},{"internalType":"uint256","name":"idsLength","type":"uint256"}],"name":"LBToken__LengthMismatch","type":"error"},{"inputs":[],"name":"LBToken__MintToAddress0","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"LBToken__SelfApproval","type":"error"},{"inputs":[{"internalType":"address","name":"owner","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":"LBToken__TransferFromOrToAddress0","type":"error"},{"inputs":[],"name":"LBToken__TransferToSelf","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"Math128x128__PowerUnderflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"prod1","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"Math512Bits__MulDivOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"prod1","type":"uint256"},{"internalType":"uint256","name":"offset","type":"uint256"}],"name":"Math512Bits__MulShiftOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"}],"name":"Math512Bits__OffsetOverflows","type":"error"},{"inputs":[{"internalType":"uint256","name":"_minTimestamp","type":"uint256"},{"internalType":"uint256","name":"_lookUpTimestamp","type":"uint256"}],"name":"Oracle__LookUpTimestampTooOld","type":"error"},{"inputs":[],"name":"Oracle__NotInitialized","type":"error"},{"inputs":[],"name":"ReentrancyGuardUpgradeable__AlreadyInitialized","type":"error"},{"inputs":[],"name":"ReentrancyGuardUpgradeable__ReentrantCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"SafeCast__Exceeds112Bits","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"SafeCast__Exceeds128Bits","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"SafeCast__Exceeds24Bits","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"SafeCast__Exceeds40Bits","type":"error"},{"inputs":[],"name":"TokenHelper__CallFailed","type":"error"},{"inputs":[],"name":"TokenHelper__NonContract","type":"error"},{"inputs":[],"name":"TokenHelper__TransferFailed","type":"error"},{"inputs":[],"name":"TreeMath__ErrorDepthSearch","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":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feesX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feesY","type":"uint256"}],"name":"CompositionFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"DepositedToBin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"FeesCollected","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":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSize","type":"uint256"}],"name":"OracleSizeIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"ProtocolFeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bool","name":"swapForY","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"volatilityAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"}],"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":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountX","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"WithdrawnFromBin","type":"event"},{"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":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"collectFees","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectProtocolFees","outputs":[{"internalType":"uint128","name":"amountX","type":"uint128"},{"internalType":"uint128","name":"amountY","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract ILBFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeParameters","outputs":[{"components":[{"internalType":"uint16","name":"binStep","type":"uint16"},{"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":"maxVolatilityAccumulated","type":"uint24"},{"internalType":"uint24","name":"volatilityAccumulated","type":"uint24"},{"internalType":"uint24","name":"volatilityReference","type":"uint24"},{"internalType":"uint24","name":"indexRef","type":"uint24"},{"internalType":"uint40","name":"time","type":"uint40"}],"internalType":"struct FeeHelper.FeeParameters","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"_id","type":"uint24"},{"internalType":"bool","name":"_swapForY","type":"bool"}],"name":"findFirstNonEmptyBinId","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBFlashLoanCallback","name":"_receiver","type":"address"},{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forceDecay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"_id","type":"uint24"}],"name":"getBin","outputs":[{"internalType":"uint256","name":"reserveX","type":"uint256"},{"internalType":"uint256","name":"reserveY","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalFees","outputs":[{"internalType":"uint128","name":"feesXTotal","type":"uint128"},{"internalType":"uint128","name":"feesYTotal","type":"uint128"},{"internalType":"uint128","name":"feesXProtocol","type":"uint128"},{"internalType":"uint128","name":"feesYProtocol","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleParameters","outputs":[{"internalType":"uint256","name":"oracleSampleLifetime","type":"uint256"},{"internalType":"uint256","name":"oracleSize","type":"uint256"},{"internalType":"uint256","name":"oracleActiveSize","type":"uint256"},{"internalType":"uint256","name":"oracleLastTimestamp","type":"uint256"},{"internalType":"uint256","name":"oracleId","type":"uint256"},{"internalType":"uint256","name":"min","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timeDelta","type":"uint256"}],"name":"getOracleSampleFrom","outputs":[{"internalType":"uint256","name":"cumulativeId","type":"uint256"},{"internalType":"uint256","name":"cumulativeVolatilityAccumulated","type":"uint256"},{"internalType":"uint256","name":"cumulativeBinCrossed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReservesAndId","outputs":[{"internalType":"uint256","name":"reserveX","type":"uint256"},{"internalType":"uint256","name":"reserveY","type":"uint256"},{"internalType":"uint256","name":"activeId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_newLength","type":"uint16"}],"name":"increaseOracleLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_tokenX","type":"address"},{"internalType":"contract IERC20","name":"_tokenY","type":"address"},{"internalType":"uint24","name":"_activeId","type":"uint24"},{"internalType":"uint16","name":"_sampleLifetime","type":"uint16"},{"internalType":"bytes32","name":"_packedFeeParameters","type":"bytes32"}],"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":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_distributionX","type":"uint256[]"},{"internalType":"uint256[]","name":"_distributionY","type":"uint256[]"},{"internalType":"address","name":"_to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"liquidityMinted","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"pendingFees","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","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":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_packedFeeParameters","type":"bytes32"}],"name":"setFeesParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_swapForY","type":"bool"},{"internalType":"address","name":"_to","type":"address"}],"name":"swap","outputs":[{"internalType":"uint256","name":"amountXOut","type":"uint256"},{"internalType":"uint256","name":"amountYOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"tokenX","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenY","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"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
60a06040523480156200001157600080fd5b506040516200601b3803806200601b83398101604081905262000034916200006e565b6001600160a01b0381166200005c576040516317be04b760e01b815260040160405180910390fd5b6001600160a01b0316608052620000a0565b6000602082840312156200008157600080fd5b81516001600160a01b03811681146200009957600080fd5b9392505050565b608051615f3c620000df6000396000818161063c0152818161185101528181612cea015281816130bf0152818161323d01526141a60152615f3c6000f3fe608060405234801561001057600080fd5b50600436106101e45760003560e01c80638f919a831161010f578063bd85b039116100a2578063d3b9fbe411610071578063d3b9fbe414610684578063e985e9c51461068c578063f7cff1f81461069f578063fba0ee64146106b257600080fd5b8063bd85b03914610617578063c45a015514610637578063c7bd65861461065e578063d32db4371461067157600080fd5b8063a21635a7116100de578063a21635a7146105a3578063a22cb465146105b6578063a582cdaa146105c9578063b7d19fc41461060457600080fd5b80638f919a83146103f357806395d89b411461041a57806398c7adf314610439578063a1af5b9a1461057b57600080fd5b80631b05b83e1161018757806354b5fc871161015657806354b5fc871461036e57806355182894146103815780635cffe9de146103be578063714c8592146103d157600080fd5b80631b05b83e146102ef578063225b20b9146103285780634e1273f41461033b57806353c059a01461035b57600080fd5b80630abe9688116101c35780630abe9688146102745780630acd451d1461029c5780630febdd49146102af57806316dc165b146102c457600080fd5b8062fdd58e146101e957806301ffc9a71461020f57806306fdde0314610232575b600080fd5b6101fc6101f73660046154c0565b6106c5565b6040519081526020015b60405180910390f35b61022261021d3660046154ec565b6106ed565b6040519015158152602001610206565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b6040516102069190615542565b610287610282366004615588565b610718565b60408051928352602083019190915201610206565b6102876102aa3660046155ef565b61072d565b6102c26102bd366004615673565b6109f8565b005b6004546102d7906001600160a01b031681565b6040516001600160a01b039091168152602001610206565b6007546006546001600160881b03601882901c811692169062ffffff165b60408051938452602084019290925290820152606001610206565b6102876103363660046156b9565b610b1c565b61034e61034936600461570e565b610da9565b60405161020691906157b5565b6102876103693660046157d6565b610e9f565b6102c261037c36600461580f565b611846565b6103896118c8565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610206565b6102c26103cc366004615828565b61193d565b6103e46103df3660046158c7565b611da0565b60405161020693929190615975565b610406610401366004615994565b612ca3565b60405162ffffff9091168152602001610206565b60408051808201909152600381526213109560ea1b6020820152610267565b61056e6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810191909152506040805161018081018252600a5461ffff80821683526201000082048116602084015264010000000082048116938301939093526601000000000000810483166060830152600160401b8104831660808301526a0100000000000000000000810462ffffff90811660a0840152600160681b820490931660c0830152600160781b8104831660e0830152600160901b81048316610100830152600160a81b81048316610120830152600160c01b8104909216610140820152600160d81b90910464ffffffffff1661016082015290565b60405161020691906159c0565b610583612cb8565b604080516001600160801b03938416815292909116602083015201610206565b61030d6105b136600461580f565b612ee2565b6102c26105c4366004615ab2565b613064565b6105d1613073565b604080516001600160801b0395861681529385166020850152918416918301919091529091166060820152608001610206565b6005546102d7906001600160a01b031681565b6101fc61062536600461580f565b60009081526002602052604090205490565b6102d77f000000000000000000000000000000000000000000000000000000000000000081565b6102c261066c366004615ae2565b6130ab565b6102c261067f366004615afd565b6130b4565b6102c2613232565b61022261069a366004615b5d565b613318565b6102876106ad3660046156b9565b613324565b6102c26106c0366004615b7b565b613477565b6000818152602081815260408083206001600160a01b03861684529091529020545b92915050565b60006106f882613608565b806106e757506001600160e01b0319821663a22f6d6d60e01b1492915050565b6000806107248361363e565b91509150915091565b6000806001600354146107535760405163802d9bf160e01b815260040160405180910390fd5b60026003558515806107655750858414155b15610783576040516306fed5ff60e01b815260040160405180910390fd5b6007546006546001600160881b03601882901c811692169062ffffff1660005b898110156109535760006107ce8c8c848181106107c2576107c2615c10565b90506020020135613688565b905060008a8a848181106107e4576107e4615c10565b905060200201359050806000141561081b57604051636715a79f60e11b815262ffffff831660048201526024015b60405180910390fd5b6000806108278461363e565b9150915060006108488562ffffff1660009081526002602052604090205490565b9050600080888762ffffff1611610875576108648685856136b6565b9b8c019b998a900399938490039390505b888762ffffff161061089d5761088c8686856136b6565b9c8d019c9a8b90039a948590039491505b841580156108a9575083155b156108be576108be600c62ffffff89166136e0565b6000878152600b60205260409020607085901b860190556108e53062ffffff891688613765565b8662ffffff168e6001600160a01b0316336001600160a01b03167fda5e7177dface55f5e0eff7dfc67420a1db4243ddfcf0ecc84ed93e034dd8cc28585604051610939929190918252602082015260400190565b60405180910390a4505050505050508060010190506107a3565b50600680547fffffffffffffffffffffffff0000000000000000000000000000000000ffffff1663010000006001600160881b0386811691909102919091179091556007805470ffffffffffffffffffffffffffffffffff19169184169190911790556004546109cd906001600160a01b0316878761386a565b6005546109e4906001600160a01b0316878661386a565b505060016003555090969095509350505050565b83836001600160a01b0382161580610a1757506001600160a01b038116155b15610a355760405163020d233960e51b815260040160405180910390fd5b806001600160a01b0316826001600160a01b03161415610a6857604051638696f16760e01b815260040160405180910390fd5b8533610a74828261391f565b610aa45760405163548f773d60e01b81526001600160a01b03808416600483015282166024820152604401610812565b33610ab18989898961396b565b876001600160a01b0316896001600160a01b0316826001600160a01b03167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628a8a604051610b09929190918252602082015260400190565b60405180910390a4505050505050505050565b600080600160035414610b425760405163802d9bf160e01b815260040160405180910390fd5b60026003556001600160a01b0385161580610b6557506001600160a01b03851630145b15610b835760405163bdfe054560e01b815260040160405180910390fd5b6001600160a01b0385166000908152600f6020526040812080549190556001600160801b0381169250608081901c915060005b84811015610c92576000868683818110610bd257610bd2615c10565b9050602002013590506000610be789836106c5565b90508015610c88576000828152600b60209081526040808320815160808101835281546001600160701b038082168352600160701b90910416938101939093526001810154918301919091526002015460608201529080610c4a838d8787613a0b565b6001600160801b031691506001600160801b03169150610c6c838d8787613a96565b610c76828a615c3c565b9850610c828189615c3c565b97505050505b5050600101610bb6565b508215610cdc5760088054849190600090610cb79084906001600160801b0316615c54565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b8115610d255760098054839190600090610d009084906001600160801b0316615c54565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b600454610d3c906001600160a01b0316878561386a565b600554610d53906001600160a01b0316878461386a565b60408051848152602081018490526001600160a01b0388169133917f28a87b6059180e46de5fb9ab35eb043e8fe00ab45afcc7789e3934ecbbcde3ea910160405180910390a35060016003559094909350915050565b60608382808214610dd757604051639c802a7560e01b81526004810183905260248101829052604401610812565b8567ffffffffffffffff811115610df057610df0615c7c565b604051908082528060200260200182016040528015610e19578160200160208202803683370190505b50925060005b86811015610e9457610e6f888883818110610e3c57610e3c615c10565b9050602002016020810190610e519190615c92565b878784818110610e6357610e63615c10565b905060200201356106c5565b848281518110610e8157610e81615c10565b6020908102919091010152600101610e1f565b505050949350505050565b600080600160035414610ec55760405163802d9bf160e01b815260040160405180910390fd5b6002600355604080516101408101825260065462ffffff811682526001600160881b03630100000090910481166020808401919091526007549182168385015261ffff600160881b830481166060850152600160981b830481166080850152600160a81b8304811660a085015264ffffffffff600160b81b84041660c0850152600160e01b90920490911660e0830152825180840184526008546001600160801b038082168352600160801b9182900481168385015261010085019290925284518086019095526009548083168652041690830152610120810191909152600085610fe857604082015161012083015151600554610fe3926001600160a01b03909116916001600160881b03909116906001600160801b0316613af7565b611021565b602082015161010083015151600454611021926001600160a01b03909116916001600160881b03909116906001600160801b0316613af7565b9050806110415760405163256111db60e21b815260040160405180910390fd5b6040805161018081018252600a5461ffff80821683526201000082048116602084015264010000000082048116938301939093526601000000000000810483166060830152600160401b81048316608083015262ffffff6a01000000000000000000008204811660a0840152600160681b820490931660c0830152600160781b8104831660e0830152600160901b81048316610100830152600160a81b81048316610120830152600160c01b8104831661014083015264ffffffffff600160d81b90910416610160820152835190911661111b8282613b7b565b60005b845162ffffff166000908152600b6020908152604091829020825160808101845281546001600160701b038082168352600160701b909104169281019290925260018101549282019290925260029091015460608201528915801561118c575080516001600160701b031615155b806111ab57508980156111ab575060208101516001600160701b031615155b1561132c5760008060006111d7878a6000015162ffffff168f8b88613c3890949392919063ffffffff16565b9250925092506112228d6111f0578961012001516111f7565b8961010001515b828f6112188d6000015162ffffff1660009081526002602052604090205490565b8893929190613d5c565b611243898e61123086613ddd565b61123986613ddd565b8893929190613e0a565b8051611258906001600160801b031684615c3c565b6112629089615caf565b975061126e8286615c3c565b945083600b60008b6000015162ffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160701b0302191690836001600160701b03160217905550602082015181600001600e6101000a8154816001600160701b0302191690836001600160701b0316021790555060408201518160010155606082015181600201559050506113288c8a600001518f86868c610100015162ffffff1687600001516001600160801b0316613ec3565b5050505b841561134d57855161134190600c908c613f31565b62ffffff168652611353565b50611359565b5061111e565b60006113c4866080015161ffff16876060015161ffff168860c0015164ffffffffff168960e0015161ffff168a6000015162ffffff1689610100015162ffffff166113b68d6000015162ffffff168b6140e790919063ffffffff16565b6011969594939291906140fe565b90508560e0015161ffff16811415806113e6575060c086015164ffffffffff16155b1561142a5761ffff811660e08701526113fe42614176565b64ffffffffff1660c087015260a086015161ffff1681141561142a5760a08601805160010161ffff1690525b83600a60008201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548161ffff021916908361ffff16021790555060408201518160000160046101000a81548161ffff021916908361ffff16021790555060608201518160000160066101000a81548161ffff021916908361ffff16021790555060808201518160000160086101000a81548161ffff021916908361ffff16021790555060a082015181600001600a6101000a81548162ffffff021916908362ffffff16021790555060c082015181600001600d6101000a81548161ffff021916908361ffff16021790555060e082015181600001600f6101000a81548162ffffff021916908362ffffff1602179055506101008201518160000160126101000a81548162ffffff021916908362ffffff1602179055506101208201518160000160156101000a81548162ffffff021916908362ffffff1602179055506101408201518160000160186101000a81548162ffffff021916908362ffffff16021790555061016082015181600001601b6101000a81548164ffffffffff021916908364ffffffffff16021790555090505085600660008201518160000160006101000a81548162ffffff021916908362ffffff16021790555060208201518160000160036101000a8154816001600160881b0302191690836001600160881b0316021790555060408201518160010160006101000a8154816001600160881b0302191690836001600160881b0316021790555060608201518160010160116101000a81548161ffff021916908361ffff16021790555060808201518160010160136101000a81548161ffff021916908361ffff16021790555060a08201518160010160156101000a81548161ffff021916908361ffff16021790555060c08201518160010160176101000a81548164ffffffffff021916908364ffffffffff16021790555060e082015181600101601c6101000a81548161ffff021916908361ffff1602179055506101008201518160020160008201518160000160006101000a8154816001600160801b0302191690836001600160801b0316021790555060208201518160000160106101000a8154816001600160801b0302191690836001600160801b0316021790555050506101208201518160030160008201518160000160006101000a8154816001600160801b0302191690836001600160801b0316021790555060208201518160000160106101000a8154816001600160801b0302191690836001600160801b0316021790555050509050508915611815576005549196508691611810906001600160a01b03168a8461386a565b611831565b6004549197508791611831906001600160a01b03168a8461386a565b50505050505060016003819055509250929050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461188f57604051632486085360e01b815260040160405180910390fd5b600a805471ffffffffffffffffffffffffffffffffffff831671ffffffffffffffffffffffffffffffffffff1990911617905550565b50565b600080600080600080600061190b60075461ffff608882901c811692609883901c82169260a881901c83169264ffffffffff60b883901c169260e09290921c1690565b939a5091985096509450925084156119235786611926565b60005b91506119328588615cc6565b905090919293949596565b6001600354146119605760405163802d9bf160e01b815260040160405180910390fd5b60026003556004546001600160a01b03908116908516811480159061199357506005546001600160a01b03868116911614155b156119b15760405163992dfe2760e01b815260040160405180910390fd5b60006119bc856141a1565b9050600060405180604001604052806119d48461425c565b6001600160801b03168152600a5460209091019061271090611a0190600160681b900461ffff1686615cc6565b611a0b9190615cfb565b6001600160801b031690526040516370a0823160e01b81523060048201529091506000906001600160a01b038916906370a0823190602401602060405180830381865afa158015611a60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a849190615d1d565b9050611a9a6001600160a01b0389168a8961386a565b8151604051635dbacec560e01b81527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9916001600160a01b038c1691635dbacec591611af39133918e918e91908e908e90600401615d36565b6020604051808303816000875af1158015611b12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b369190615d1d565b14611b5457604051634a57011360e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611b9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbf9190615d1d565b8351909150611bd7906001600160801b031683615c3c565b8114611bf6576040516314e232bd60e01b815260040160405180910390fd5b60065462ffffff166000818152600260205260409020548515611d3157866001600160a01b03168b6001600160a01b03161415611cbd57600080611c546008546009546001600160801b038083169390821692608090811c92901c90565b5089519294509250611c859160089150611c6e9085615d9a565b60208a0151611c7d9085615d9a565b60801b169055565b611c8f8784614289565b6000858152600b602052604081206001018054909190611cb0908490615c3c565b90915550611d3192505050565b600080611ce46008546009546001600160801b038083169390821692608090811c92901c90565b8a519295509350611cfe9250600991611c6e915085615d9a565b611d088784614289565b6000858152600b602052604081206002018054909190611d29908490615c3c565b909155505050505b8451604080516001600160a01b038e81168252602082018e90526001600160801b03909316818301529051918e169133917f3659d15bd4bb92ab352a8d35bc3119ec6e7e0ab48e4d46201c8a28e02b6a8a86919081900360600190a35050600160035550505050505050505050565b6000806060600160035414611dc85760405163802d9bf160e01b815260040160405180910390fd5b6002600355881580611dda5750888714155b80611de55750888514155b15611e03576040516306fed5ff60e01b815260040160405180910390fd5b60006006604051806101400160405290816000820160009054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160039054906101000a90046001600160881b03166001600160881b03166001600160881b031681526020016001820160009054906101000a90046001600160881b03166001600160881b03166001600160881b031681526020016001820160119054906101000a900461ffff1661ffff1661ffff1681526020016001820160139054906101000a900461ffff1661ffff1661ffff1681526020016001820160159054906101000a900461ffff1661ffff1661ffff1681526020016001820160179054906101000a900464ffffffffff1664ffffffffff1664ffffffffff16815260200160018201601c9054906101000a900461ffff1661ffff1661ffff168152602001600282016040518060400160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b0316815250508152602001600382016040518060400160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b0316815250508152505090506000600a604051806101800160405290816000820160009054906101000a900461ffff1661ffff1661ffff1681526020016000820160029054906101000a900461ffff1661ffff1661ffff1681526020016000820160049054906101000a900461ffff1661ffff1661ffff1681526020016000820160069054906101000a900461ffff1661ffff1661ffff1681526020016000820160089054906101000a900461ffff1661ffff1661ffff16815260200160008201600a9054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600d9054906101000a900461ffff1661ffff1661ffff16815260200160008201600f9054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160129054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160159054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160189054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201601b9054906101000a900464ffffffffff1664ffffffffff1664ffffffffff16815250509050612238604051806101a00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60208301516101008401515160045461227a92612275926001600160a01b03909216916001600160881b03909116906001600160801b0316613af7565b613ddd565b6001600160701b031681526040830151610120840151516005546122c292612275926001600160a01b03909216916001600160881b03909116906001600160801b0316613af7565b6001600160701b031660208201528b67ffffffffffffffff8111156122e9576122e9615c7c565b604051908082528060200260200182016040528015612312578160200160208202803683370190505b50935060005b8c811015612a19576123358e8e838181106107c2576107c2615c10565b62ffffff1661010083018190526000908152600b6020908152604091829020825160808101845281546001600160701b03808216808452600160701b90920416938201939093526001820154938101939093526002015460608301521580156123a9575060208101516001600160701b0316155b1561242f57610100830151600881901c6000818152600e602090815260408083208054600160ff80891682901b90921790925560109690961c808552600d845291842080549690951681901b909517909355908052600c90527f13649b2456f1b42fef0f0040b3aaeabcd21a76a0f3f5defd4f583839455116e880549290911b90911790555b8c8c8381811061244157612441615c10565b905060200201358360c0018181516124599190615c3c565b9052508a8a8381811061246e5761246e615c10565b905060200201358360e0018181516124869190615c3c565b905250670de0b6b3a76400008d8d848181106124a4576124a4615c10565b90506020020135846000015102816124be576124be615ce5565b04610120840152670de0b6b3a76400008b8b848181106124e0576124e0615c10565b90506020020135846020015102816124fa576124fa615ce5565b0461014084015261010083015184516000916125199161ffff166142c2565b9050856000015162ffffff168461010001511061280d57856000015162ffffff1684610100015114156127e05781516001600160701b031615158061256a575060208201516001600160701b031615155b156127db57610100840151600090815260026020526040812054905060008060008761014001516125ac89610120015160808861430a9092919063ffffffff16565b6125b69190615c3c565b905060006125c48286615c3c565b90506125ef89610120015188600001516001600160701b03166125e79190615c3c565b8390836136b6565b935061261289610140015188602001516001600160701b03166125e79190615c3c565b9250505061262e87610100015189613b7b90919063ffffffff16565b60408051808201909152600080825260208201528288610120015111156126cf5761267361266c848a6101200151038b61439990919063ffffffff16565b8a90614405565b905080600001516001600160801b031688610120018181516126959190615caf565b90525080516080890180516001600160801b03909216916126b7908390615c3c565b9052506101008a01516126cf90879083600188613d5c565b818861014001511115612755576126f961266c838a6101400151038b61439990919063ffffffff16565b905080600001516001600160801b0316886101400181815161271b9190615caf565b905250805160a0890180516001600160801b039092169161273d908390615c3c565b9052506101208a015161275590879083600088613d5c565b60008860800151118061276c575060008860a00151115b156127d6578761010001518e6001600160a01b0316336001600160a01b03167f56f8e764728c77dd99ffbc1b64e6d02e227e6ec8214f165d4ef31351de136a0d8b608001518c60a001516040516127cd929190918252602082015260400190565b60405180910390a45b505050505b61283a565b610140840151156127db57836101000151604051632262d0a160e21b815260040161081291815260200190565b6101208401511561283a57836101000151604051632262d0a160e21b815260040161081291815260200190565b600084610140015161285d86610120015160808561430a9092919063ffffffff16565b6128679190615c3c565b9050806128905784610100015160405163b42cd28760e01b815260040161081291815260200190565b808885815181106128a3576128a3615c10565b6020908102919091010152610120850151835184906128c3908390615dc5565b6001600160701b03169052506101408501516020840180516128e6908390615dc5565b6001600160701b03908116909152610120870180516020808c0180516001600160881b0393861601831690526101408a0180516040808f0180519288169290920190941690529251828b018051909101905291516060808b0180519092019091526101008a0180516000908152600b855283902089518154958b01518716600160701b026001600160e01b0319909616961695909517939093178455908701516001840155860151600290920191909155516129a591508c9083614451565b8461010001518b6001600160a01b0316336001600160a01b03167f4216cc3bd0c40a90259d92f800c06ede5c47765f41a488072b7e7104a1f95841886101200151896101400151604051612a03929190918252602082015260400190565b60405180910390a4836001019350505050612318565b50670de0b6b3a76400008160c001511180612a3f5750670de0b6b3a76400008160e00151115b15612a5d5760405163eee8ff2760e01b815260040160405180910390fd5b82516006805460208087015162ffffff90941673ffffffffffffffffffffffffffffffffffffffff199092169190911763010000006001600160881b0394851602179091556040808601516007805460608901516080808b015160a08c015160c08d015160e08e015197909a167fffffffffffffffffffffffffff0000000000000000000000000000000000000090951694909417600160881b61ffff94851602177fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff16600160981b918416919091027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff1617600160a81b93831693909302929092177fffff00000000000000ffffffffffffffffffffffffffffffffffffffffffffff16600160b81b64ffffffffff909816979097027fffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff1696909617600160e01b96909316959095029190911790556101008601518051908301516001600160801b03918216600160801b918316820217600855610120880151805194015193821693909116029190911760095590820151908201518251910190811015612c3f578151600454612c3f916001600160a01b03909116908a9084900361386a565b60008260a0015183606001510190508083602001511115612c7c576020830151600554612c7c916001600160a01b03909116908b9084900361386a565b505060408101516060909101516001600355909d909c50929a509198505050505050505050565b6000612cb1600c8484613f31565b9392505050565b600080600160035414612cde5760405163802d9bf160e01b815260040160405180910390fd5b600260038190555060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663469048406040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6a9190615de7565b9050336001600160a01b03821614612da657604051631ed01e0560e31b81526001600160a01b0382166004820152336024820152604401610812565b600080600080612dd06008546009546001600160801b038083169390821692608090811c92901c90565b93509350935093506001826001600160801b03161115612e2d57612df5600183615c54565b9650612e018785615c54565b600160801b81166008559350600454612e2d906001600160a01b0316866001600160801b038a1661386a565b6001816001600160801b03161115612e8257612e4a600182615c54565b9550612e568684615c54565b600160801b81166009559250600554612e82906001600160a01b0316866001600160801b03891661386a565b604080516001600160801b03808a168252881660208201526001600160a01b0387169133917f26b782206d6b531bf95d487110cfefdc443291f176f1977e94abcb7e67bd1b79910160405180910390a35050600160035550929391925050565b6000808080612ef18542615caf565b9050600080612f2e60075461ffff608882901c811692609883901c82169260a881901c83169264ffffffffff60b883901c169260e09290921c1690565b945050935050506000612f4f838386601161450f909392919063ffffffff16565b91995097509550905083811015613059576040805161018081018252600a5461ffff80821683526201000082048116602084015264010000000082048116938301939093526601000000000000810483166060830152600160401b81048316608083015262ffffff6a01000000000000000000008204811660a0840152600160681b820490931660c0830152600160781b8104831660e0830152600160901b81048316610100830152600160a81b81048316610120830152600160c01b8104831661014083015264ffffffffff600160d81b9091041661016082015260065490911661303b8282613b7b565b61010090910151828603918202989098019762ffffff160295909501945b505050509193909250565b61306f33838361471a565b5050565b60008060008061309d6008546009546001600160801b038083169390821692608090811c92901c90565b935093509350935090919293565b6118c5816147c5565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146130fd57604051632486085360e01b815260040160405180910390fd5b6001600160a01b038516158061311a57506001600160a01b038416155b15613138576040516317be04b760e01b815260040160405180910390fd5b6004546001600160a01b031615613162576040516359e270a560e01b815260040160405180910390fd5b61316a61489a565b6004805473ffffffffffffffffffffffffffffffffffffffff199081166001600160a01b0388811691909117909255600580549091169186169190911790556006805462ffffff191662ffffff8516179055600780547fffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffff16600160881b61ffff851602179055600a805471ffffffffffffffffffffffffffffffffffff191671ffffffffffffffffffffffffffffffffffff831617905561322b60026147c5565b5050505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461327b57604051632486085360e01b815260040160405180910390fd5b600a54612710906132a390600160a81b810462ffffff1690600160401b900461ffff16615cc6565b6132ad9190615cfb565b600a80546006547fffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffff909116600160a81b62ffffff948516027fffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff16179216600160c01b02919091179055565b6000612cb1838361391f565b6000806001600160a01b03851630148061334557506001600160a01b038516155b156133555750600090508061346f565b6001600160a01b0385166000908152600f60205260409020546001600160801b0381169250608081901c91506000805b8581101561346b5760008787838181106133a1576133a1615c10565b9050602002013590508083101580156133b957508115155b156133d757604051630e15c23b60e31b815260040160405180910390fd5b60006133e38a836106c5565b90508015613460576000828152600b60209081526040808320815160808101835281546001600160701b038082168352600160701b90910416938101939093526001810154918301919091526002015460608201529080613446838e8787613a0b565b6001600160801b039182169b909b019a1698909801975050505b509150600101613385565b5050505b935093915050565b82818082146134a357604051639c802a7560e01b81526004810183905260248101829052604401610812565b87876001600160a01b03821615806134c257506001600160a01b038116155b156134e05760405163020d233960e51b815260040160405180910390fd5b806001600160a01b0316826001600160a01b0316141561351357604051638696f16760e01b815260040160405180910390fd5b893361351f828261391f565b61354f5760405163548f773d60e01b81526001600160a01b03808416600483015282166024820152604401610812565b60005b8981101561359e576135968d8d8d8d8581811061357157613571615c10565b905060200201358c8c8681811061358a5761358a615c10565b9050602002013561396b565b600101613552565b508a6001600160a01b03168c6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8d8d8d8d6040516135f29493929190615e53565b60405180910390a4505050505050505050505050565b60006001600160e01b031982166337eb4f6360e21b14806106e757506001600160e01b031982166301ffc9a760e01b1492915050565b6000818152600b60205260409020546001600160701b0381811691607081901c9161366884613ddd565b61367184613ddd565b6001600160701b0391821697911695509350505050565b8062ffffff811681146136b157604051631da527cf60e21b815260048101839052602401610812565b919050565b60008060006136c586866148a4565b915091506136d686868685856148c3565b9695505050505050565b600881901c600081815260028401602052604090208054600160ff85161b6000191816908190558061375f5750601082901c600081815260018581016020526040909120805460ff85169290921b6000191890911690819055908161322b5760008080526020869052604090208054600190921b600019189190911690555b50505050565b6001600160a01b03831661378c57604051631306fc5960e11b815260040160405180910390fd5b6000828152602081815260408083206001600160a01b0387168452909152902054818110156137e657604051625b387760e31b81526001600160a01b03851660048201526024810184905260448101839052606401610812565b6137f38460008585614976565b6000838152602081815260408083206001600160a01b0388168085529083528184208686039055868452600283528184208054879003905581518781529283018690529133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a450505050565b801561391a57604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b17905260006138d88583614a78565b9050600081511180156138fc5750808060200190518101906138fa9190615e85565b155b1561322b5760405163197138bd60e11b815260040160405180910390fd5b505050565b6000816001600160a01b0316836001600160a01b03161480612cb15750506001600160a01b03918216600090815260016020908152604080832093909416825291909152205460ff1690565b6000828152602081815260408083206001600160a01b0388168452909152902054818110156139c6576040516358b4690f60e01b81526001600160a01b03861660048201526024810184905260448101839052606401610812565b6139d285858585614976565b6000928352602083815260408085206001600160a01b039788168652909152808420918390039091559290931681522080549091019055565b6001600160a01b038316600090815260106020908152604080832085845282528083208151808301835281548082526001909201549381019390935290870151839291613a6d91613a5e9087608061430a565b613a689190615caf565b61425c565b60208201516060890151919450613a8a91613a5e9087608061430a565b91505094509492505050565b6040840151600090613aaa9083608061430a565b6060860151909150600090613ac19084608061430a565b6001600160a01b039590951660009081526010602090815260408083209683529590529390932090815560010192909255505050565b6040516370a0823160e01b81523060048201526000908284019081906001600160a01b038716906370a0823190602401602060405180830381865afa158015613b44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b689190615d1d565b613b729190615caf565b95945050505050565b600082610160015164ffffffffff1642613b959190615caf565b9050826040015161ffff1681101580613bb8575061016083015164ffffffffff16155b15613c185762ffffff8216610140840152606083015161ffff16811015613c0f5761271083610100015162ffffff16846080015161ffff160281613bfe57613bfe615ce5565b0462ffffff16610120840152613c18565b60006101208401525b613c2142614176565b64ffffffffff1661016084015261391a8383614b43565b604080518082019091526000808252602082018190529081906000613c6587896000015161ffff166142c2565b90506000808715613c935760208b01516001600160701b03169150613c8c82608085614bb5565b9050613caf565b8a516001600160701b03169150613cac83836080614be7565b90505b613cb98a8a614b43565b613ccd613cc68b83614c19565b8b90614405565b80519094508790613ce7906001600160801b031683615c3c565b11613cf757809550819450613d4e565b613d04613cc68b89614c4b565b8051909450613d1c906001600160801b031688615caf565b955087613d3457613d2f86608085614c74565b613d40565b613d408387608061430a565b945081851115613d4e578194505b505050955095509592505050565b825184518590613d6d908390615d9a565b6001600160801b0390811690915260208086015190870180519091019091169052508115613db857613d9f8382614289565b85604001818151613db09190615c3c565b90525061322b565b613dc28382614289565b85606001818151613dd39190615c3c565b9052505050505050565b806001600160701b03811681146136b157604051631ce5dcf760e01b815260048101839052602401610812565b8215613e69578185600001818151613e229190615dc5565b6001600160701b03908116909152602087810180518590038316905286018051858316016001600160881b039081169091526040870180519285169092031690525061322b565b8185602001818151613e7b9190615dc5565b6001600160701b039081169091528651839003811687526020860180516001600160881b03858416909103811690915260408701805192861692909201169052505050505050565b60408051861515815260208101869052908101849052606081018390526080810182905262ffffff8716906001600160a01b0389169033907fc528cda9e500228b16ce84fadae290d9a49aecb17483110004c5af0a07f6fd739060a00160405180910390a450505050505050565b61ffff600883901c1691600090819060ff16838015613f4f57508015155b80613f64575083158015613f6457508060ff14155b15613faf5762ffffff851660009081526002870160205260409020549150613f8d828286614cb7565b90506000198114613faf5763ffffff00600886901b1681015b92505050612cb1565b5061ffff600885901c169360ff16838015613fc957508015155b80613fde575083158015613fde57508060ff14155b1561405b5762ffffff851660009081526001870160205260409020549150614007828286614cb7565b9050600019811461405b5762ffffff60089590951b63ffffff0016810194851660009081526002870160205260409020549150613fa6856140488487614ce6565b60ff1660089190911b63ffffff00160190565b6000808052602087905260409020549150614077828686614cb7565b905060001981141561409c576040516310d6486160e01b815260040160405180910390fd5b600081815260018701602052604090205491506140bd816140488487614ce6565b945085600262ffffff871660009081529101602052604090205491506136d6856140488487614ce6565b60008183116140f857828203612cb1565b50900390565b6000806141248585858d8a61ffff811061411a5761411a615c10565b0154929190614d04565b9050876141318842615caf565b1015801561413e57508615155b1561414f5788600187089150614153565b8591505b808a8361ffff811061416757614167615c10565b01555098975050505050505050565b8064ffffffffff811681146136b1576040516302dfd99760e01b815260048101839052602401610812565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634847cdc86040518163ffffffff1660e01b8152600401602060405180830381865afa158015614202573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142269190615d1d565b9050670de0b6b3a764000060018161423e8487615cc6565b6142489190615c3c565b6142529190615caf565b612cb19190615cfb565b806001600160801b03811681146136b15760405163089f6cfb60e21b815260048101839052602401610812565b600081608084602001516001600160801b031685600001516001600160801b031603901b816142ba576142ba615ce5565b049392505050565b600062ffffff8311156142e85760405163163d8bab60e21b815260040160405180910390fd5b627fffff198301614302816142fc85614dca565b90614e10565b949350505050565b600060ff82111561433157604051630b72ecf560e41b815260048101839052602401610812565b60008061433e86866148a4565b91509150816000146143505781841c92505b8015614390576001841b811061438357604051633d90990f60e01b81526004810182905260248101859052604401610812565b836101000381901b830192505b50509392505050565b6000806143a584615069565b905060006143bb670de0b6b3a764000080615cc6565b9050806001816143d3670de0b6b3a764000086615c3c565b6143dd8689615cc6565b6143e79190615cc6565b6143f19190615c3c565b6143fb9190615caf565b613b729190615cfb565b60408051808201909152600080825260208201526144228261425c565b6001600160801b0316815260c08301516127109061ffff168302046001600160801b0316602082015292915050565b6001600160a01b03831661447857604051632a97acbf60e11b815260040160405180910390fd5b6144856000848484614976565b600082815260026020526040812080548392906144a3908490615c3c565b90915550506000828152602081815260408083206001600160a01b0387168085529083528184208054860190558151868152928301859052929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a4505050565b6000808080866145325760405163c686b52f60e01b815260040160405180910390fd5b6000876001880890506000898261ffff811061455057614550615c10565b0154905061455d81615092565b95508686111561458a576040516340a02e1d60e01b81526004810187905260248101889052604401610812565b886001146146e057898861ffff81106145a5576145a5615c10565b015490506145b281615092565b9550868611156146e05760006145ca8b8a8a8d6150a5565b90925090508181146146de576000886145e283615092565b6145ec9190615caf565b905060006145f984615092565b614603908b615caf565b905060006146118284615c3c565b9050808261461e86615182565b6146289190615cc6565b8461463288615182565b61463c9190615cc6565b6146469190615c3c565b6146509190615cfb565b9850808261465d86615198565b6146679190615cc6565b8461467188615198565b61467b9190615cc6565b6146859190615c3c565b61468f9190615cfb565b9750808261469c866151ae565b6146a69190615cc6565b846146b0886151ae565b6146ba9190615cc6565b6146c49190615c3c565b6146ce9190615cfb565b96508a995050505050505061470f565b505b6146e981615092565b95506146f481615182565b94506146ff81615198565b935061470a816151ae565b925050505b945094509450949050565b816001600160a01b0316836001600160a01b031614156147585760405163782ee70760e01b81526001600160a01b0384166004820152602401610812565b6001600160a01b03838116600081815260016020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b60075461ffff600160981b9091048116908216811061480457604051635762a96b60e11b815261ffff8316600482015260248101829052604401610812565b600780547fffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffff16600160981b61ffff851602179055805b8261ffff16811015614859576148516011826151ba565b60010161483a565b506040805182815261ffff841660208201527f525a4241308ea122822834c841f67b00d5efc977ad9118724750f974f7f6531c910160405180910390a15050565b6148a26151db565b565b6000806000198385098385029250828110838203039150509250929050565b6000816148e1578383816148d9576148d9615ce5565b049050613b72565b83821061490b5760405163656b542b60e11b81526004810183905260248101859052604401610812565b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b826001600160a01b0316846001600160a01b03161461375f576000828152600b6020908152604091829020825160808101845281546001600160701b038082168352600160701b909104169281019290925260018101549282019290925260029091015460608201526001600160a01b03851615801590614a0057506001600160a01b0385163014155b15614a2b576000614a1186856106c5565b9050614a2982878684614a248882615caf565b615203565b505b6001600160a01b03841615801590614a4c57506001600160a01b0384163014155b1561322b576000614a5d85856106c5565b9050614a7082868684614a248882615c3c565b505050505050565b6060600080846001600160a01b031684604051614a959190615ea2565b6000604051808303816000865af19150503d8060008114614ad2576040519150601f19603f3d011682016040523d82523d6000602084013e614ad7565b606091505b50915091508115614b1c578051158015614af957506001600160a01b0385163b155b15614b1757604051635d97df8960e01b815260040160405180910390fd5b614302565b8051614b3b57604051632407429160e01b815260040160405180910390fd5b805181602001fd5b600082610120015162ffffff16612710614b7085610140015162ffffff16856140e790919063ffffffff16565b614b7a9190615cc6565b614b849190615c3c565b90508260e0015162ffffff168111614b9c5780614ba2565b8260e001515b62ffffff16610100909301929092525050565b6000614bc2848484614c74565b90508180614bd257614bd2615ce5565b6001841b850915612cb1576001019392505050565b6000614bf484848461430a565b90506001821b80614c0757614c07615ce5565b83850915612cb1576001019392505050565b600080614c2584615069565b90506000614c3b82670de0b6b3a7640000615caf565b9050806001816143e78588615cc6565b6000670de0b6b3a76400006001670de0b6b3a7640000614c6a86615069565b61423e9086615cc6565b600060ff831115614c9b57604051630b72ecf560e41b815260048101849052602401610812565b83831b61010084900385901c6136d6866001871b8685856148c3565b600081614cd257614b1784614ccd856001615ebe565b6152b1565b61430284614ce1600186615ee3565b6152df565b600081614cfb57614cf683615310565b612cb1565b612cb1836153a6565b600080614d1086615092565b614d1a9042615caf565b90506000818602614d2a88615182565b0190506000828602614d3b89615198565b0190506000838602614d4c8a6151ae565b0190506501fffffffffe42600190811b919091166d01fffffffffffffffe0000000000602986901b167501fffffffffffffffe00000000000000000000000000606986901b167ffffffffffffffffffffffe00000000000000000000000000000000000000000060a986901b16171717179998505050505050505050565b6000811580614dda575061271082115b15614dfb576040516374da1e1160e11b815260048101839052602401610812565b612710608083901b04600160801b0192915050565b6000808083614e285750600160801b91506106e79050565b50826000811215614e3a579015906000035b6210000081101561502a57600160801b9250846001600160801b03811115614e6457911591600019045b6001821615614e755792830260801c925b800260801c6002821615614e8b5792830260801c925b800260801c6004821615614ea15792830260801c925b800260801c6008821615614eb75792830260801c925b800260801c6010821615614ecd5792830260801c925b800260801c6020821615614ee35792830260801c925b800260801c6040821615614ef95792830260801c925b8002608090811c90821615614f105792830260801c925b800260801c610100821615614f275792830260801c925b800260801c610200821615614f3e5792830260801c925b800260801c610400821615614f555792830260801c925b800260801c610800821615614f6c5792830260801c925b800260801c611000821615614f835792830260801c925b800260801c612000821615614f9a5792830260801c925b800260801c614000821615614fb15792830260801c925b800260801c618000821615614fc85792830260801c925b800260801c62010000821615614fe05792830260801c925b800260801c62020000821615614ff85792830260801c925b800260801c620400008216156150105792830260801c925b800260801c620800008216156150285792830260801c925b505b8261505257604051630e9c7d6160e31b81526004810186905260248101859052604401610812565b8161505d5782613b72565b613b7283600019615cfb565b600061507482615435565b8251602084015161ffff9182169116026402540be400020192915050565b6000600182901c64ffffffffff166106e7565b600080600183828080805b85851061511d5785850160011c9350888b850892508b8361ffff81106150d8576150d8615c10565b015491506150e582615092565b9050898110156150fa578360010195506150b0565b8981111561510d576001840394506150b0565b5095508594506151799350505050565b8981101561514c5788600184089250818c8461ffff811061514057615140615c10565b01549098509650615172565b8b615157848b615481565b61ffff811061516857615168615c10565b0154975090955085905b5050505050505b94509492505050565b6000602982901c67ffffffffffffffff166106e7565b6000606982901c67ffffffffffffffff166106e7565b600060a982901c6106e7565b6001828261ffff81106151cf576151cf615c10565b01805490911790555050565b600354156151fc5760405163d80075c160e01b815260040160405180910390fd5b6001600355565b6001600160a01b0384166000908152600f6020526040812054906001600160801b03821690608083901c908061523b8a8a8a8a613a0b565b9150915061524b8a8a8a89613a96565b6152558285615d9a565b93506152618184615d9a565b6001600160a01b03999099166000908152600f602052604090206001600160801b039490941660809990991b6fffffffffffffffffffffffffffffffff1916989098179092555050505050505050565b60ff81169190911c90600082156152d557816152cc84615310565b0160ff16612cb1565b5060001992915050565b60ff8181031691821b91600090831561530557806152fc856153a6565b60ff1603614302565b506000199392505050565b6000608082901b156153245750608090811b905b604082901b1561533657604091821b91015b602082901b1561534857602091821b91015b601082901b1561535a57601091821b91015b600882901b1561536c57600891821b91015b600482901b1561537e57600491821b91015b600282901b1561539057600291821b91015b600182901b1561539e576001015b60ff03919050565b6000600160801b82106153bb5750608090811c905b600160401b82106153ce57604091821c91015b64010000000082106153e257602091821c91015b6201000082106153f457601091821c91015b610100821061540557600891821c91015b6010821061541557600491821c91015b6004821061542557600291821c91015b600282106136b157600101919050565b60008160a0015162ffffff166000146136b1576000826000015161ffff1683610100015162ffffff1602905060648360a0015162ffffff1682830202606301816142ba576142ba615ce5565b600081156106e75782801561549d5782600185030691506154a4565b6001830391505b5092915050565b6001600160a01b03811681146118c557600080fd5b600080604083850312156154d357600080fd5b82356154de816154ab565b946020939093013593505050565b6000602082840312156154fe57600080fd5b81356001600160e01b031981168114612cb157600080fd5b60005b83811015615531578181015183820152602001615519565b8381111561375f5750506000910152565b6020815260008251806020840152615561816040850160208701615516565b601f01601f19169190910160400192915050565b803562ffffff811681146136b157600080fd5b60006020828403121561559a57600080fd5b612cb182615575565b60008083601f8401126155b557600080fd5b50813567ffffffffffffffff8111156155cd57600080fd5b6020830191508360208260051b85010111156155e857600080fd5b9250929050565b60008060008060006060868803121561560757600080fd5b853567ffffffffffffffff8082111561561f57600080fd5b61562b89838a016155a3565b9097509550602088013591508082111561564457600080fd5b50615651888289016155a3565b9094509250506040860135615665816154ab565b809150509295509295909350565b6000806000806080858703121561568957600080fd5b8435615694816154ab565b935060208501356156a4816154ab565b93969395505050506040820135916060013590565b6000806000604084860312156156ce57600080fd5b83356156d9816154ab565b9250602084013567ffffffffffffffff8111156156f557600080fd5b615701868287016155a3565b9497909650939450505050565b6000806000806040858703121561572457600080fd5b843567ffffffffffffffff8082111561573c57600080fd5b615748888389016155a3565b9096509450602087013591508082111561576157600080fd5b5061576e878288016155a3565b95989497509550505050565b600081518084526020808501945080840160005b838110156157aa5781518752958201959082019060010161578e565b509495945050505050565b602081526000612cb1602083018461577a565b80151581146118c557600080fd5b600080604083850312156157e957600080fd5b82356157f4816157c8565b91506020830135615804816154ab565b809150509250929050565b60006020828403121561582157600080fd5b5035919050565b60008060008060006080868803121561584057600080fd5b853561584b816154ab565b9450602086013561585b816154ab565b935060408601359250606086013567ffffffffffffffff8082111561587f57600080fd5b818801915088601f83011261589357600080fd5b8135818111156158a257600080fd5b8960208285010111156158b457600080fd5b9699959850939650602001949392505050565b60008060008060008060006080888a0312156158e257600080fd5b873567ffffffffffffffff808211156158fa57600080fd5b6159068b838c016155a3565b909950975060208a013591508082111561591f57600080fd5b61592b8b838c016155a3565b909750955060408a013591508082111561594457600080fd5b506159518a828b016155a3565b9094509250506060880135615965816154ab565b8091505092959891949750929550565b838152826020820152606060408201526000613b72606083018461577a565b600080604083850312156159a757600080fd5b6159b083615575565b91506020830135615804816157c8565b815161ffff168152610180810160208301516159e2602084018261ffff169052565b5060408301516159f8604084018261ffff169052565b506060830151615a0e606084018261ffff169052565b506080830151615a24608084018261ffff169052565b5060a0830151615a3b60a084018262ffffff169052565b5060c0830151615a5160c084018261ffff169052565b5060e0830151615a6860e084018262ffffff169052565b506101008381015162ffffff908116918401919091526101208085015182169084015261014080850151909116908301526101609283015164ffffffffff16929091019190915290565b60008060408385031215615ac557600080fd5b82356159b0816154ab565b803561ffff811681146136b157600080fd5b600060208284031215615af457600080fd5b612cb182615ad0565b600080600080600060a08688031215615b1557600080fd5b8535615b20816154ab565b94506020860135615b30816154ab565b9350615b3e60408701615575565b9250615b4c60608701615ad0565b949793965091946080013592915050565b60008060408385031215615b7057600080fd5b82356157f4816154ab565b60008060008060008060808789031215615b9457600080fd5b8635615b9f816154ab565b95506020870135615baf816154ab565b9450604087013567ffffffffffffffff80821115615bcc57600080fd5b615bd88a838b016155a3565b90965094506060890135915080821115615bf157600080fd5b50615bfe89828a016155a3565b979a9699509497509295939492505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008219821115615c4f57615c4f615c26565b500190565b60006001600160801b0383811690831681811015615c7457615c74615c26565b039392505050565b634e487b7160e01b600052604160045260246000fd5b600060208284031215615ca457600080fd5b8135612cb1816154ab565b600082821015615cc157615cc1615c26565b500390565b6000816000190483118215151615615ce057615ce0615c26565b500290565b634e487b7160e01b600052601260045260246000fd5b600082615d1857634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615d2f57600080fd5b5051919050565b60006001600160a01b0380891683528088166020840152508560408301526001600160801b038516606083015260a060808301528260a0830152828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b60006001600160801b03808316818516808303821115615dbc57615dbc615c26565b01949350505050565b60006001600160701b03808316818516808303821115615dbc57615dbc615c26565b600060208284031215615df957600080fd5b8151612cb1816154ab565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615e3657600080fd5b8260051b8083602087013760009401602001938452509192915050565b604081526000615e67604083018688615e04565b8281036020840152615e7a818587615e04565b979650505050505050565b600060208284031215615e9757600080fd5b8151612cb1816157c8565b60008251615eb4818460208701615516565b9190910192915050565b600060ff821660ff84168060ff03821115615edb57615edb615c26565b019392505050565b600060ff821660ff841680821015615efd57615efd615c26565b9003939250505056fea264697066735822122094cb6cc718f320a3975a3cfb38c7284372dddcb3591b2b760bf1fca6ef1d43b564736f6c634300080a00330000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101e45760003560e01c80638f919a831161010f578063bd85b039116100a2578063d3b9fbe411610071578063d3b9fbe414610684578063e985e9c51461068c578063f7cff1f81461069f578063fba0ee64146106b257600080fd5b8063bd85b03914610617578063c45a015514610637578063c7bd65861461065e578063d32db4371461067157600080fd5b8063a21635a7116100de578063a21635a7146105a3578063a22cb465146105b6578063a582cdaa146105c9578063b7d19fc41461060457600080fd5b80638f919a83146103f357806395d89b411461041a57806398c7adf314610439578063a1af5b9a1461057b57600080fd5b80631b05b83e1161018757806354b5fc871161015657806354b5fc871461036e57806355182894146103815780635cffe9de146103be578063714c8592146103d157600080fd5b80631b05b83e146102ef578063225b20b9146103285780634e1273f41461033b57806353c059a01461035b57600080fd5b80630abe9688116101c35780630abe9688146102745780630acd451d1461029c5780630febdd49146102af57806316dc165b146102c457600080fd5b8062fdd58e146101e957806301ffc9a71461020f57806306fdde0314610232575b600080fd5b6101fc6101f73660046154c0565b6106c5565b6040519081526020015b60405180910390f35b61022261021d3660046154ec565b6106ed565b6040519015158152602001610206565b60408051808201909152601481527f4c697175696469747920426f6f6b20546f6b656e00000000000000000000000060208201525b6040516102069190615542565b610287610282366004615588565b610718565b60408051928352602083019190915201610206565b6102876102aa3660046155ef565b61072d565b6102c26102bd366004615673565b6109f8565b005b6004546102d7906001600160a01b031681565b6040516001600160a01b039091168152602001610206565b6007546006546001600160881b03601882901c811692169062ffffff165b60408051938452602084019290925290820152606001610206565b6102876103363660046156b9565b610b1c565b61034e61034936600461570e565b610da9565b60405161020691906157b5565b6102876103693660046157d6565b610e9f565b6102c261037c36600461580f565b611846565b6103896118c8565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610206565b6102c26103cc366004615828565b61193d565b6103e46103df3660046158c7565b611da0565b60405161020693929190615975565b610406610401366004615994565b612ca3565b60405162ffffff9091168152602001610206565b60408051808201909152600381526213109560ea1b6020820152610267565b61056e6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810191909152506040805161018081018252600a5461ffff80821683526201000082048116602084015264010000000082048116938301939093526601000000000000810483166060830152600160401b8104831660808301526a0100000000000000000000810462ffffff90811660a0840152600160681b820490931660c0830152600160781b8104831660e0830152600160901b81048316610100830152600160a81b81048316610120830152600160c01b8104909216610140820152600160d81b90910464ffffffffff1661016082015290565b60405161020691906159c0565b610583612cb8565b604080516001600160801b03938416815292909116602083015201610206565b61030d6105b136600461580f565b612ee2565b6102c26105c4366004615ab2565b613064565b6105d1613073565b604080516001600160801b0395861681529385166020850152918416918301919091529091166060820152608001610206565b6005546102d7906001600160a01b031681565b6101fc61062536600461580f565b60009081526002602052604090205490565b6102d77f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c298281565b6102c261066c366004615ae2565b6130ab565b6102c261067f366004615afd565b6130b4565b6102c2613232565b61022261069a366004615b5d565b613318565b6102876106ad3660046156b9565b613324565b6102c26106c0366004615b7b565b613477565b6000818152602081815260408083206001600160a01b03861684529091529020545b92915050565b60006106f882613608565b806106e757506001600160e01b0319821663a22f6d6d60e01b1492915050565b6000806107248361363e565b91509150915091565b6000806001600354146107535760405163802d9bf160e01b815260040160405180910390fd5b60026003558515806107655750858414155b15610783576040516306fed5ff60e01b815260040160405180910390fd5b6007546006546001600160881b03601882901c811692169062ffffff1660005b898110156109535760006107ce8c8c848181106107c2576107c2615c10565b90506020020135613688565b905060008a8a848181106107e4576107e4615c10565b905060200201359050806000141561081b57604051636715a79f60e11b815262ffffff831660048201526024015b60405180910390fd5b6000806108278461363e565b9150915060006108488562ffffff1660009081526002602052604090205490565b9050600080888762ffffff1611610875576108648685856136b6565b9b8c019b998a900399938490039390505b888762ffffff161061089d5761088c8686856136b6565b9c8d019c9a8b90039a948590039491505b841580156108a9575083155b156108be576108be600c62ffffff89166136e0565b6000878152600b60205260409020607085901b860190556108e53062ffffff891688613765565b8662ffffff168e6001600160a01b0316336001600160a01b03167fda5e7177dface55f5e0eff7dfc67420a1db4243ddfcf0ecc84ed93e034dd8cc28585604051610939929190918252602082015260400190565b60405180910390a4505050505050508060010190506107a3565b50600680547fffffffffffffffffffffffff0000000000000000000000000000000000ffffff1663010000006001600160881b0386811691909102919091179091556007805470ffffffffffffffffffffffffffffffffff19169184169190911790556004546109cd906001600160a01b0316878761386a565b6005546109e4906001600160a01b0316878661386a565b505060016003555090969095509350505050565b83836001600160a01b0382161580610a1757506001600160a01b038116155b15610a355760405163020d233960e51b815260040160405180910390fd5b806001600160a01b0316826001600160a01b03161415610a6857604051638696f16760e01b815260040160405180910390fd5b8533610a74828261391f565b610aa45760405163548f773d60e01b81526001600160a01b03808416600483015282166024820152604401610812565b33610ab18989898961396b565b876001600160a01b0316896001600160a01b0316826001600160a01b03167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628a8a604051610b09929190918252602082015260400190565b60405180910390a4505050505050505050565b600080600160035414610b425760405163802d9bf160e01b815260040160405180910390fd5b60026003556001600160a01b0385161580610b6557506001600160a01b03851630145b15610b835760405163bdfe054560e01b815260040160405180910390fd5b6001600160a01b0385166000908152600f6020526040812080549190556001600160801b0381169250608081901c915060005b84811015610c92576000868683818110610bd257610bd2615c10565b9050602002013590506000610be789836106c5565b90508015610c88576000828152600b60209081526040808320815160808101835281546001600160701b038082168352600160701b90910416938101939093526001810154918301919091526002015460608201529080610c4a838d8787613a0b565b6001600160801b031691506001600160801b03169150610c6c838d8787613a96565b610c76828a615c3c565b9850610c828189615c3c565b97505050505b5050600101610bb6565b508215610cdc5760088054849190600090610cb79084906001600160801b0316615c54565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b8115610d255760098054839190600090610d009084906001600160801b0316615c54565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b600454610d3c906001600160a01b0316878561386a565b600554610d53906001600160a01b0316878461386a565b60408051848152602081018490526001600160a01b0388169133917f28a87b6059180e46de5fb9ab35eb043e8fe00ab45afcc7789e3934ecbbcde3ea910160405180910390a35060016003559094909350915050565b60608382808214610dd757604051639c802a7560e01b81526004810183905260248101829052604401610812565b8567ffffffffffffffff811115610df057610df0615c7c565b604051908082528060200260200182016040528015610e19578160200160208202803683370190505b50925060005b86811015610e9457610e6f888883818110610e3c57610e3c615c10565b9050602002016020810190610e519190615c92565b878784818110610e6357610e63615c10565b905060200201356106c5565b848281518110610e8157610e81615c10565b6020908102919091010152600101610e1f565b505050949350505050565b600080600160035414610ec55760405163802d9bf160e01b815260040160405180910390fd5b6002600355604080516101408101825260065462ffffff811682526001600160881b03630100000090910481166020808401919091526007549182168385015261ffff600160881b830481166060850152600160981b830481166080850152600160a81b8304811660a085015264ffffffffff600160b81b84041660c0850152600160e01b90920490911660e0830152825180840184526008546001600160801b038082168352600160801b9182900481168385015261010085019290925284518086019095526009548083168652041690830152610120810191909152600085610fe857604082015161012083015151600554610fe3926001600160a01b03909116916001600160881b03909116906001600160801b0316613af7565b611021565b602082015161010083015151600454611021926001600160a01b03909116916001600160881b03909116906001600160801b0316613af7565b9050806110415760405163256111db60e21b815260040160405180910390fd5b6040805161018081018252600a5461ffff80821683526201000082048116602084015264010000000082048116938301939093526601000000000000810483166060830152600160401b81048316608083015262ffffff6a01000000000000000000008204811660a0840152600160681b820490931660c0830152600160781b8104831660e0830152600160901b81048316610100830152600160a81b81048316610120830152600160c01b8104831661014083015264ffffffffff600160d81b90910416610160820152835190911661111b8282613b7b565b60005b845162ffffff166000908152600b6020908152604091829020825160808101845281546001600160701b038082168352600160701b909104169281019290925260018101549282019290925260029091015460608201528915801561118c575080516001600160701b031615155b806111ab57508980156111ab575060208101516001600160701b031615155b1561132c5760008060006111d7878a6000015162ffffff168f8b88613c3890949392919063ffffffff16565b9250925092506112228d6111f0578961012001516111f7565b8961010001515b828f6112188d6000015162ffffff1660009081526002602052604090205490565b8893929190613d5c565b611243898e61123086613ddd565b61123986613ddd565b8893929190613e0a565b8051611258906001600160801b031684615c3c565b6112629089615caf565b975061126e8286615c3c565b945083600b60008b6000015162ffffff16815260200190815260200160002060008201518160000160006101000a8154816001600160701b0302191690836001600160701b03160217905550602082015181600001600e6101000a8154816001600160701b0302191690836001600160701b0316021790555060408201518160010155606082015181600201559050506113288c8a600001518f86868c610100015162ffffff1687600001516001600160801b0316613ec3565b5050505b841561134d57855161134190600c908c613f31565b62ffffff168652611353565b50611359565b5061111e565b60006113c4866080015161ffff16876060015161ffff168860c0015164ffffffffff168960e0015161ffff168a6000015162ffffff1689610100015162ffffff166113b68d6000015162ffffff168b6140e790919063ffffffff16565b6011969594939291906140fe565b90508560e0015161ffff16811415806113e6575060c086015164ffffffffff16155b1561142a5761ffff811660e08701526113fe42614176565b64ffffffffff1660c087015260a086015161ffff1681141561142a5760a08601805160010161ffff1690525b83600a60008201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548161ffff021916908361ffff16021790555060408201518160000160046101000a81548161ffff021916908361ffff16021790555060608201518160000160066101000a81548161ffff021916908361ffff16021790555060808201518160000160086101000a81548161ffff021916908361ffff16021790555060a082015181600001600a6101000a81548162ffffff021916908362ffffff16021790555060c082015181600001600d6101000a81548161ffff021916908361ffff16021790555060e082015181600001600f6101000a81548162ffffff021916908362ffffff1602179055506101008201518160000160126101000a81548162ffffff021916908362ffffff1602179055506101208201518160000160156101000a81548162ffffff021916908362ffffff1602179055506101408201518160000160186101000a81548162ffffff021916908362ffffff16021790555061016082015181600001601b6101000a81548164ffffffffff021916908364ffffffffff16021790555090505085600660008201518160000160006101000a81548162ffffff021916908362ffffff16021790555060208201518160000160036101000a8154816001600160881b0302191690836001600160881b0316021790555060408201518160010160006101000a8154816001600160881b0302191690836001600160881b0316021790555060608201518160010160116101000a81548161ffff021916908361ffff16021790555060808201518160010160136101000a81548161ffff021916908361ffff16021790555060a08201518160010160156101000a81548161ffff021916908361ffff16021790555060c08201518160010160176101000a81548164ffffffffff021916908364ffffffffff16021790555060e082015181600101601c6101000a81548161ffff021916908361ffff1602179055506101008201518160020160008201518160000160006101000a8154816001600160801b0302191690836001600160801b0316021790555060208201518160000160106101000a8154816001600160801b0302191690836001600160801b0316021790555050506101208201518160030160008201518160000160006101000a8154816001600160801b0302191690836001600160801b0316021790555060208201518160000160106101000a8154816001600160801b0302191690836001600160801b0316021790555050509050508915611815576005549196508691611810906001600160a01b03168a8461386a565b611831565b6004549197508791611831906001600160a01b03168a8461386a565b50505050505060016003819055509250929050565b336001600160a01b037f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982161461188f57604051632486085360e01b815260040160405180910390fd5b600a805471ffffffffffffffffffffffffffffffffffff831671ffffffffffffffffffffffffffffffffffff1990911617905550565b50565b600080600080600080600061190b60075461ffff608882901c811692609883901c82169260a881901c83169264ffffffffff60b883901c169260e09290921c1690565b939a5091985096509450925084156119235786611926565b60005b91506119328588615cc6565b905090919293949596565b6001600354146119605760405163802d9bf160e01b815260040160405180910390fd5b60026003556004546001600160a01b03908116908516811480159061199357506005546001600160a01b03868116911614155b156119b15760405163992dfe2760e01b815260040160405180910390fd5b60006119bc856141a1565b9050600060405180604001604052806119d48461425c565b6001600160801b03168152600a5460209091019061271090611a0190600160681b900461ffff1686615cc6565b611a0b9190615cfb565b6001600160801b031690526040516370a0823160e01b81523060048201529091506000906001600160a01b038916906370a0823190602401602060405180830381865afa158015611a60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a849190615d1d565b9050611a9a6001600160a01b0389168a8961386a565b8151604051635dbacec560e01b81527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9916001600160a01b038c1691635dbacec591611af39133918e918e91908e908e90600401615d36565b6020604051808303816000875af1158015611b12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b369190615d1d565b14611b5457604051634a57011360e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611b9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbf9190615d1d565b8351909150611bd7906001600160801b031683615c3c565b8114611bf6576040516314e232bd60e01b815260040160405180910390fd5b60065462ffffff166000818152600260205260409020548515611d3157866001600160a01b03168b6001600160a01b03161415611cbd57600080611c546008546009546001600160801b038083169390821692608090811c92901c90565b5089519294509250611c859160089150611c6e9085615d9a565b60208a0151611c7d9085615d9a565b60801b169055565b611c8f8784614289565b6000858152600b602052604081206001018054909190611cb0908490615c3c565b90915550611d3192505050565b600080611ce46008546009546001600160801b038083169390821692608090811c92901c90565b8a519295509350611cfe9250600991611c6e915085615d9a565b611d088784614289565b6000858152600b602052604081206002018054909190611d29908490615c3c565b909155505050505b8451604080516001600160a01b038e81168252602082018e90526001600160801b03909316818301529051918e169133917f3659d15bd4bb92ab352a8d35bc3119ec6e7e0ab48e4d46201c8a28e02b6a8a86919081900360600190a35050600160035550505050505050505050565b6000806060600160035414611dc85760405163802d9bf160e01b815260040160405180910390fd5b6002600355881580611dda5750888714155b80611de55750888514155b15611e03576040516306fed5ff60e01b815260040160405180910390fd5b60006006604051806101400160405290816000820160009054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160039054906101000a90046001600160881b03166001600160881b03166001600160881b031681526020016001820160009054906101000a90046001600160881b03166001600160881b03166001600160881b031681526020016001820160119054906101000a900461ffff1661ffff1661ffff1681526020016001820160139054906101000a900461ffff1661ffff1661ffff1681526020016001820160159054906101000a900461ffff1661ffff1661ffff1681526020016001820160179054906101000a900464ffffffffff1664ffffffffff1664ffffffffff16815260200160018201601c9054906101000a900461ffff1661ffff1661ffff168152602001600282016040518060400160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b0316815250508152602001600382016040518060400160405290816000820160009054906101000a90046001600160801b03166001600160801b03166001600160801b031681526020016000820160109054906101000a90046001600160801b03166001600160801b03166001600160801b0316815250508152505090506000600a604051806101800160405290816000820160009054906101000a900461ffff1661ffff1661ffff1681526020016000820160029054906101000a900461ffff1661ffff1661ffff1681526020016000820160049054906101000a900461ffff1661ffff1661ffff1681526020016000820160069054906101000a900461ffff1661ffff1661ffff1681526020016000820160089054906101000a900461ffff1661ffff1661ffff16815260200160008201600a9054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600d9054906101000a900461ffff1661ffff1661ffff16815260200160008201600f9054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160129054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160159054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160189054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201601b9054906101000a900464ffffffffff1664ffffffffff1664ffffffffff16815250509050612238604051806101a00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60208301516101008401515160045461227a92612275926001600160a01b03909216916001600160881b03909116906001600160801b0316613af7565b613ddd565b6001600160701b031681526040830151610120840151516005546122c292612275926001600160a01b03909216916001600160881b03909116906001600160801b0316613af7565b6001600160701b031660208201528b67ffffffffffffffff8111156122e9576122e9615c7c565b604051908082528060200260200182016040528015612312578160200160208202803683370190505b50935060005b8c811015612a19576123358e8e838181106107c2576107c2615c10565b62ffffff1661010083018190526000908152600b6020908152604091829020825160808101845281546001600160701b03808216808452600160701b90920416938201939093526001820154938101939093526002015460608301521580156123a9575060208101516001600160701b0316155b1561242f57610100830151600881901c6000818152600e602090815260408083208054600160ff80891682901b90921790925560109690961c808552600d845291842080549690951681901b909517909355908052600c90527f13649b2456f1b42fef0f0040b3aaeabcd21a76a0f3f5defd4f583839455116e880549290911b90911790555b8c8c8381811061244157612441615c10565b905060200201358360c0018181516124599190615c3c565b9052508a8a8381811061246e5761246e615c10565b905060200201358360e0018181516124869190615c3c565b905250670de0b6b3a76400008d8d848181106124a4576124a4615c10565b90506020020135846000015102816124be576124be615ce5565b04610120840152670de0b6b3a76400008b8b848181106124e0576124e0615c10565b90506020020135846020015102816124fa576124fa615ce5565b0461014084015261010083015184516000916125199161ffff166142c2565b9050856000015162ffffff168461010001511061280d57856000015162ffffff1684610100015114156127e05781516001600160701b031615158061256a575060208201516001600160701b031615155b156127db57610100840151600090815260026020526040812054905060008060008761014001516125ac89610120015160808861430a9092919063ffffffff16565b6125b69190615c3c565b905060006125c48286615c3c565b90506125ef89610120015188600001516001600160701b03166125e79190615c3c565b8390836136b6565b935061261289610140015188602001516001600160701b03166125e79190615c3c565b9250505061262e87610100015189613b7b90919063ffffffff16565b60408051808201909152600080825260208201528288610120015111156126cf5761267361266c848a6101200151038b61439990919063ffffffff16565b8a90614405565b905080600001516001600160801b031688610120018181516126959190615caf565b90525080516080890180516001600160801b03909216916126b7908390615c3c565b9052506101008a01516126cf90879083600188613d5c565b818861014001511115612755576126f961266c838a6101400151038b61439990919063ffffffff16565b905080600001516001600160801b0316886101400181815161271b9190615caf565b905250805160a0890180516001600160801b039092169161273d908390615c3c565b9052506101208a015161275590879083600088613d5c565b60008860800151118061276c575060008860a00151115b156127d6578761010001518e6001600160a01b0316336001600160a01b03167f56f8e764728c77dd99ffbc1b64e6d02e227e6ec8214f165d4ef31351de136a0d8b608001518c60a001516040516127cd929190918252602082015260400190565b60405180910390a45b505050505b61283a565b610140840151156127db57836101000151604051632262d0a160e21b815260040161081291815260200190565b6101208401511561283a57836101000151604051632262d0a160e21b815260040161081291815260200190565b600084610140015161285d86610120015160808561430a9092919063ffffffff16565b6128679190615c3c565b9050806128905784610100015160405163b42cd28760e01b815260040161081291815260200190565b808885815181106128a3576128a3615c10565b6020908102919091010152610120850151835184906128c3908390615dc5565b6001600160701b03169052506101408501516020840180516128e6908390615dc5565b6001600160701b03908116909152610120870180516020808c0180516001600160881b0393861601831690526101408a0180516040808f0180519288169290920190941690529251828b018051909101905291516060808b0180519092019091526101008a0180516000908152600b855283902089518154958b01518716600160701b026001600160e01b0319909616961695909517939093178455908701516001840155860151600290920191909155516129a591508c9083614451565b8461010001518b6001600160a01b0316336001600160a01b03167f4216cc3bd0c40a90259d92f800c06ede5c47765f41a488072b7e7104a1f95841886101200151896101400151604051612a03929190918252602082015260400190565b60405180910390a4836001019350505050612318565b50670de0b6b3a76400008160c001511180612a3f5750670de0b6b3a76400008160e00151115b15612a5d5760405163eee8ff2760e01b815260040160405180910390fd5b82516006805460208087015162ffffff90941673ffffffffffffffffffffffffffffffffffffffff199092169190911763010000006001600160881b0394851602179091556040808601516007805460608901516080808b015160a08c015160c08d015160e08e015197909a167fffffffffffffffffffffffffff0000000000000000000000000000000000000090951694909417600160881b61ffff94851602177fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff16600160981b918416919091027fffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffff1617600160a81b93831693909302929092177fffff00000000000000ffffffffffffffffffffffffffffffffffffffffffffff16600160b81b64ffffffffff909816979097027fffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff1696909617600160e01b96909316959095029190911790556101008601518051908301516001600160801b03918216600160801b918316820217600855610120880151805194015193821693909116029190911760095590820151908201518251910190811015612c3f578151600454612c3f916001600160a01b03909116908a9084900361386a565b60008260a0015183606001510190508083602001511115612c7c576020830151600554612c7c916001600160a01b03909116908b9084900361386a565b505060408101516060909101516001600355909d909c50929a509198505050505050505050565b6000612cb1600c8484613f31565b9392505050565b600080600160035414612cde5760405163802d9bf160e01b815260040160405180910390fd5b600260038190555060007f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c29826001600160a01b031663469048406040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6a9190615de7565b9050336001600160a01b03821614612da657604051631ed01e0560e31b81526001600160a01b0382166004820152336024820152604401610812565b600080600080612dd06008546009546001600160801b038083169390821692608090811c92901c90565b93509350935093506001826001600160801b03161115612e2d57612df5600183615c54565b9650612e018785615c54565b600160801b81166008559350600454612e2d906001600160a01b0316866001600160801b038a1661386a565b6001816001600160801b03161115612e8257612e4a600182615c54565b9550612e568684615c54565b600160801b81166009559250600554612e82906001600160a01b0316866001600160801b03891661386a565b604080516001600160801b03808a168252881660208201526001600160a01b0387169133917f26b782206d6b531bf95d487110cfefdc443291f176f1977e94abcb7e67bd1b79910160405180910390a35050600160035550929391925050565b6000808080612ef18542615caf565b9050600080612f2e60075461ffff608882901c811692609883901c82169260a881901c83169264ffffffffff60b883901c169260e09290921c1690565b945050935050506000612f4f838386601161450f909392919063ffffffff16565b91995097509550905083811015613059576040805161018081018252600a5461ffff80821683526201000082048116602084015264010000000082048116938301939093526601000000000000810483166060830152600160401b81048316608083015262ffffff6a01000000000000000000008204811660a0840152600160681b820490931660c0830152600160781b8104831660e0830152600160901b81048316610100830152600160a81b81048316610120830152600160c01b8104831661014083015264ffffffffff600160d81b9091041661016082015260065490911661303b8282613b7b565b61010090910151828603918202989098019762ffffff160295909501945b505050509193909250565b61306f33838361471a565b5050565b60008060008061309d6008546009546001600160801b038083169390821692608090811c92901c90565b935093509350935090919293565b6118c5816147c5565b336001600160a01b037f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c298216146130fd57604051632486085360e01b815260040160405180910390fd5b6001600160a01b038516158061311a57506001600160a01b038416155b15613138576040516317be04b760e01b815260040160405180910390fd5b6004546001600160a01b031615613162576040516359e270a560e01b815260040160405180910390fd5b61316a61489a565b6004805473ffffffffffffffffffffffffffffffffffffffff199081166001600160a01b0388811691909117909255600580549091169186169190911790556006805462ffffff191662ffffff8516179055600780547fffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffff16600160881b61ffff851602179055600a805471ffffffffffffffffffffffffffffffffffff191671ffffffffffffffffffffffffffffffffffff831617905561322b60026147c5565b5050505050565b336001600160a01b037f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982161461327b57604051632486085360e01b815260040160405180910390fd5b600a54612710906132a390600160a81b810462ffffff1690600160401b900461ffff16615cc6565b6132ad9190615cfb565b600a80546006547fffffffffff000000000000ffffffffffffffffffffffffffffffffffffffffff909116600160a81b62ffffff948516027fffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffff16179216600160c01b02919091179055565b6000612cb1838361391f565b6000806001600160a01b03851630148061334557506001600160a01b038516155b156133555750600090508061346f565b6001600160a01b0385166000908152600f60205260409020546001600160801b0381169250608081901c91506000805b8581101561346b5760008787838181106133a1576133a1615c10565b9050602002013590508083101580156133b957508115155b156133d757604051630e15c23b60e31b815260040160405180910390fd5b60006133e38a836106c5565b90508015613460576000828152600b60209081526040808320815160808101835281546001600160701b038082168352600160701b90910416938101939093526001810154918301919091526002015460608201529080613446838e8787613a0b565b6001600160801b039182169b909b019a1698909801975050505b509150600101613385565b5050505b935093915050565b82818082146134a357604051639c802a7560e01b81526004810183905260248101829052604401610812565b87876001600160a01b03821615806134c257506001600160a01b038116155b156134e05760405163020d233960e51b815260040160405180910390fd5b806001600160a01b0316826001600160a01b0316141561351357604051638696f16760e01b815260040160405180910390fd5b893361351f828261391f565b61354f5760405163548f773d60e01b81526001600160a01b03808416600483015282166024820152604401610812565b60005b8981101561359e576135968d8d8d8d8581811061357157613571615c10565b905060200201358c8c8681811061358a5761358a615c10565b9050602002013561396b565b600101613552565b508a6001600160a01b03168c6001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8d8d8d8d6040516135f29493929190615e53565b60405180910390a4505050505050505050505050565b60006001600160e01b031982166337eb4f6360e21b14806106e757506001600160e01b031982166301ffc9a760e01b1492915050565b6000818152600b60205260409020546001600160701b0381811691607081901c9161366884613ddd565b61367184613ddd565b6001600160701b0391821697911695509350505050565b8062ffffff811681146136b157604051631da527cf60e21b815260048101839052602401610812565b919050565b60008060006136c586866148a4565b915091506136d686868685856148c3565b9695505050505050565b600881901c600081815260028401602052604090208054600160ff85161b6000191816908190558061375f5750601082901c600081815260018581016020526040909120805460ff85169290921b6000191890911690819055908161322b5760008080526020869052604090208054600190921b600019189190911690555b50505050565b6001600160a01b03831661378c57604051631306fc5960e11b815260040160405180910390fd5b6000828152602081815260408083206001600160a01b0387168452909152902054818110156137e657604051625b387760e31b81526001600160a01b03851660048201526024810184905260448101839052606401610812565b6137f38460008585614976565b6000838152602081815260408083206001600160a01b0388168085529083528184208686039055868452600283528184208054879003905581518781529283018690529133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a450505050565b801561391a57604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b17905260006138d88583614a78565b9050600081511180156138fc5750808060200190518101906138fa9190615e85565b155b1561322b5760405163197138bd60e11b815260040160405180910390fd5b505050565b6000816001600160a01b0316836001600160a01b03161480612cb15750506001600160a01b03918216600090815260016020908152604080832093909416825291909152205460ff1690565b6000828152602081815260408083206001600160a01b0388168452909152902054818110156139c6576040516358b4690f60e01b81526001600160a01b03861660048201526024810184905260448101839052606401610812565b6139d285858585614976565b6000928352602083815260408085206001600160a01b039788168652909152808420918390039091559290931681522080549091019055565b6001600160a01b038316600090815260106020908152604080832085845282528083208151808301835281548082526001909201549381019390935290870151839291613a6d91613a5e9087608061430a565b613a689190615caf565b61425c565b60208201516060890151919450613a8a91613a5e9087608061430a565b91505094509492505050565b6040840151600090613aaa9083608061430a565b6060860151909150600090613ac19084608061430a565b6001600160a01b039590951660009081526010602090815260408083209683529590529390932090815560010192909255505050565b6040516370a0823160e01b81523060048201526000908284019081906001600160a01b038716906370a0823190602401602060405180830381865afa158015613b44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b689190615d1d565b613b729190615caf565b95945050505050565b600082610160015164ffffffffff1642613b959190615caf565b9050826040015161ffff1681101580613bb8575061016083015164ffffffffff16155b15613c185762ffffff8216610140840152606083015161ffff16811015613c0f5761271083610100015162ffffff16846080015161ffff160281613bfe57613bfe615ce5565b0462ffffff16610120840152613c18565b60006101208401525b613c2142614176565b64ffffffffff1661016084015261391a8383614b43565b604080518082019091526000808252602082018190529081906000613c6587896000015161ffff166142c2565b90506000808715613c935760208b01516001600160701b03169150613c8c82608085614bb5565b9050613caf565b8a516001600160701b03169150613cac83836080614be7565b90505b613cb98a8a614b43565b613ccd613cc68b83614c19565b8b90614405565b80519094508790613ce7906001600160801b031683615c3c565b11613cf757809550819450613d4e565b613d04613cc68b89614c4b565b8051909450613d1c906001600160801b031688615caf565b955087613d3457613d2f86608085614c74565b613d40565b613d408387608061430a565b945081851115613d4e578194505b505050955095509592505050565b825184518590613d6d908390615d9a565b6001600160801b0390811690915260208086015190870180519091019091169052508115613db857613d9f8382614289565b85604001818151613db09190615c3c565b90525061322b565b613dc28382614289565b85606001818151613dd39190615c3c565b9052505050505050565b806001600160701b03811681146136b157604051631ce5dcf760e01b815260048101839052602401610812565b8215613e69578185600001818151613e229190615dc5565b6001600160701b03908116909152602087810180518590038316905286018051858316016001600160881b039081169091526040870180519285169092031690525061322b565b8185602001818151613e7b9190615dc5565b6001600160701b039081169091528651839003811687526020860180516001600160881b03858416909103811690915260408701805192861692909201169052505050505050565b60408051861515815260208101869052908101849052606081018390526080810182905262ffffff8716906001600160a01b0389169033907fc528cda9e500228b16ce84fadae290d9a49aecb17483110004c5af0a07f6fd739060a00160405180910390a450505050505050565b61ffff600883901c1691600090819060ff16838015613f4f57508015155b80613f64575083158015613f6457508060ff14155b15613faf5762ffffff851660009081526002870160205260409020549150613f8d828286614cb7565b90506000198114613faf5763ffffff00600886901b1681015b92505050612cb1565b5061ffff600885901c169360ff16838015613fc957508015155b80613fde575083158015613fde57508060ff14155b1561405b5762ffffff851660009081526001870160205260409020549150614007828286614cb7565b9050600019811461405b5762ffffff60089590951b63ffffff0016810194851660009081526002870160205260409020549150613fa6856140488487614ce6565b60ff1660089190911b63ffffff00160190565b6000808052602087905260409020549150614077828686614cb7565b905060001981141561409c576040516310d6486160e01b815260040160405180910390fd5b600081815260018701602052604090205491506140bd816140488487614ce6565b945085600262ffffff871660009081529101602052604090205491506136d6856140488487614ce6565b60008183116140f857828203612cb1565b50900390565b6000806141248585858d8a61ffff811061411a5761411a615c10565b0154929190614d04565b9050876141318842615caf565b1015801561413e57508615155b1561414f5788600187089150614153565b8591505b808a8361ffff811061416757614167615c10565b01555098975050505050505050565b8064ffffffffff811681146136b1576040516302dfd99760e01b815260048101839052602401610812565b6000807f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c29826001600160a01b0316634847cdc86040518163ffffffff1660e01b8152600401602060405180830381865afa158015614202573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142269190615d1d565b9050670de0b6b3a764000060018161423e8487615cc6565b6142489190615c3c565b6142529190615caf565b612cb19190615cfb565b806001600160801b03811681146136b15760405163089f6cfb60e21b815260048101839052602401610812565b600081608084602001516001600160801b031685600001516001600160801b031603901b816142ba576142ba615ce5565b049392505050565b600062ffffff8311156142e85760405163163d8bab60e21b815260040160405180910390fd5b627fffff198301614302816142fc85614dca565b90614e10565b949350505050565b600060ff82111561433157604051630b72ecf560e41b815260048101839052602401610812565b60008061433e86866148a4565b91509150816000146143505781841c92505b8015614390576001841b811061438357604051633d90990f60e01b81526004810182905260248101859052604401610812565b836101000381901b830192505b50509392505050565b6000806143a584615069565b905060006143bb670de0b6b3a764000080615cc6565b9050806001816143d3670de0b6b3a764000086615c3c565b6143dd8689615cc6565b6143e79190615cc6565b6143f19190615c3c565b6143fb9190615caf565b613b729190615cfb565b60408051808201909152600080825260208201526144228261425c565b6001600160801b0316815260c08301516127109061ffff168302046001600160801b0316602082015292915050565b6001600160a01b03831661447857604051632a97acbf60e11b815260040160405180910390fd5b6144856000848484614976565b600082815260026020526040812080548392906144a3908490615c3c565b90915550506000828152602081815260408083206001600160a01b0387168085529083528184208054860190558151868152928301859052929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a4505050565b6000808080866145325760405163c686b52f60e01b815260040160405180910390fd5b6000876001880890506000898261ffff811061455057614550615c10565b0154905061455d81615092565b95508686111561458a576040516340a02e1d60e01b81526004810187905260248101889052604401610812565b886001146146e057898861ffff81106145a5576145a5615c10565b015490506145b281615092565b9550868611156146e05760006145ca8b8a8a8d6150a5565b90925090508181146146de576000886145e283615092565b6145ec9190615caf565b905060006145f984615092565b614603908b615caf565b905060006146118284615c3c565b9050808261461e86615182565b6146289190615cc6565b8461463288615182565b61463c9190615cc6565b6146469190615c3c565b6146509190615cfb565b9850808261465d86615198565b6146679190615cc6565b8461467188615198565b61467b9190615cc6565b6146859190615c3c565b61468f9190615cfb565b9750808261469c866151ae565b6146a69190615cc6565b846146b0886151ae565b6146ba9190615cc6565b6146c49190615c3c565b6146ce9190615cfb565b96508a995050505050505061470f565b505b6146e981615092565b95506146f481615182565b94506146ff81615198565b935061470a816151ae565b925050505b945094509450949050565b816001600160a01b0316836001600160a01b031614156147585760405163782ee70760e01b81526001600160a01b0384166004820152602401610812565b6001600160a01b03838116600081815260016020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b60075461ffff600160981b9091048116908216811061480457604051635762a96b60e11b815261ffff8316600482015260248101829052604401610812565b600780547fffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffff16600160981b61ffff851602179055805b8261ffff16811015614859576148516011826151ba565b60010161483a565b506040805182815261ffff841660208201527f525a4241308ea122822834c841f67b00d5efc977ad9118724750f974f7f6531c910160405180910390a15050565b6148a26151db565b565b6000806000198385098385029250828110838203039150509250929050565b6000816148e1578383816148d9576148d9615ce5565b049050613b72565b83821061490b5760405163656b542b60e11b81526004810183905260248101859052604401610812565b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b826001600160a01b0316846001600160a01b03161461375f576000828152600b6020908152604091829020825160808101845281546001600160701b038082168352600160701b909104169281019290925260018101549282019290925260029091015460608201526001600160a01b03851615801590614a0057506001600160a01b0385163014155b15614a2b576000614a1186856106c5565b9050614a2982878684614a248882615caf565b615203565b505b6001600160a01b03841615801590614a4c57506001600160a01b0384163014155b1561322b576000614a5d85856106c5565b9050614a7082868684614a248882615c3c565b505050505050565b6060600080846001600160a01b031684604051614a959190615ea2565b6000604051808303816000865af19150503d8060008114614ad2576040519150601f19603f3d011682016040523d82523d6000602084013e614ad7565b606091505b50915091508115614b1c578051158015614af957506001600160a01b0385163b155b15614b1757604051635d97df8960e01b815260040160405180910390fd5b614302565b8051614b3b57604051632407429160e01b815260040160405180910390fd5b805181602001fd5b600082610120015162ffffff16612710614b7085610140015162ffffff16856140e790919063ffffffff16565b614b7a9190615cc6565b614b849190615c3c565b90508260e0015162ffffff168111614b9c5780614ba2565b8260e001515b62ffffff16610100909301929092525050565b6000614bc2848484614c74565b90508180614bd257614bd2615ce5565b6001841b850915612cb1576001019392505050565b6000614bf484848461430a565b90506001821b80614c0757614c07615ce5565b83850915612cb1576001019392505050565b600080614c2584615069565b90506000614c3b82670de0b6b3a7640000615caf565b9050806001816143e78588615cc6565b6000670de0b6b3a76400006001670de0b6b3a7640000614c6a86615069565b61423e9086615cc6565b600060ff831115614c9b57604051630b72ecf560e41b815260048101849052602401610812565b83831b61010084900385901c6136d6866001871b8685856148c3565b600081614cd257614b1784614ccd856001615ebe565b6152b1565b61430284614ce1600186615ee3565b6152df565b600081614cfb57614cf683615310565b612cb1565b612cb1836153a6565b600080614d1086615092565b614d1a9042615caf565b90506000818602614d2a88615182565b0190506000828602614d3b89615198565b0190506000838602614d4c8a6151ae565b0190506501fffffffffe42600190811b919091166d01fffffffffffffffe0000000000602986901b167501fffffffffffffffe00000000000000000000000000606986901b167ffffffffffffffffffffffe00000000000000000000000000000000000000000060a986901b16171717179998505050505050505050565b6000811580614dda575061271082115b15614dfb576040516374da1e1160e11b815260048101839052602401610812565b612710608083901b04600160801b0192915050565b6000808083614e285750600160801b91506106e79050565b50826000811215614e3a579015906000035b6210000081101561502a57600160801b9250846001600160801b03811115614e6457911591600019045b6001821615614e755792830260801c925b800260801c6002821615614e8b5792830260801c925b800260801c6004821615614ea15792830260801c925b800260801c6008821615614eb75792830260801c925b800260801c6010821615614ecd5792830260801c925b800260801c6020821615614ee35792830260801c925b800260801c6040821615614ef95792830260801c925b8002608090811c90821615614f105792830260801c925b800260801c610100821615614f275792830260801c925b800260801c610200821615614f3e5792830260801c925b800260801c610400821615614f555792830260801c925b800260801c610800821615614f6c5792830260801c925b800260801c611000821615614f835792830260801c925b800260801c612000821615614f9a5792830260801c925b800260801c614000821615614fb15792830260801c925b800260801c618000821615614fc85792830260801c925b800260801c62010000821615614fe05792830260801c925b800260801c62020000821615614ff85792830260801c925b800260801c620400008216156150105792830260801c925b800260801c620800008216156150285792830260801c925b505b8261505257604051630e9c7d6160e31b81526004810186905260248101859052604401610812565b8161505d5782613b72565b613b7283600019615cfb565b600061507482615435565b8251602084015161ffff9182169116026402540be400020192915050565b6000600182901c64ffffffffff166106e7565b600080600183828080805b85851061511d5785850160011c9350888b850892508b8361ffff81106150d8576150d8615c10565b015491506150e582615092565b9050898110156150fa578360010195506150b0565b8981111561510d576001840394506150b0565b5095508594506151799350505050565b8981101561514c5788600184089250818c8461ffff811061514057615140615c10565b01549098509650615172565b8b615157848b615481565b61ffff811061516857615168615c10565b0154975090955085905b5050505050505b94509492505050565b6000602982901c67ffffffffffffffff166106e7565b6000606982901c67ffffffffffffffff166106e7565b600060a982901c6106e7565b6001828261ffff81106151cf576151cf615c10565b01805490911790555050565b600354156151fc5760405163d80075c160e01b815260040160405180910390fd5b6001600355565b6001600160a01b0384166000908152600f6020526040812054906001600160801b03821690608083901c908061523b8a8a8a8a613a0b565b9150915061524b8a8a8a89613a96565b6152558285615d9a565b93506152618184615d9a565b6001600160a01b03999099166000908152600f602052604090206001600160801b039490941660809990991b6fffffffffffffffffffffffffffffffff1916989098179092555050505050505050565b60ff81169190911c90600082156152d557816152cc84615310565b0160ff16612cb1565b5060001992915050565b60ff8181031691821b91600090831561530557806152fc856153a6565b60ff1603614302565b506000199392505050565b6000608082901b156153245750608090811b905b604082901b1561533657604091821b91015b602082901b1561534857602091821b91015b601082901b1561535a57601091821b91015b600882901b1561536c57600891821b91015b600482901b1561537e57600491821b91015b600282901b1561539057600291821b91015b600182901b1561539e576001015b60ff03919050565b6000600160801b82106153bb5750608090811c905b600160401b82106153ce57604091821c91015b64010000000082106153e257602091821c91015b6201000082106153f457601091821c91015b610100821061540557600891821c91015b6010821061541557600491821c91015b6004821061542557600291821c91015b600282106136b157600101919050565b60008160a0015162ffffff166000146136b1576000826000015161ffff1683610100015162ffffff1602905060648360a0015162ffffff1682830202606301816142ba576142ba615ce5565b600081156106e75782801561549d5782600185030691506154a4565b6001830391505b5092915050565b6001600160a01b03811681146118c557600080fd5b600080604083850312156154d357600080fd5b82356154de816154ab565b946020939093013593505050565b6000602082840312156154fe57600080fd5b81356001600160e01b031981168114612cb157600080fd5b60005b83811015615531578181015183820152602001615519565b8381111561375f5750506000910152565b6020815260008251806020840152615561816040850160208701615516565b601f01601f19169190910160400192915050565b803562ffffff811681146136b157600080fd5b60006020828403121561559a57600080fd5b612cb182615575565b60008083601f8401126155b557600080fd5b50813567ffffffffffffffff8111156155cd57600080fd5b6020830191508360208260051b85010111156155e857600080fd5b9250929050565b60008060008060006060868803121561560757600080fd5b853567ffffffffffffffff8082111561561f57600080fd5b61562b89838a016155a3565b9097509550602088013591508082111561564457600080fd5b50615651888289016155a3565b9094509250506040860135615665816154ab565b809150509295509295909350565b6000806000806080858703121561568957600080fd5b8435615694816154ab565b935060208501356156a4816154ab565b93969395505050506040820135916060013590565b6000806000604084860312156156ce57600080fd5b83356156d9816154ab565b9250602084013567ffffffffffffffff8111156156f557600080fd5b615701868287016155a3565b9497909650939450505050565b6000806000806040858703121561572457600080fd5b843567ffffffffffffffff8082111561573c57600080fd5b615748888389016155a3565b9096509450602087013591508082111561576157600080fd5b5061576e878288016155a3565b95989497509550505050565b600081518084526020808501945080840160005b838110156157aa5781518752958201959082019060010161578e565b509495945050505050565b602081526000612cb1602083018461577a565b80151581146118c557600080fd5b600080604083850312156157e957600080fd5b82356157f4816157c8565b91506020830135615804816154ab565b809150509250929050565b60006020828403121561582157600080fd5b5035919050565b60008060008060006080868803121561584057600080fd5b853561584b816154ab565b9450602086013561585b816154ab565b935060408601359250606086013567ffffffffffffffff8082111561587f57600080fd5b818801915088601f83011261589357600080fd5b8135818111156158a257600080fd5b8960208285010111156158b457600080fd5b9699959850939650602001949392505050565b60008060008060008060006080888a0312156158e257600080fd5b873567ffffffffffffffff808211156158fa57600080fd5b6159068b838c016155a3565b909950975060208a013591508082111561591f57600080fd5b61592b8b838c016155a3565b909750955060408a013591508082111561594457600080fd5b506159518a828b016155a3565b9094509250506060880135615965816154ab565b8091505092959891949750929550565b838152826020820152606060408201526000613b72606083018461577a565b600080604083850312156159a757600080fd5b6159b083615575565b91506020830135615804816157c8565b815161ffff168152610180810160208301516159e2602084018261ffff169052565b5060408301516159f8604084018261ffff169052565b506060830151615a0e606084018261ffff169052565b506080830151615a24608084018261ffff169052565b5060a0830151615a3b60a084018262ffffff169052565b5060c0830151615a5160c084018261ffff169052565b5060e0830151615a6860e084018262ffffff169052565b506101008381015162ffffff908116918401919091526101208085015182169084015261014080850151909116908301526101609283015164ffffffffff16929091019190915290565b60008060408385031215615ac557600080fd5b82356159b0816154ab565b803561ffff811681146136b157600080fd5b600060208284031215615af457600080fd5b612cb182615ad0565b600080600080600060a08688031215615b1557600080fd5b8535615b20816154ab565b94506020860135615b30816154ab565b9350615b3e60408701615575565b9250615b4c60608701615ad0565b949793965091946080013592915050565b60008060408385031215615b7057600080fd5b82356157f4816154ab565b60008060008060008060808789031215615b9457600080fd5b8635615b9f816154ab565b95506020870135615baf816154ab565b9450604087013567ffffffffffffffff80821115615bcc57600080fd5b615bd88a838b016155a3565b90965094506060890135915080821115615bf157600080fd5b50615bfe89828a016155a3565b979a9699509497509295939492505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008219821115615c4f57615c4f615c26565b500190565b60006001600160801b0383811690831681811015615c7457615c74615c26565b039392505050565b634e487b7160e01b600052604160045260246000fd5b600060208284031215615ca457600080fd5b8135612cb1816154ab565b600082821015615cc157615cc1615c26565b500390565b6000816000190483118215151615615ce057615ce0615c26565b500290565b634e487b7160e01b600052601260045260246000fd5b600082615d1857634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615d2f57600080fd5b5051919050565b60006001600160a01b0380891683528088166020840152508560408301526001600160801b038516606083015260a060808301528260a0830152828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b60006001600160801b03808316818516808303821115615dbc57615dbc615c26565b01949350505050565b60006001600160701b03808316818516808303821115615dbc57615dbc615c26565b600060208284031215615df957600080fd5b8151612cb1816154ab565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615e3657600080fd5b8260051b8083602087013760009401602001938452509192915050565b604081526000615e67604083018688615e04565b8281036020840152615e7a818587615e04565b979650505050505050565b600060208284031215615e9757600080fd5b8151612cb1816157c8565b60008251615eb4818460208701615516565b9190910192915050565b600060ff821660ff84168060ff03821115615edb57615edb615c26565b019392505050565b600060ff821660ff841680821015615efd57615efd615c26565b9003939250505056fea264697066735822122094cb6cc718f320a3975a3cfb38c7284372dddcb3591b2b760bf1fca6ef1d43b564736f6c634300080a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982
-----Decoded View---------------
Arg [0] : _factory (address): 0x1886D09C9Ade0c5DB822D85D21678Db67B6c2982
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982
Loading...
Loading
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.