Latest 25 from a total of 296,545 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Remove Liquidity... | 395873424 | 7 days ago | IN | 0 ETH | 0.00000703 | ||||
| Remove Liquidity... | 383190008 | 44 days ago | IN | 0 ETH | 0.00000752 | ||||
| Remove Liquidity... | 381416112 | 49 days ago | IN | 0 ETH | 0.00000187 | ||||
| Remove Liquidity... | 380359580 | 52 days ago | IN | 0 ETH | 0.00002817 | ||||
| Remove Liquidity... | 380059326 | 53 days ago | IN | 0 ETH | 0.00001718 | ||||
| Remove Liquidity... | 380042176 | 53 days ago | IN | 0 ETH | 0.00001707 | ||||
| Remove Liquidity... | 379379167 | 55 days ago | IN | 0 ETH | 0.00000705 | ||||
| Remove Liquidity... | 378693287 | 57 days ago | IN | 0 ETH | 0.00000706 | ||||
| Remove Liquidity... | 378633049 | 57 days ago | IN | 0 ETH | 0.00000705 | ||||
| Remove Liquidity... | 378433837 | 58 days ago | IN | 0 ETH | 0.00000698 | ||||
| Remove Liquidity... | 378364862 | 58 days ago | IN | 0 ETH | 0.00000717 | ||||
| Remove Liquidity | 376900273 | 62 days ago | IN | 0 ETH | 0.00001645 | ||||
| Remove Liquidity... | 376740861 | 63 days ago | IN | 0 ETH | 0.00000701 | ||||
| Remove Liquidity | 374195213 | 70 days ago | IN | 0 ETH | 0.00000683 | ||||
| Remove Liquidity | 373983729 | 71 days ago | IN | 0 ETH | 0.00003446 | ||||
| Remove Liquidity... | 373158640 | 73 days ago | IN | 0 ETH | 0.00000677 | ||||
| Remove Liquidity... | 373118155 | 73 days ago | IN | 0 ETH | 0.00001816 | ||||
| Remove Liquidity... | 371878842 | 77 days ago | IN | 0 ETH | 0.00000912 | ||||
| Remove Liquidity... | 371873274 | 77 days ago | IN | 0 ETH | 0.00002443 | ||||
| Remove Liquidity... | 371304883 | 78 days ago | IN | 0 ETH | 0.00006456 | ||||
| Remove Liquidity... | 370316333 | 81 days ago | IN | 0 ETH | 0.00000699 | ||||
| Remove Liquidity... | 370018698 | 82 days ago | IN | 0 ETH | 0.00000699 | ||||
| Remove Liquidity... | 367909546 | 88 days ago | IN | 0 ETH | 0.00002759 | ||||
| Remove Liquidity... | 367909108 | 88 days ago | IN | 0 ETH | 0.00000202 | ||||
| Remove Liquidity... | 367347560 | 90 days ago | IN | 0 ETH | 0.00000863 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 366947715 | 91 days ago | 0.00000064 ETH | ||||
| 366947715 | 91 days ago | 0.00000064 ETH | ||||
| 333761823 | 187 days ago | 0.00703986 ETH | ||||
| 333761823 | 187 days ago | 0.00703986 ETH | ||||
| 248078331 | 437 days ago | 158.27352849 ETH | ||||
| 248078331 | 437 days ago | 158.27352849 ETH | ||||
| 243622572 | 450 days ago | 0.00701756 ETH | ||||
| 243622572 | 450 days ago | 0.00701756 ETH | ||||
| 239924827 | 460 days ago | 0 ETH | ||||
| 239924827 | 460 days ago | 0 ETH | ||||
| 229004052 | 492 days ago | 0 ETH | ||||
| 229004052 | 492 days ago | 0 ETH | ||||
| 214381788 | 534 days ago | 0 ETH | ||||
| 214381788 | 534 days ago | 0 ETH | ||||
| 196943670 | 586 days ago | 0.00000088 ETH | ||||
| 196943670 | 586 days ago | 0.00000088 ETH | ||||
| 192677555 | 598 days ago | 0.00127952 ETH | ||||
| 192677555 | 598 days ago | 0.00127952 ETH | ||||
| 192031155 | 600 days ago | 0.01505862 ETH | ||||
| 192031155 | 600 days ago | 0.01505862 ETH | ||||
| 191809240 | 601 days ago | 0.00000006 ETH | ||||
| 191809240 | 601 days ago | 0.00000006 ETH | ||||
| 191595107 | 601 days ago | 0.01749364 ETH | ||||
| 191595107 | 601 days ago | 0.01749364 ETH | ||||
| 185721778 | 619 days ago | 0.09332618 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LBRouter
Compiler Version
v0.8.10+commit.fc410830
Optimization Enabled:
Yes with 800 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "openzeppelin/token/ERC20/IERC20.sol";
import "./LBErrors.sol";
import "./libraries/BinHelper.sol";
import "./libraries/Constants.sol";
import "./libraries/FeeHelper.sol";
import "./libraries/JoeLibrary.sol";
import "./libraries/Math512Bits.sol";
import "./libraries/SwapHelper.sol";
import "./libraries/TokenHelper.sol";
import "./interfaces/IJoePair.sol";
import "./interfaces/ILBToken.sol";
import "./interfaces/ILBRouter.sol";
/// @title Liquidity Book Router
/// @author Trader Joe
/// @notice Main contract to interact with to swap and manage liquidity on Joe V2 exchange.
contract LBRouter is ILBRouter {
using TokenHelper for IERC20;
using TokenHelper for IWAVAX;
using FeeHelper for FeeHelper.FeeParameters;
using Math512Bits for uint256;
using SwapHelper for ILBPair.Bin;
using JoeLibrary for uint256;
ILBFactory public immutable override factory;
IJoeFactory public immutable override oldFactory;
IWAVAX public immutable override wavax;
modifier onlyFactoryOwner() {
if (msg.sender != factory.owner()) revert LBRouter__NotFactoryOwner();
_;
}
modifier ensure(uint256 _deadline) {
if (block.timestamp > _deadline) revert LBRouter__DeadlineExceeded(_deadline, block.timestamp);
_;
}
modifier verifyInputs(uint256[] memory _pairBinSteps, IERC20[] memory _tokenPath) {
if (_pairBinSteps.length == 0 || _pairBinSteps.length + 1 != _tokenPath.length)
revert LBRouter__LengthsMismatch();
_;
}
/// @notice Constructor
/// @param _factory LBFactory address
/// @param _oldFactory Address of old factory (Joe V1)
/// @param _wavax Address of WAVAX
constructor(
ILBFactory _factory,
IJoeFactory _oldFactory,
IWAVAX _wavax
) {
factory = _factory;
oldFactory = _oldFactory;
wavax = _wavax;
}
/// @dev Receive function that only accept AVAX from the WAVAX contract
receive() external payable {
if (msg.sender != address(wavax)) revert LBRouter__SenderIsNotWAVAX();
}
/// @notice Returns the approximate id corresponding to the inputted price.
/// Warning, the returned id may be inaccurate close to the start price of a bin
/// @param _LBPair The address of the LBPair
/// @param _price The price of y per x (multiplied by 1e36)
/// @return The id corresponding to this price
function getIdFromPrice(ILBPair _LBPair, uint256 _price) external view override returns (uint24) {
return BinHelper.getIdFromPrice(_price, _LBPair.feeParameters().binStep);
}
/// @notice Returns the price corresponding to the inputted id
/// @param _LBPair The address of the LBPair
/// @param _id The id
/// @return The price corresponding to this id
function getPriceFromId(ILBPair _LBPair, uint24 _id) external view override returns (uint256) {
return BinHelper.getPriceFromId(_id, _LBPair.feeParameters().binStep);
}
/// @notice Simulate a swap in
/// @param _LBPair The address of the LBPair
/// @param _amountOut The amount of token to receive
/// @param _swapForY Whether you swap X for Y (true), or Y for X (false)
/// @return amountIn The amount of token to send in order to receive _amountOut token
/// @return feesIn The amount of fees paid in token sent
function getSwapIn(
ILBPair _LBPair,
uint256 _amountOut,
bool _swapForY
) public view override returns (uint256 amountIn, uint256 feesIn) {
(uint256 _pairReserveX, uint256 _pairReserveY, uint256 _activeId) = _LBPair.getReservesAndId();
if (_amountOut == 0 || (_swapForY ? _amountOut > _pairReserveY : _amountOut > _pairReserveX))
revert LBRouter__WrongAmounts(_amountOut, _swapForY ? _pairReserveY : _pairReserveX); // If this is wrong, then we're sure the amounts sent are wrong
FeeHelper.FeeParameters memory _fp = _LBPair.feeParameters();
_fp.updateVariableFeeParameters(_activeId);
uint256 _amountOutOfBin;
uint256 _amountInWithFees;
uint256 _reserve;
// Performs the actual swap, bin per bin
// It uses the findFirstNonEmptyBinId function to make sure the bin we're currently looking at
// has liquidity in it.
while (true) {
{
(uint256 _reserveX, uint256 _reserveY) = _LBPair.getBin(uint24(_activeId));
_reserve = _swapForY ? _reserveY : _reserveX;
}
uint256 _price = BinHelper.getPriceFromId(_activeId, _fp.binStep);
if (_reserve != 0) {
_amountOutOfBin = _amountOut >= _reserve ? _reserve : _amountOut;
uint256 _amountInToBin = _swapForY
? _amountOutOfBin.shiftDivRoundUp(Constants.SCALE_OFFSET, _price)
: _price.mulShiftRoundUp(_amountOutOfBin, Constants.SCALE_OFFSET);
// We update the fee, but we don't store the new volatility reference, volatility accumulated and indexRef to not penalize traders
_fp.updateVolatilityAccumulated(_activeId);
uint256 _fee = _fp.getFeeAmount(_amountInToBin);
_amountInWithFees = _amountInToBin + _fee;
if (_amountInWithFees + _reserve > type(uint112).max) revert LBRouter__SwapOverflows(_activeId);
amountIn += _amountInWithFees;
feesIn += _fee;
_amountOut -= _amountOutOfBin;
}
if (_amountOut != 0) {
_activeId = _LBPair.findFirstNonEmptyBinId(uint24(_activeId), _swapForY);
} else {
break;
}
}
if (_amountOut != 0) revert LBRouter__BrokenSwapSafetyCheck(); // Safety check, but should never be false as it would have reverted on transfer
}
/// @notice Simulate a swap out
/// @param _LBPair The address of the LBPair
/// @param _amountIn The amount of token sent
/// @param _swapForY Whether you swap X for Y (true), or Y for X (false)
/// @return amountOut The amount of token received if _amountIn tokenX are sent
/// @return feesIn The amount of fees paid in token sent
function getSwapOut(
ILBPair _LBPair,
uint256 _amountIn,
bool _swapForY
) external view override returns (uint256 amountOut, uint256 feesIn) {
(, , uint256 _activeId) = _LBPair.getReservesAndId();
FeeHelper.FeeParameters memory _fp = _LBPair.feeParameters();
_fp.updateVariableFeeParameters(_activeId);
ILBPair.Bin memory _bin;
// Performs the actual swap, bin per bin
// It uses the findFirstNonEmptyBinId function to make sure the bin we're currently looking at
// has liquidity in it.
while (true) {
{
(uint256 _reserveX, uint256 _reserveY) = _LBPair.getBin(uint24(_activeId));
_bin = ILBPair.Bin(uint112(_reserveX), uint112(_reserveY), 0, 0);
}
if (_bin.reserveX != 0 || _bin.reserveY != 0) {
(uint256 _amountInToBin, uint256 _amountOutOfBin, FeeHelper.FeesDistribution memory _fees) = _bin
.getAmounts(_fp, _activeId, _swapForY, _amountIn);
if (_amountInToBin > type(uint112).max) revert LBRouter__BinReserveOverflows(_activeId);
_amountIn -= _amountInToBin + _fees.total;
feesIn += _fees.total;
amountOut += _amountOutOfBin;
}
if (_amountIn != 0) {
_activeId = _LBPair.findFirstNonEmptyBinId(uint24(_activeId), _swapForY);
} else {
break;
}
}
if (_amountIn != 0) revert LBRouter__TooMuchTokensIn(_amountIn);
}
/// @notice Create a liquidity bin LBPair for _tokenX and _tokenY using the factory
/// @param _tokenX The address of the first token
/// @param _tokenY The address of the second token
/// @param _activeId The active id of the pair
/// @param _binStep The bin step in basis point, used to calculate log(1 + binStep)
/// @return pair The address of the newly created LBPair
function createLBPair(
IERC20 _tokenX,
IERC20 _tokenY,
uint24 _activeId,
uint16 _binStep
) external override returns (ILBPair pair) {
pair = factory.createLBPair(_tokenX, _tokenY, _activeId, _binStep);
}
/// @notice Add liquidity while performing safety checks
/// @dev This function is compliant with fee on transfer tokens
/// @param _liquidityParameters The liquidity parameters
/// @return depositIds Bin ids where the liquidity was actually deposited
/// @return liquidityMinted Amounts of LBToken minted for each bin
function addLiquidity(LiquidityParameters calldata _liquidityParameters)
external
override
returns (uint256[] memory depositIds, uint256[] memory liquidityMinted)
{
ILBPair _LBPair = _getLBPairInformation(
_liquidityParameters.tokenX,
_liquidityParameters.tokenY,
_liquidityParameters.binStep
);
if (_liquidityParameters.tokenX != _LBPair.tokenX()) revert LBRouter__WrongTokenOrder();
_liquidityParameters.tokenX.safeTransferFrom(msg.sender, address(_LBPair), _liquidityParameters.amountX);
_liquidityParameters.tokenY.safeTransferFrom(msg.sender, address(_LBPair), _liquidityParameters.amountY);
(depositIds, liquidityMinted) = _addLiquidity(_liquidityParameters, _LBPair);
}
/// @notice Add liquidity with AVAX while performing safety checks
/// @dev This function is compliant with fee on transfer tokens
/// @param _liquidityParameters The liquidity parameters
/// @return depositIds Bin ids where the liquidity was actually deposited
/// @return liquidityMinted Amounts of LBToken minted for each bin
function addLiquidityAVAX(LiquidityParameters calldata _liquidityParameters)
external
payable
override
returns (uint256[] memory depositIds, uint256[] memory liquidityMinted)
{
ILBPair _LBPair = _getLBPairInformation(
_liquidityParameters.tokenX,
_liquidityParameters.tokenY,
_liquidityParameters.binStep
);
if (_liquidityParameters.tokenX != _LBPair.tokenX()) revert LBRouter__WrongTokenOrder();
if (_liquidityParameters.tokenX == wavax && _liquidityParameters.amountX == msg.value) {
_wavaxDepositAndTransfer(address(_LBPair), msg.value);
_liquidityParameters.tokenY.safeTransferFrom(msg.sender, address(_LBPair), _liquidityParameters.amountY);
} else if (_liquidityParameters.tokenY == wavax && _liquidityParameters.amountY == msg.value) {
_liquidityParameters.tokenX.safeTransferFrom(msg.sender, address(_LBPair), _liquidityParameters.amountX);
_wavaxDepositAndTransfer(address(_LBPair), msg.value);
} else
revert LBRouter__WrongAvaxLiquidityParameters(
address(_liquidityParameters.tokenX),
address(_liquidityParameters.tokenY),
_liquidityParameters.amountX,
_liquidityParameters.amountY,
msg.value
);
(depositIds, liquidityMinted) = _addLiquidity(_liquidityParameters, _LBPair);
}
/// @notice Remove liquidity while performing safety checks
/// @dev This function is compliant with fee on transfer tokens
/// @param _tokenX The address of token X
/// @param _tokenY The address of token Y
/// @param _binStep The bin step of the LBPair
/// @param _amountXMin The min amount to receive of token X
/// @param _amountYMin The min amount to receive of token Y
/// @param _ids The list of ids to burn
/// @param _amounts The list of amounts to burn of each id in `_ids`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountX Amount of token X returned
/// @return amountY Amount of token Y returned
function removeLiquidity(
IERC20 _tokenX,
IERC20 _tokenY,
uint16 _binStep,
uint256 _amountXMin,
uint256 _amountYMin,
uint256[] memory _ids,
uint256[] memory _amounts,
address _to,
uint256 _deadline
) external override ensure(_deadline) returns (uint256 amountX, uint256 amountY) {
ILBPair _LBPair = _getLBPairInformation(_tokenX, _tokenY, _binStep);
bool _isWrongOrder = _tokenX != _LBPair.tokenX();
if (_isWrongOrder) (_amountXMin, _amountYMin) = (_amountYMin, _amountXMin);
(amountX, amountY) = _removeLiquidity(_LBPair, _amountXMin, _amountYMin, _ids, _amounts, _to);
if (_isWrongOrder) (amountX, amountY) = (amountY, amountX);
}
/// @notice Remove AVAX liquidity while performing safety checks
/// @dev This function is **NOT** compliant with fee on transfer tokens.
/// This is wanted as it would make users pays the fee on transfer twice,
/// use the `removeLiquidity` function to remove liquidity with fee on transfer tokens.
/// @param _token The address of token
/// @param _binStep The bin step of the LBPair
/// @param _amountTokenMin The min amount to receive of token
/// @param _amountAVAXMin The min amount to receive of AVAX
/// @param _ids The list of ids to burn
/// @param _amounts The list of amounts to burn of each id in `_ids`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountToken Amount of token returned
/// @return amountAVAX Amount of AVAX returned
function removeLiquidityAVAX(
IERC20 _token,
uint16 _binStep,
uint256 _amountTokenMin,
uint256 _amountAVAXMin,
uint256[] memory _ids,
uint256[] memory _amounts,
address payable _to,
uint256 _deadline
) external override ensure(_deadline) returns (uint256 amountToken, uint256 amountAVAX) {
ILBPair _LBPair = _getLBPairInformation(_token, IERC20(wavax), _binStep);
bool _isAVAXTokenY = IERC20(wavax) == _LBPair.tokenY();
{
if (!_isAVAXTokenY) {
(_amountTokenMin, _amountAVAXMin) = (_amountAVAXMin, _amountTokenMin);
}
(uint256 _amountX, uint256 _amountY) = _removeLiquidity(
_LBPair,
_amountTokenMin,
_amountAVAXMin,
_ids,
_amounts,
address(this)
);
(amountToken, amountAVAX) = _isAVAXTokenY ? (_amountX, _amountY) : (_amountY, _amountX);
}
_token.safeTransfer(_to, amountToken);
wavax.withdraw(amountAVAX);
_safeTransferAVAX(_to, amountAVAX);
}
/// @notice Swaps exact tokens for tokens while performing safety checks
/// @param _amountIn The amount of token to send
/// @param _amountOutMin The min amount of token to receive
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountOut Output amount of the swap
function swapExactTokensForTokens(
uint256 _amountIn,
uint256 _amountOutMin,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to,
uint256 _deadline
) external override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256 amountOut) {
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
_tokenPath[0].safeTransferFrom(msg.sender, _pairs[0], _amountIn);
amountOut = _swapExactTokensForTokens(_amountIn, _pairs, _pairBinSteps, _tokenPath, _to);
if (_amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(_amountOutMin, amountOut);
}
/// @notice Swaps exact tokens for AVAX while performing safety checks
/// @param _amountIn The amount of token to send
/// @param _amountOutMinAVAX The min amount of AVAX to receive
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountOut Output amount of the swap
function swapExactTokensForAVAX(
uint256 _amountIn,
uint256 _amountOutMinAVAX,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address payable _to,
uint256 _deadline
) external override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256 amountOut) {
if (_tokenPath[_pairBinSteps.length] != IERC20(wavax))
revert LBRouter__InvalidTokenPath(address(_tokenPath[_pairBinSteps.length]));
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
_tokenPath[0].safeTransferFrom(msg.sender, _pairs[0], _amountIn);
amountOut = _swapExactTokensForTokens(_amountIn, _pairs, _pairBinSteps, _tokenPath, address(this));
if (_amountOutMinAVAX > amountOut) revert LBRouter__InsufficientAmountOut(_amountOutMinAVAX, amountOut);
wavax.withdraw(amountOut);
_safeTransferAVAX(_to, amountOut);
}
/// @notice Swaps exact AVAX for tokens while performing safety checks
/// @param _amountOutMin The min amount of token to receive
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountOut Output amount of the swap
function swapExactAVAXForTokens(
uint256 _amountOutMin,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to,
uint256 _deadline
) external payable override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256 amountOut) {
if (_tokenPath[0] != IERC20(wavax)) revert LBRouter__InvalidTokenPath(address(_tokenPath[0]));
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
_wavaxDepositAndTransfer(_pairs[0], msg.value);
amountOut = _swapExactTokensForTokens(msg.value, _pairs, _pairBinSteps, _tokenPath, _to);
if (_amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(_amountOutMin, amountOut);
}
/// @notice Swaps tokens for exact tokens while performing safety checks
/// @param _amountOut The amount of token to receive
/// @param _amountInMax The max amount of token to send
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountsIn Input amounts for every step of the swap
function swapTokensForExactTokens(
uint256 _amountOut,
uint256 _amountInMax,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to,
uint256 _deadline
) external override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256[] memory amountsIn) {
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
amountsIn = _getAmountsIn(_pairBinSteps, _pairs, _tokenPath, _amountOut);
if (amountsIn[0] > _amountInMax) revert LBRouter__MaxAmountInExceeded(_amountInMax, amountsIn[0]);
_tokenPath[0].safeTransferFrom(msg.sender, _pairs[0], amountsIn[0]);
uint256 _amountOutReal = _swapTokensForExactTokens(_pairs, _pairBinSteps, _tokenPath, amountsIn, _to);
if (_amountOutReal < _amountOut) revert LBRouter__InsufficientAmountOut(_amountOut, _amountOutReal);
}
/// @notice Swaps tokens for exact AVAX while performing safety checks
/// @param _amountAVAXOut The amount of AVAX to receive
/// @param _amountInMax The max amount of token to send
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountsIn Input amounts for every step of the swap
function swapTokensForExactAVAX(
uint256 _amountAVAXOut,
uint256 _amountInMax,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address payable _to,
uint256 _deadline
) external override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256[] memory amountsIn) {
if (_tokenPath[_pairBinSteps.length] != IERC20(wavax))
revert LBRouter__InvalidTokenPath(address(_tokenPath[_pairBinSteps.length]));
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
amountsIn = _getAmountsIn(_pairBinSteps, _pairs, _tokenPath, _amountAVAXOut);
if (amountsIn[0] > _amountInMax) revert LBRouter__MaxAmountInExceeded(_amountInMax, amountsIn[0]);
_tokenPath[0].safeTransferFrom(msg.sender, _pairs[0], amountsIn[0]);
uint256 _amountOutReal = _swapTokensForExactTokens(_pairs, _pairBinSteps, _tokenPath, amountsIn, address(this));
if (_amountOutReal < _amountAVAXOut) revert LBRouter__InsufficientAmountOut(_amountAVAXOut, _amountOutReal);
wavax.withdraw(_amountOutReal);
_safeTransferAVAX(_to, _amountOutReal);
}
/// @notice Swaps AVAX for exact tokens while performing safety checks
/// @dev Will refund any AVAX amount sent in excess to `msg.sender`
/// @param _amountOut The amount of tokens to receive
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountsIn Input amounts for every step of the swap
function swapAVAXForExactTokens(
uint256 _amountOut,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to,
uint256 _deadline
)
external
payable
override
ensure(_deadline)
verifyInputs(_pairBinSteps, _tokenPath)
returns (uint256[] memory amountsIn)
{
if (_tokenPath[0] != IERC20(wavax)) revert LBRouter__InvalidTokenPath(address(_tokenPath[0]));
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
amountsIn = _getAmountsIn(_pairBinSteps, _pairs, _tokenPath, _amountOut);
if (amountsIn[0] > msg.value) revert LBRouter__MaxAmountInExceeded(msg.value, amountsIn[0]);
_wavaxDepositAndTransfer(_pairs[0], amountsIn[0]);
uint256 _amountOutReal = _swapTokensForExactTokens(_pairs, _pairBinSteps, _tokenPath, amountsIn, _to);
if (_amountOutReal < _amountOut) revert LBRouter__InsufficientAmountOut(_amountOut, _amountOutReal);
if (msg.value > amountsIn[0]) _safeTransferAVAX(msg.sender, msg.value - amountsIn[0]);
}
/// @notice Swaps exact tokens for tokens while performing safety checks supporting for fee on transfer tokens
/// @param _amountIn The amount of token to send
/// @param _amountOutMin The min amount of token to receive
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountOut Output amount of the swap
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 _amountIn,
uint256 _amountOutMin,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to,
uint256 _deadline
) external override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256 amountOut) {
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
IERC20 _targetToken = _tokenPath[_pairs.length];
uint256 _balanceBefore = _targetToken.balanceOf(_to);
_tokenPath[0].safeTransferFrom(msg.sender, _pairs[0], _amountIn);
_swapSupportingFeeOnTransferTokens(_pairs, _pairBinSteps, _tokenPath, _to);
amountOut = _targetToken.balanceOf(_to) - _balanceBefore;
if (_amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(_amountOutMin, amountOut);
}
/// @notice Swaps exact tokens for AVAX while performing safety checks supporting for fee on transfer tokens
/// @param _amountIn The amount of token to send
/// @param _amountOutMinAVAX The min amount of AVAX to receive
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountOut Output amount of the swap
function swapExactTokensForAVAXSupportingFeeOnTransferTokens(
uint256 _amountIn,
uint256 _amountOutMinAVAX,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address payable _to,
uint256 _deadline
) external override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256 amountOut) {
if (_tokenPath[_pairBinSteps.length] != IERC20(wavax))
revert LBRouter__InvalidTokenPath(address(_tokenPath[_pairBinSteps.length]));
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
uint256 _balanceBefore = wavax.balanceOf(address(this));
_tokenPath[0].safeTransferFrom(msg.sender, _pairs[0], _amountIn);
_swapSupportingFeeOnTransferTokens(_pairs, _pairBinSteps, _tokenPath, address(this));
amountOut = wavax.balanceOf(address(this)) - _balanceBefore;
if (_amountOutMinAVAX > amountOut) revert LBRouter__InsufficientAmountOut(_amountOutMinAVAX, amountOut);
wavax.withdraw(amountOut);
_safeTransferAVAX(_to, amountOut);
}
/// @notice Swaps exact AVAX for tokens while performing safety checks supporting for fee on transfer tokens
/// @param _amountOutMin The min amount of token to receive
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @param _deadline The deadline of the tx
/// @return amountOut Output amount of the swap
function swapExactAVAXForTokensSupportingFeeOnTransferTokens(
uint256 _amountOutMin,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to,
uint256 _deadline
) external payable override ensure(_deadline) verifyInputs(_pairBinSteps, _tokenPath) returns (uint256 amountOut) {
if (_tokenPath[0] != IERC20(wavax)) revert LBRouter__InvalidTokenPath(address(_tokenPath[0]));
address[] memory _pairs = _getPairs(_pairBinSteps, _tokenPath);
IERC20 _targetToken = _tokenPath[_pairs.length];
uint256 _balanceBefore = _targetToken.balanceOf(_to);
_wavaxDepositAndTransfer(_pairs[0], msg.value);
_swapSupportingFeeOnTransferTokens(_pairs, _pairBinSteps, _tokenPath, _to);
amountOut = _targetToken.balanceOf(_to) - _balanceBefore;
if (_amountOutMin > amountOut) revert LBRouter__InsufficientAmountOut(_amountOutMin, amountOut);
}
/// @notice Unstuck tokens that are sent to this contract by mistake
/// @dev Only callable by the factory owner
/// @param _token The address of the token
/// @param _to The address of the user to send back the tokens
/// @param _amount The amount to send
function sweep(
IERC20 _token,
address _to,
uint256 _amount
) external override onlyFactoryOwner {
if (address(_token) == address(0)) {
if (_amount == type(uint256).max) _amount = address(this).balance;
_safeTransferAVAX(_to, _amount);
} else {
if (_amount == type(uint256).max) _amount = _token.balanceOf(address(this));
_token.safeTransfer(_to, _amount);
}
}
/// @notice Unstuck LBTokens that are sent to this contract by mistake
/// @dev Only callable by the factory owner
/// @param _lbToken The address of the LBToken
/// @param _to The address of the user to send back the tokens
/// @param _ids The list of token ids
/// @param _amounts The list of amounts to send
function sweepLBToken(
ILBToken _lbToken,
address _to,
uint256[] calldata _ids,
uint256[] calldata _amounts
) external override onlyFactoryOwner {
_lbToken.safeBatchTransferFrom(address(this), _to, _ids, _amounts);
}
/// @notice Helper function to add liquidity
/// @param _liq The liquidity parameter
/// @param _LBPair LBPair where liquidity is deposited
/// @return depositIds Bin ids where the liquidity was actually deposited
/// @return liquidityMinted Amounts of LBToken minted for each bin
function _addLiquidity(LiquidityParameters calldata _liq, ILBPair _LBPair)
private
ensure(_liq.deadline)
returns (uint256[] memory depositIds, uint256[] memory liquidityMinted)
{
unchecked {
if (_liq.deltaIds.length != _liq.distributionX.length && _liq.deltaIds.length != _liq.distributionY.length)
revert LBRouter__LengthsMismatch();
if (_liq.activeIdDesired > type(uint24).max || _liq.idSlippage > type(uint24).max)
revert LBRouter__IdDesiredOverflows(_liq.activeIdDesired, _liq.idSlippage);
(, , uint256 _activeId) = _LBPair.getReservesAndId();
if (
_liq.activeIdDesired + _liq.idSlippage < _activeId || _activeId + _liq.idSlippage < _liq.activeIdDesired
) revert LBRouter__IdSlippageCaught(_liq.activeIdDesired, _liq.idSlippage, _activeId);
depositIds = new uint256[](_liq.deltaIds.length);
for (uint256 i; i < depositIds.length; ++i) {
int256 _id = int256(_activeId) + _liq.deltaIds[i];
if (_id < 0 || uint256(_id) > type(uint24).max) revert LBRouter__IdOverflows(_id);
depositIds[i] = uint256(_id);
}
uint256 _amountXAdded;
uint256 _amountYAdded;
(_amountXAdded, _amountYAdded, liquidityMinted) = _LBPair.mint(
depositIds,
_liq.distributionX,
_liq.distributionY,
_liq.to
);
if (_amountXAdded < _liq.amountXMin || _amountYAdded < _liq.amountYMin)
revert LBRouter__AmountSlippageCaught(_liq.amountXMin, _amountXAdded, _liq.amountYMin, _amountYAdded);
}
}
/// @notice Helper function to return the amounts in
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _pairs The list of pairs
/// @param _tokenPath The swap path
/// @param _amountOut The amount out
/// @return amountsIn The list of amounts in
function _getAmountsIn(
uint256[] memory _pairBinSteps,
address[] memory _pairs,
IERC20[] memory _tokenPath,
uint256 _amountOut
) private view returns (uint256[] memory amountsIn) {
amountsIn = new uint256[](_tokenPath.length);
// Avoid doing -1, as `_pairs.length == _pairBinSteps.length-1`
amountsIn[_pairs.length] = _amountOut;
for (uint256 i = _pairs.length; i != 0; i--) {
IERC20 _token = _tokenPath[i - 1];
uint256 _binStep = _pairBinSteps[i - 1];
address _pair = _pairs[i - 1];
if (_binStep == 0) {
(uint256 _reserveIn, uint256 _reserveOut, ) = IJoePair(_pair).getReserves();
if (_token > _tokenPath[i]) {
(_reserveIn, _reserveOut) = (_reserveOut, _reserveIn);
}
uint256 amountOut_ = amountsIn[i];
amountsIn[i - 1] = amountOut_.getAmountIn(_reserveIn, _reserveOut);
} else {
(amountsIn[i - 1], ) = getSwapIn(ILBPair(_pair), amountsIn[i], ILBPair(_pair).tokenX() == _token);
}
}
}
/// @notice Helper function to remove liquidity
/// @param _LBPair The address of the LBPair
/// @param _amountXMin The min amount to receive of token X
/// @param _amountYMin The min amount to receive of token Y
/// @param _ids The list of ids to burn
/// @param _amounts The list of amounts to burn of each id in `_ids`
/// @param _to The address of the recipient
/// @return amountX The amount of token X sent by the pair
/// @return amountY The amount of token Y sent by the pair
function _removeLiquidity(
ILBPair _LBPair,
uint256 _amountXMin,
uint256 _amountYMin,
uint256[] memory _ids,
uint256[] memory _amounts,
address _to
) private returns (uint256 amountX, uint256 amountY) {
ILBToken(address(_LBPair)).safeBatchTransferFrom(msg.sender, address(_LBPair), _ids, _amounts);
(amountX, amountY) = _LBPair.burn(_ids, _amounts, _to);
if (amountX < _amountXMin || amountY < _amountYMin)
revert LBRouter__AmountSlippageCaught(_amountXMin, amountX, _amountYMin, amountY);
}
/// @notice Helper function to swap exact tokens for tokens
/// @param _amountIn The amount of token sent
/// @param _pairs The list of pairs
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
/// @return amountOut The amount of token sent to `_to`
function _swapExactTokensForTokens(
uint256 _amountIn,
address[] memory _pairs,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to
) private returns (uint256 amountOut) {
IERC20 _token;
uint256 _binStep;
address _recipient;
address _pair;
IERC20 _tokenNext = _tokenPath[0];
amountOut = _amountIn;
unchecked {
for (uint256 i; i < _pairs.length; ++i) {
_pair = _pairs[i];
_binStep = _pairBinSteps[i];
_token = _tokenNext;
_tokenNext = _tokenPath[i + 1];
_recipient = i + 1 == _pairs.length ? _to : _pairs[i + 1];
if (_binStep == 0) {
(uint256 _reserve0, uint256 _reserve1, ) = IJoePair(_pair).getReserves();
if (_token < _tokenNext) {
amountOut = amountOut.getAmountOut(_reserve0, _reserve1);
IJoePair(_pair).swap(0, amountOut, _recipient, "");
} else {
amountOut = amountOut.getAmountOut(_reserve1, _reserve0);
IJoePair(_pair).swap(amountOut, 0, _recipient, "");
}
} else {
bool _swapForY = _tokenNext == ILBPair(_pair).tokenY();
(uint256 _amountXOut, uint256 _amountYOut) = ILBPair(_pair).swap(_swapForY, _recipient);
if (_swapForY) amountOut = _amountYOut;
else amountOut = _amountXOut;
}
}
}
}
/// @notice Helper function to swap tokens for exact tokens
/// @param _pairs The array of pairs
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _amountsIn The list of amounts in
/// @param _to The address of the recipient
/// @return amountOut The amount of token sent to `_to`
function _swapTokensForExactTokens(
address[] memory _pairs,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
uint256[] memory _amountsIn,
address _to
) private returns (uint256 amountOut) {
IERC20 _token;
uint256 _binStep;
address _recipient;
address _pair;
IERC20 _tokenNext = _tokenPath[0];
unchecked {
for (uint256 i; i < _pairs.length; ++i) {
_pair = _pairs[i];
_binStep = _pairBinSteps[i];
_token = _tokenNext;
_tokenNext = _tokenPath[i + 1];
_recipient = i + 1 == _pairs.length ? _to : _pairs[i + 1];
if (_binStep == 0) {
amountOut = _amountsIn[i + 1];
if (_token < _tokenNext) {
IJoePair(_pair).swap(0, amountOut, _recipient, "");
} else {
IJoePair(_pair).swap(amountOut, 0, _recipient, "");
}
} else {
bool _swapForY = _tokenNext == ILBPair(_pair).tokenY();
(uint256 _amountXOut, uint256 _amountYOut) = ILBPair(_pair).swap(_swapForY, _recipient);
if (_swapForY) amountOut = _amountYOut;
else amountOut = _amountXOut;
}
}
}
}
/// @notice Helper function to swap exact tokens supporting for fee on transfer tokens
/// @param _pairs The list of pairs
/// @param _pairBinSteps The bin step of the pairs (0: V1, other values will use V2)
/// @param _tokenPath The swap path using the binSteps following `_pairBinSteps`
/// @param _to The address of the recipient
function _swapSupportingFeeOnTransferTokens(
address[] memory _pairs,
uint256[] memory _pairBinSteps,
IERC20[] memory _tokenPath,
address _to
) private {
IERC20 _token;
uint256 _binStep;
address _recipient;
address _pair;
IERC20 _tokenNext = _tokenPath[0];
unchecked {
for (uint256 i; i < _pairs.length; ++i) {
_pair = _pairs[i];
_binStep = _pairBinSteps[i];
_token = _tokenNext;
_tokenNext = _tokenPath[i + 1];
_recipient = i + 1 == _pairs.length ? _to : _pairs[i + 1];
if (_binStep == 0) {
(uint256 _reserve0, uint256 _reserve1, ) = IJoePair(_pair).getReserves();
if (_token < _tokenNext) {
uint256 _amountIn = _token.balanceOf(_pair) - _reserve0;
uint256 _amountOut = _amountIn.getAmountOut(_reserve0, _reserve1);
IJoePair(_pair).swap(0, _amountOut, _recipient, "");
} else {
uint256 _amountIn = _token.balanceOf(_pair) - _reserve1;
uint256 _amountOut = _amountIn.getAmountOut(_reserve1, _reserve0);
IJoePair(_pair).swap(_amountOut, 0, _recipient, "");
}
} else {
ILBPair(_pair).swap(_tokenNext == ILBPair(_pair).tokenY(), _recipient);
}
}
}
}
/// @notice Helper function to return the address of the LBPair
/// @dev Revert if the pair is not created yet
/// @param _tokenX The address of the tokenX
/// @param _tokenY The address of the tokenY
/// @param _binStep The bin step of the LBPair
/// @return The address of the LBPair
function _getLBPairInformation(
IERC20 _tokenX,
IERC20 _tokenY,
uint256 _binStep
) private view returns (ILBPair) {
ILBPair _LBPair = factory.getLBPairInformation(_tokenX, _tokenY, _binStep).LBPair;
if (address(_LBPair) == address(0))
revert LBRouter__PairNotCreated(address(_tokenX), address(_tokenY), _binStep);
return _LBPair;
}
/// @notice Helper function to return the address of the pair (v1 or v2, according to `_binStep`)
/// @dev Revert if the pair is not created yet
/// @param _binStep The bin step of the LBPair, 0 means using V1 pair, any other value will use V2
/// @param _tokenX The address of the tokenX
/// @param _tokenY The address of the tokenY
/// @return _pair The address of the pair of binStep `_binStep`
function _getPair(
uint256 _binStep,
IERC20 _tokenX,
IERC20 _tokenY
) private view returns (address _pair) {
if (_binStep == 0) {
_pair = oldFactory.getPair(address(_tokenX), address(_tokenY));
if (_pair == address(0)) revert LBRouter__PairNotCreated(address(_tokenX), address(_tokenY), _binStep);
} else _pair = address(_getLBPairInformation(_tokenX, _tokenY, _binStep));
}
function _getPairs(uint256[] memory _pairBinSteps, IERC20[] memory _tokenPath)
private
view
returns (address[] memory pairs)
{
pairs = new address[](_pairBinSteps.length);
IERC20 _token;
IERC20 _tokenNext = _tokenPath[0];
unchecked {
for (uint256 i; i < pairs.length; ++i) {
_token = _tokenNext;
_tokenNext = _tokenPath[i + 1];
pairs[i] = _getPair(_pairBinSteps[i], _token, _tokenNext);
}
}
}
/// @notice Helper function to transfer AVAX
/// @param _to The address of the recipient
/// @param _amount The AVAX amount to send
function _safeTransferAVAX(address _to, uint256 _amount) private {
(bool success, ) = _to.call{value: _amount}("");
if (!success) revert LBRouter__FailedToSendAVAX(_to, _amount);
}
/// @notice Helper function to deposit and transfer wavax
/// @param _to The address of the recipient
/// @param _amount The AVAX amount to wrap
function _wavaxDepositAndTransfer(address _to, uint256 _amount) private {
wavax.deposit{value: _amount}();
wavax.safeTransfer(_to, _amount);
}
}// 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
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: GPL-3.0
pragma solidity 0.8.10;
/// @title Joe V1 Factory Interface
/// @notice Interface to interact with Joe V1 Factory
interface IJoeFactory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function migrator() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
function setMigrator(address) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.10;
/// @title Joe V1 Pair Interface
/// @notice Interface to interact with Joe V1 Pairs
interface IJoePair {
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint256);
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint256);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves()
external
view
returns (
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
);
function price0CumulativeLast() external view returns (uint256);
function price1CumulativeLast() external view returns (uint256);
function kLast() external view returns (uint256);
function mint(address to) external returns (uint256 liquidity);
function burn(address to) external returns (uint256 amount0, uint256 amount1);
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}// 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 "./IJoeFactory.sol";
import "./ILBPair.sol";
import "./ILBToken.sol";
import "./IWAVAX.sol";
/// @title Liquidity Book Router Interface
/// @author Trader Joe
/// @notice Required interface of LBRouter contract
interface ILBRouter {
/// @dev The liquidity parameters, such as:
/// - tokenX: The address of token X
/// - tokenY: The address of token Y
/// - binStep: The bin step of the pair
/// - amountX: The amount to send of token X
/// - amountY: The amount to send of token Y
/// - amountXMin: The min amount of token X added to liquidity
/// - amountYMin: The min amount of token Y added to liquidity
/// - activeIdDesired: The active id that user wants to add liquidity from
/// - idSlippage: The number of id that are allowed to slip
/// - deltaIds: The list of delta ids to add liquidity (`deltaId = activeId - desiredId`)
/// - distributionX: The distribution of tokenX with sum(distributionX) = 100e18 (100%) or 0 (0%)
/// - distributionY: The distribution of tokenY with sum(distributionY) = 100e18 (100%) or 0 (0%)
/// - to: The address of the recipient
/// - deadline: The deadline of the tx
struct LiquidityParameters {
IERC20 tokenX;
IERC20 tokenY;
uint256 binStep;
uint256 amountX;
uint256 amountY;
uint256 amountXMin;
uint256 amountYMin;
uint256 activeIdDesired;
uint256 idSlippage;
int256[] deltaIds;
uint256[] distributionX;
uint256[] distributionY;
address to;
uint256 deadline;
}
function factory() external view returns (ILBFactory);
function oldFactory() external view returns (IJoeFactory);
function wavax() external view returns (IWAVAX);
function getIdFromPrice(ILBPair LBPair, uint256 price) external view returns (uint24);
function getPriceFromId(ILBPair LBPair, uint24 id) external view returns (uint256);
function getSwapIn(
ILBPair LBPair,
uint256 amountOut,
bool swapForY
) external view returns (uint256 amountIn, uint256 feesIn);
function getSwapOut(
ILBPair LBPair,
uint256 amountIn,
bool swapForY
) external view returns (uint256 amountOut, uint256 feesIn);
function createLBPair(
IERC20 tokenX,
IERC20 tokenY,
uint24 activeId,
uint16 binStep
) external returns (ILBPair pair);
function addLiquidity(LiquidityParameters calldata liquidityParameters)
external
returns (uint256[] memory depositIds, uint256[] memory liquidityMinted);
function addLiquidityAVAX(LiquidityParameters calldata liquidityParameters)
external
payable
returns (uint256[] memory depositIds, uint256[] memory liquidityMinted);
function removeLiquidity(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
uint256 amountXMin,
uint256 amountYMin,
uint256[] memory ids,
uint256[] memory amounts,
address to,
uint256 deadline
) external returns (uint256 amountX, uint256 amountY);
function removeLiquidityAVAX(
IERC20 token,
uint16 binStep,
uint256 amountTokenMin,
uint256 amountAVAXMin,
uint256[] memory ids,
uint256[] memory amounts,
address payable to,
uint256 deadline
) external returns (uint256 amountToken, uint256 amountAVAX);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactTokensForAVAX(
uint256 amountIn,
uint256 amountOutMinAVAX,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address payable to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactAVAXForTokens(
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external payable returns (uint256 amountOut);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external returns (uint256[] memory amountsIn);
function swapTokensForExactAVAX(
uint256 amountOut,
uint256 amountInMax,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address payable to,
uint256 deadline
) external returns (uint256[] memory amountsIn);
function swapAVAXForExactTokens(
uint256 amountOut,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external payable returns (uint256[] memory amountsIn);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactTokensForAVAXSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMinAVAX,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address payable to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactAVAXForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external payable returns (uint256 amountOut);
function sweep(
IERC20 token,
address to,
uint256 amount
) external;
function sweepLBToken(
ILBToken _lbToken,
address _to,
uint256[] calldata _ids,
uint256[] calldata _amounts
) 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 "openzeppelin/token/ERC20/IERC20.sol";
/// @title WAVAX Interface
/// @notice Required interface of Wrapped AVAX contract
interface IWAVAX is IERC20 {
function deposit() external payable;
function withdraw(uint256) 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 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;
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: GPL-3.0
pragma solidity 0.8.10;
import "../LBErrors.sol";
/// @title Liquidity Book Joe Library Helper Library
/// @author Trader Joe
/// @notice Helper contract used for Joe V1 related calculations
library JoeLibrary {
// returns sorted token addresses, used to handle return values from pairs sorted in this order
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
if (tokenA == tokenB) revert JoeLibrary__IdenticalAddresses();
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
if (token0 == address(0)) revert JoeLibrary__AddressZero();
}
// given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
function quote(
uint256 amountA,
uint256 reserveA,
uint256 reserveB
) internal pure returns (uint256 amountB) {
if (amountA == 0) revert JoeLibrary__InsufficientAmount();
if (reserveA == 0 || reserveB == 0) revert JoeLibrary__InsufficientLiquidity();
amountB = (amountA * reserveB) / reserveA;
}
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
function getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256 amountOut) {
if (amountIn == 0) revert JoeLibrary__InsufficientAmount();
if (reserveIn == 0 || reserveOut == 0) revert JoeLibrary__InsufficientLiquidity();
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator;
}
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
function getAmountIn(
uint256 amountOut,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256 amountIn) {
if (amountOut == 0) revert JoeLibrary__InsufficientAmount();
if (reserveIn == 0 || reserveOut == 0) revert JoeLibrary__InsufficientLiquidity();
uint256 numerator = reserveIn * amountOut * 1000;
uint256 denominator = (reserveOut - amountOut) * 997;
amountIn = numerator / denominator + 1;
}
}// 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";
/// @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 "./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;
}
}{
"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"},{"internalType":"contract IJoeFactory","name":"_oldFactory","type":"address"},{"internalType":"contract IWAVAX","name":"_wavax","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":"JoeLibrary__InsufficientAmount","type":"error"},{"inputs":[],"name":"JoeLibrary__InsufficientLiquidity","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountXMin","type":"uint256"},{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountYMin","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"name":"LBRouter__AmountSlippageCaught","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"LBRouter__BinReserveOverflows","type":"error"},{"inputs":[],"name":"LBRouter__BrokenSwapSafetyCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"currentTimestamp","type":"uint256"}],"name":"LBRouter__DeadlineExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LBRouter__FailedToSendAVAX","type":"error"},{"inputs":[{"internalType":"uint256","name":"idDesired","type":"uint256"},{"internalType":"uint256","name":"idSlippage","type":"uint256"}],"name":"LBRouter__IdDesiredOverflows","type":"error"},{"inputs":[{"internalType":"int256","name":"id","type":"int256"}],"name":"LBRouter__IdOverflows","type":"error"},{"inputs":[{"internalType":"uint256","name":"activeIdDesired","type":"uint256"},{"internalType":"uint256","name":"idSlippage","type":"uint256"},{"internalType":"uint256","name":"activeId","type":"uint256"}],"name":"LBRouter__IdSlippageCaught","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"LBRouter__InsufficientAmountOut","type":"error"},{"inputs":[{"internalType":"address","name":"wrongToken","type":"address"}],"name":"LBRouter__InvalidTokenPath","type":"error"},{"inputs":[],"name":"LBRouter__LengthsMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"LBRouter__MaxAmountInExceeded","type":"error"},{"inputs":[],"name":"LBRouter__NotFactoryOwner","type":"error"},{"inputs":[{"internalType":"address","name":"tokenX","type":"address"},{"internalType":"address","name":"tokenY","type":"address"},{"internalType":"uint256","name":"binStep","type":"uint256"}],"name":"LBRouter__PairNotCreated","type":"error"},{"inputs":[],"name":"LBRouter__SenderIsNotWAVAX","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"LBRouter__SwapOverflows","type":"error"},{"inputs":[{"internalType":"uint256","name":"excess","type":"uint256"}],"name":"LBRouter__TooMuchTokensIn","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"reserve","type":"uint256"}],"name":"LBRouter__WrongAmounts","type":"error"},{"inputs":[{"internalType":"address","name":"tokenX","type":"address"},{"internalType":"address","name":"tokenY","type":"address"},{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"},{"internalType":"uint256","name":"msgValue","type":"uint256"}],"name":"LBRouter__WrongAvaxLiquidityParameters","type":"error"},{"inputs":[],"name":"LBRouter__WrongTokenOrder","type":"error"},{"inputs":[],"name":"Math128x128__LogUnderflow","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":"x","type":"uint256"}],"name":"SafeCast__Exceeds128Bits","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":[{"components":[{"internalType":"contract IERC20","name":"tokenX","type":"address"},{"internalType":"contract IERC20","name":"tokenY","type":"address"},{"internalType":"uint256","name":"binStep","type":"uint256"},{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"},{"internalType":"uint256","name":"amountXMin","type":"uint256"},{"internalType":"uint256","name":"amountYMin","type":"uint256"},{"internalType":"uint256","name":"activeIdDesired","type":"uint256"},{"internalType":"uint256","name":"idSlippage","type":"uint256"},{"internalType":"int256[]","name":"deltaIds","type":"int256[]"},{"internalType":"uint256[]","name":"distributionX","type":"uint256[]"},{"internalType":"uint256[]","name":"distributionY","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct ILBRouter.LiquidityParameters","name":"_liquidityParameters","type":"tuple"}],"name":"addLiquidity","outputs":[{"internalType":"uint256[]","name":"depositIds","type":"uint256[]"},{"internalType":"uint256[]","name":"liquidityMinted","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"tokenX","type":"address"},{"internalType":"contract IERC20","name":"tokenY","type":"address"},{"internalType":"uint256","name":"binStep","type":"uint256"},{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"},{"internalType":"uint256","name":"amountXMin","type":"uint256"},{"internalType":"uint256","name":"amountYMin","type":"uint256"},{"internalType":"uint256","name":"activeIdDesired","type":"uint256"},{"internalType":"uint256","name":"idSlippage","type":"uint256"},{"internalType":"int256[]","name":"deltaIds","type":"int256[]"},{"internalType":"uint256[]","name":"distributionX","type":"uint256[]"},{"internalType":"uint256[]","name":"distributionY","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct ILBRouter.LiquidityParameters","name":"_liquidityParameters","type":"tuple"}],"name":"addLiquidityAVAX","outputs":[{"internalType":"uint256[]","name":"depositIds","type":"uint256[]"},{"internalType":"uint256[]","name":"liquidityMinted","type":"uint256[]"}],"stateMutability":"payable","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":"_binStep","type":"uint16"}],"name":"createLBPair","outputs":[{"internalType":"contract ILBPair","name":"pair","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract ILBFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"_LBPair","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getIdFromPrice","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"_LBPair","type":"address"},{"internalType":"uint24","name":"_id","type":"uint24"}],"name":"getPriceFromId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"_LBPair","type":"address"},{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"bool","name":"_swapForY","type":"bool"}],"name":"getSwapIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"feesIn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"_LBPair","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"bool","name":"_swapForY","type":"bool"}],"name":"getSwapOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"feesIn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oldFactory","outputs":[{"internalType":"contract IJoeFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_tokenX","type":"address"},{"internalType":"contract IERC20","name":"_tokenY","type":"address"},{"internalType":"uint16","name":"_binStep","type":"uint16"},{"internalType":"uint256","name":"_amountXMin","type":"uint256"},{"internalType":"uint256","name":"_amountYMin","type":"uint256"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountX","type":"uint256"},{"internalType":"uint256","name":"amountY","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint16","name":"_binStep","type":"uint16"},{"internalType":"uint256","name":"_amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"_amountAVAXMin","type":"uint256"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"removeLiquidityAVAX","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountAVAX","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapAVAXForExactTokens","outputs":[{"internalType":"uint256[]","name":"amountsIn","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactAVAXForTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactAVAXForTokensSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMinAVAX","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactTokensForAVAX","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMinAVAX","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactTokensForAVAXSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountAVAXOut","type":"uint256"},{"internalType":"uint256","name":"_amountInMax","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapTokensForExactAVAX","outputs":[{"internalType":"uint256[]","name":"amountsIn","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"uint256","name":"_amountInMax","type":"uint256"},{"internalType":"uint256[]","name":"_pairBinSteps","type":"uint256[]"},{"internalType":"contract IERC20[]","name":"_tokenPath","type":"address[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amountsIn","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILBToken","name":"_lbToken","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"sweepLBToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wavax","outputs":[{"internalType":"contract IWAVAX","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60e06040523480156200001157600080fd5b5060405162005ae038038062005ae083398101604081905262000034916200006b565b6001600160a01b0392831660805290821660a0521660c052620000bf565b6001600160a01b03811681146200006857600080fd5b50565b6000806000606084860312156200008157600080fd5b83516200008e8162000052565b6020850151909350620000a18162000052565b6040850151909250620000b48162000052565b809150509250925092565b60805160a05160c0516159496200019760003960008181610194015281816101eb01528181610ac301528181610c3d01528181610e640152818161170a0152818161185f01528181611948015281816119db01528181611a9e01528181611b5e01528181611d1101528181611dab01528181611e3501528181612332015281816123b0015281816125610152818161263a01528181612eac0152612f2a01526000818161023c01526143200152600081816104390152818161139001528181611533015281816121510152613b7301526159496000f3fe6080604052600436106101845760003560e01c80636d3420ed116100d6578063d0e380f21161007f578063ea8f43d811610059578063ea8f43d8146104c9578063f96fe925146104dc578063fb321c701461051057600080fd5b8063d0e380f21461045b578063e324a3e41461047b578063e9361c08146104a957600080fd5b8063bcb1c957116100b0578063bcb1c957146103e7578063c22159b614610407578063c45a01551461042757600080fd5b80636d3420ed146103875780639a17e820146103a7578063a7b856d3146103c757600080fd5b806342f564a01161013857806362c067671161011257806362c0676714610327578063659ac74b146103475780636d0ff4951461036757600080fd5b806342f564a0146102d4578063440830bd146102f45780635bdd4b7c1461030757600080fd5b80632004b724116101695780632004b7241461025e578063212a1d9414610293578063264bb94e146102c157600080fd5b8063117be4c2146101d95780631bd6dfe11461022a57600080fd5b366101d457336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101d2576040516310d0bef760e31b815260040160405180910390fd5b005b600080fd5b3480156101e557600080fd5b5061020d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561023657600080fd5b5061020d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561026a57600080fd5b5061027e610279366004614bc9565b610530565b60408051928352602083019190915201610221565b34801561029f57600080fd5b506102b36102ae366004614d7a565b610839565b604051908152602001610221565b6102b36102cf366004614e0e565b610a57565b6102e76102e2366004614e0e565b610bd1565b6040516102219190614ed1565b6102b3610302366004614e0e565b610df8565b34801561031357600080fd5b5061027e610322366004614bc9565b61103d565b34801561033357600080fd5b506101d2610342366004614ee4565b61138e565b34801561035357600080fd5b5061020d610362366004614f51565b6114f4565b34801561037357600080fd5b506102b3610382366004614d7a565b6115ab565b34801561039357600080fd5b506102e76103a2366004614d7a565b61169e565b3480156103b357600080fd5b506102b36103c2366004614d7a565b6118dc565b3480156103d357600080fd5b506102e76103e2366004614d7a565b611bcc565b3480156103f357600080fd5b5061027e610402366004614fad565b611cdb565b34801561041357600080fd5b5061027e610422366004615068565b611eb3565b34801561043357600080fd5b5061020d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561046757600080fd5b506102b3610476366004615130565b611fa7565b34801561048757600080fd5b5061049b610496366004615169565b612028565b6040516102219291906151a5565b3480156104b557600080fd5b506101d26104c4366004615216565b61214f565b61049b6104d7366004615169565b612272565b3480156104e857600080fd5b506104fc6104f73660046152ab565b612482565b60405162ffffff9091168152602001610221565b34801561051c57600080fd5b506102b361052b366004614d7a565b6124f5565b6000806000856001600160a01b0316631b05b83e6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610573573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059791906152d7565b925050506000866001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa1580156105dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106009190615330565b905061060c81836126a8565b6040805160808101825260008082526020820181905291810182905260608101919091525b604051630157d2d160e31b815262ffffff8416600482015260009081906001600160a01b038b1690630abe9688906024016040805180830381865afa15801561067e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a2919061541c565b604080516080810182526001600160701b03938416808252939092166020830152600090820181905260608201529350151591508190506106ef575060208101516001600160701b031615155b1561078557600080806107058486888c8e612765565b919450925090506001600160701b0383111561073c57604051633d9af4e160e11b8152600481018790526024015b60405180910390fd5b8051610751906001600160801b031684615456565b61075b908b61546e565b8151909a50610773906001600160801b031688615456565b965061077f8289615456565b97505050505b861561080c57604051638f919a8360e01b815262ffffff8416600482015286151560248201526001600160a01b03891690638f919a8390604401602060405180830381865afa1580156107dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108009190615485565b62ffffff169250610631565b861561082e576040516349cda5cd60e11b815260048101889052602401610733565b505050935093915050565b600081804211156108665760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610885575080518251610882906001615456565b14155b156108a35760405163b91b4d4d60e01b815260040160405180910390fd5b60006108af8989612889565b90506000888251815181106108c6576108c66154a2565b60209081029190910101516040516370a0823160e01b81526001600160a01b038a811660048301529192506000918316906370a0823190602401602060405180830381865afa15801561091d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094191906154b8565b9050610997338460008151811061095a5761095a6154a2565b60200260200101518f8d600081518110610976576109766154a2565b60200260200101516001600160a01b031661297e909392919063ffffffff16565b6109a3838c8c8c612a3f565b6040516370a0823160e01b81526001600160a01b038a811660048301528291908416906370a0823190602401602060405180830381865afa1580156109ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1091906154b8565b610a1a919061546e565b9650868c1115610a47576040516313fab00360e21b8152600481018d905260248101889052604401610733565b5050505050509695505050505050565b60008180421115610a845760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610aa3575080518251610aa0906001615456565b14155b15610ac15760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031687600081518110610afe57610afe6154a2565b60200260200101516001600160a01b031614610b585786600081518110610b2757610b276154a2565b602002602001015160405163cfec0e0160e01b815260040161073391906001600160a01b0391909116815260200190565b6000610b648989612889565b9050610b8a81600081518110610b7c57610b7c6154a2565b602002602001015134612eaa565b610b9734828b8b8b612f59565b9450848a1115610bc4576040516313fab00360e21b8152600481018b905260248101869052604401610733565b5050505095945050505050565b60608180421115610bfe5760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610c1d575080518251610c1a906001615456565b14155b15610c3b5760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031687600081518110610c7857610c786154a2565b60200260200101516001600160a01b031614610ca15786600081518110610b2757610b276154a2565b6000610cad8989612889565b9050610cbb89828a8d6132da565b94503485600081518110610cd157610cd16154a2565b60200260200101511115610d1f573485600081518110610cf357610cf36154a2565b602002602001015160405163194ee21960e31b8152600401610733929190918252602082015260400190565b610d5d81600081518110610d3557610d356154a2565b602002602001015186600081518110610d5057610d506154a2565b6020026020010151612eaa565b6000610d6c828b8b898c6135c5565b90508a811015610d99576040516313fab00360e21b8152600481018c905260248101829052604401610733565b85600081518110610dac57610dac6154a2565b6020026020010151341115610dea57610dea3387600081518110610dd257610dd26154a2565b602002602001015134610de5919061546e565b613894565b505050505095945050505050565b60008180421115610e255760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610e44575080518251610e41906001615456565b14155b15610e625760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031687600081518110610e9f57610e9f6154a2565b60200260200101516001600160a01b031614610ec85786600081518110610b2757610b276154a2565b6000610ed48989612889565b9050600088825181518110610eeb57610eeb6154a2565b60209081029190910101516040516370a0823160e01b81526001600160a01b038a811660048301529192506000918316906370a0823190602401602060405180830381865afa158015610f42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f6691906154b8565b9050610f7e83600081518110610b7c57610b7c6154a2565b610f8a838c8c8c612a3f565b6040516370a0823160e01b81526001600160a01b038a811660048301528291908416906370a0823190602401602060405180830381865afa158015610fd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff791906154b8565b611001919061546e565b9650868c111561102e576040516313fab00360e21b8152600481018d905260248101889052604401610733565b50505050505095945050505050565b6000806000806000876001600160a01b0316631b05b83e6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a791906152d7565b92509250925086600014806110c85750856110c4578287116110c8565b8187115b156110fd5786866110d957836110db565b825b604051637c40a4df60e01b815260048101929092526024820152604401610733565b6000886001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa15801561113e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111629190615330565b905061116e81836126a8565b60008060005b604051630157d2d160e31b815262ffffff8616600482015260009081906001600160a01b038f1690630abe9688906024016040805180830381865afa1580156111c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e5919061541c565b915091508b6111f457816111f6565b805b92505050600061120e86866000015161ffff1661391a565b905081156112cd57818c1015611224578b611226565b815b935060008b6112405761123b82866080613962565b61124c565b61124c85608084613995565b905061125886886139c7565b60006112648783613a39565b90506112708183615456565b94506001600160701b036112848587615456565b11156112a65760405163ec44eb8960e01b815260048101899052602401610733565b6112b0858d615456565b9b506112bc818c615456565b9a506112c8868f61546e565b9d5050505b8b1561135457604051638f919a8360e01b815262ffffff871660048201528b151560248201526001600160a01b038e1690638f919a8390604401602060405180830381865afa158015611324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113489190615485565b62ffffff16955061135a565b50611360565b50611174565b8a1561137f576040516330b1335760e21b815260040160405180910390fd5b50505050505050935093915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061141091906154d1565b6001600160a01b0316336001600160a01b03161461144157604051635d9515b960e11b815260040160405180910390fd5b6001600160a01b03831661146b5760001981141561145c5750475b6114668282613894565b505050565b6000198114156114e0576040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156114b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114dd91906154b8565b90505b6114666001600160a01b0384168383613a89565b60405163659ac74b60e01b81526001600160a01b038581166004830152848116602483015262ffffff8416604483015261ffff831660648301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063659ac74b906084016020604051808303816000875af115801561157e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a291906154d1565b95945050505050565b600081804211156115d85760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b85858151600014806115f75750805182516115f4906001615456565b14155b156116155760405163b91b4d4d60e01b815260040160405180910390fd5b60006116218989612889565b9050611656338260008151811061163a5761163a6154a2565b60200260200101518d8b600081518110610976576109766154a2565b6116638b828b8b8b612f59565b9450848a1115611690576040516313fab00360e21b8152600481018b905260248101869052604401610733565b505050509695505050505050565b606081804211156116cb5760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b85858151600014806116ea5750805182516116e7906001615456565b14155b156117085760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031687895181518110611745576117456154a2565b60200260200101516001600160a01b03161461176e5786885181518110610b2757610b276154a2565b600061177a8989612889565b905061178889828a8e6132da565b9450898560008151811061179e5761179e6154a2565b602002602001015111156117c0578985600081518110610cf357610cf36154a2565b61180d33826000815181106117d7576117d76154a2565b6020026020010151876000815181106117f2576117f26154a2565b60200260200101518b600081518110610976576109766154a2565b600061181c828b8b89306135c5565b90508b811015611849576040516313fab00360e21b8152600481018d905260248101829052604401610733565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156118ab57600080fd5b505af11580156118bf573d6000803e3d6000fd5b505050506118cd8882613894565b50505050509695505050505050565b600081804211156119095760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480611928575080518251611925906001615456565b14155b156119465760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031687895181518110611983576119836154a2565b60200260200101516001600160a01b0316146119ac5786885181518110610b2757610b276154a2565b60006119b88989612889565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611a22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4691906154b8565b9050611a7b3383600081518110611a5f57611a5f6154a2565b60200260200101518e8c600081518110610976576109766154a2565b611a87828b8b30612a3f565b6040516370a0823160e01b815230600482015281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b1191906154b8565b611b1b919061546e565b9550858b1115611b48576040516313fab00360e21b8152600481018c905260248101879052604401610733565b604051632e1a7d4d60e01b8152600481018790527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611baa57600080fd5b505af1158015611bbe573d6000803e3d6000fd5b505050506118cd8887613894565b60608180421115611bf95760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480611c18575080518251611c15906001615456565b14155b15611c365760405163b91b4d4d60e01b815260040160405180910390fd5b6000611c428989612889565b9050611c5089828a8e6132da565b94508985600081518110611c6657611c666154a2565b60200260200101511115611c88578985600081518110610cf357610cf36154a2565b611c9f33826000815181106117d7576117d76154a2565b6000611cae828b8b898c6135c5565b90508b8110156118cd576040516313fab00360e21b8152600481018d905260248101829052604401610733565b6000808280421115611d095760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b6000611d3a8c7f00000000000000000000000000000000000000000000000000000000000000008d61ffff16613b40565b90506000816001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da091906154d1565b6001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614905080611ddf579899985b600080611df0848e8e8e8e30613c25565b9150915082611e00578082611e03565b81815b9097509550611e1f9150506001600160a01b038e168887613a89565b604051632e1a7d4d60e01b8152600481018590527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611e8157600080fd5b505af1158015611e95573d6000803e3d6000fd5b50505050611ea38785613894565b5050509850989650505050505050565b6000808280421115611ee15760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b6000611ef28d8d8d61ffff16613b40565b90506000816001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5891906154d1565b6001600160a01b03168e6001600160a01b0316141590508015611f79579899985b611f87828c8c8c8c8c613c25565b90955093508015611f96579293925b505050995099975050505050505050565b600061201f8262ffffff16846001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa158015611ff1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120159190615330565b5161ffff1661391a565b90505b92915050565b606080600061205761203d60208601866154ee565b61204d60408701602088016154ee565b8660400135613b40565b9050806001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bb91906154d1565b6001600160a01b03166120d160208601866154ee565b6001600160a01b0316146120f85760405163b33f8ab960e01b815260040160405180910390fd5b6121203382606087013561210f60208901896154ee565b6001600160a01b031692919061297e565b61213a3382608087013561210f6040890160208a016154ee565b6121448482613d51565b909590945092505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d191906154d1565b6001600160a01b0316336001600160a01b03161461220257604051635d9515b960e11b815260040160405180910390fd5b604051633ee83b9960e21b81526001600160a01b0387169063fba0ee64906122389030908990899089908990899060040161555a565b600060405180830381600087803b15801561225257600080fd5b505af1158015612266573d6000803e3d6000fd5b50505050505050505050565b606080600061228761203d60208601866154ee565b9050806001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122eb91906154d1565b6001600160a01b031661230160208601866154ee565b6001600160a01b0316146123285760405163b33f8ab960e01b815260040160405180910390fd5b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001661235f60208601866154ee565b6001600160a01b03161480156123785750348460600135145b156123a6576123878134612eaa565b6123a13382608087013561210f6040890160208a016154ee565b61213a565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166123e060408601602087016154ee565b6001600160a01b03161480156123f95750348460800135145b1561241f576124153382606087013561210f60208901896154ee565b6123a18134612eaa565b61242c60208501856154ee565b61243c60408601602087016154ee565b60405163959ceb2b60e01b81526001600160a01b03928316600482015291166024820152606085013560448201526080850135606482015234608482015260a401610733565b600061201f82846001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa1580156124c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124eb9190615330565b5161ffff166140ed565b600081804211156125225760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b858581516000148061254157508051825161253e906001615456565b14155b1561255f5760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168789518151811061259c5761259c6154a2565b60200260200101516001600160a01b0316146125c55786885181518110610b2757610b276154a2565b60006125d18989612889565b90506125ea338260008151811061163a5761163a6154a2565b6125f78b828b8b30612f59565b9450848a1115612624576040516313fab00360e21b8152600481018b905260248101869052604401610733565b604051632e1a7d4d60e01b8152600481018690527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561268657600080fd5b505af115801561269a573d6000803e3d6000fd5b505050506116908786613894565b600082610160015164ffffffffff16426126c2919061546e565b9050826040015161ffff16811015806126e5575061016083015164ffffffffff16155b156127455762ffffff8216610140840152606083015161ffff1681101561273c5761271083610100015162ffffff16846080015161ffff16028161272b5761272b6155a7565b0462ffffff16610120840152612745565b60006101208401525b61274e42614153565b64ffffffffff1661016084015261146683836139c7565b60408051808201909152600080825260208201819052908190600061279287896000015161ffff1661391a565b905060008087156127c05760208b01516001600160701b031691506127b982608085613995565b90506127dc565b8a516001600160701b031691506127d983836080613962565b90505b6127e68a8a6139c7565b6127fa6127f38b83613a39565b8b90614183565b80519094508790612814906001600160801b031683615456565b116128245780955081945061287b565b6128316127f38b896141cf565b8051909450612849906001600160801b03168861546e565b9550876128615761285c86608085614216565b61286d565b61286d83876080614263565b94508185111561287b578194505b505050955095509592505050565b6060825167ffffffffffffffff8111156128a5576128a5614c0b565b6040519080825280602002602001820160405280156128ce578160200160208202803683370190505b509050600080836000815181106128e7576128e76154a2565b6020026020010151905060005b835181101561297557819250848160010181518110612915576129156154a2565b60200260200101519150612943868281518110612934576129346154a2565b602002602001015184846142f2565b848281518110612955576129556154a2565b6001600160a01b03909216602092830291909101909101526001016128f4565b50505092915050565b8015612a3957604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b17905260006129f486836143de565b905060008151118015612a18575080806020019051810190612a1691906155bd565b155b15612a365760405163197138bd60e11b815260040160405180910390fd5b50505b50505050565b600080600080600086600081518110612a5a57612a5a6154a2565b6020026020010151905060005b895181101561226657898181518110612a8257612a826154a2565b60200260200101519250888181518110612a9e57612a9e6154a2565b60200260200101519450819550878160010181518110612ac057612ac06154a2565b6020026020010151915089518160010114612af757898160010181518110612aea57612aea6154a2565b6020026020010151612af9565b865b935084612da157600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015612b41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6591906155f1565b506001600160701b031691506001600160701b03169150836001600160a01b0316886001600160a01b03161015612c9a576040516370a0823160e01b81526001600160a01b03868116600483015260009184918b16906370a0823190602401602060405180830381865afa158015612be1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0591906154b8565b0390506000612c158285856144a9565b60405163022c0d9f60e01b8152600060048201819052602482018390526001600160a01b038b811660448401526080606484015260848301919091529192509088169063022c0d9f9060a401600060405180830381600087803b158015612c7b57600080fd5b505af1158015612c8f573d6000803e3d6000fd5b505050505050612d9a565b6040516370a0823160e01b81526001600160a01b03868116600483015260009183918b16906370a0823190602401602060405180830381865afa158015612ce5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0991906154b8565b0390506000612d198284866144a9565b60405163022c0d9f60e01b8152600481018290526000602482018190526001600160a01b038b811660448401526080606484015260848301919091529192509088169063022c0d9f9060a401600060405180830381600087803b158015612d7f57600080fd5b505af1158015612d93573d6000803e3d6000fd5b5050505050505b5050612ea2565b826001600160a01b03166353c059a0846001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1291906154d1565b60405160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b03918216868316146004820152908716602482015260440160408051808303816000875af1158015612e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e9f919061541c565b50505b600101612a67565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015612f0557600080fd5b505af1158015612f19573d6000803e3d6000fd5b50612f559350506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016915084905083613a89565b5050565b60008060008060008087600081518110612f7557612f756154a2565b602002602001015190508a955060005b8a5181101561102e578a8181518110612fa057612fa06154a2565b60200260200101519250898181518110612fbc57612fbc6154a2565b60200260200101519450819550888160010181518110612fde57612fde6154a2565b602002602001015191508a518160010114613015578a8160010181518110613008576130086154a2565b6020026020010151613017565b875b9350846131d357600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561305f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061308391906155f1565b506001600160701b031691506001600160701b03169150836001600160a01b0316886001600160a01b03161015613142576130bf8983836144a9565b60405163022c0d9f60e01b8152600060048201819052602482018390526001600160a01b038981166044840152608060648401526084830191909152919a509086169063022c0d9f9060a401600060405180830381600087803b15801561312557600080fd5b505af1158015613139573d6000803e3d6000fd5b505050506131cc565b61314d8982846144a9565b60405163022c0d9f60e01b8152600481018290526000602482018190526001600160a01b038981166044840152608060648401526084830191909152919a509086169063022c0d9f9060a401600060405180830381600087803b1580156131b357600080fd5b505af11580156131c7573d6000803e3d6000fd5b505050505b50506132d2565b6000836001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015613213573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323791906154d1565b60405163029e02cd60e51b81526001600160a01b039182168583161460048201819052878316602483015292506000918291908716906353c059a09060440160408051808303816000875af1158015613294573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132b8919061541c565b9150915082156132ca578099506132ce565b8199505b5050505b600101612f85565b6060825167ffffffffffffffff8111156132f6576132f6614c0b565b60405190808252806020026020018201604052801561331f578160200160208202803683370190505b5090508181855181518110613336576133366154a2565b602090810291909101015283515b80156135bc5760008461335860018461546e565b81518110613368576133686154a2565b60200260200101519050600087600184613382919061546e565b81518110613392576133926154a2565b602002602001015190506000876001856133ac919061546e565b815181106133bc576133bc6154a2565b6020026020010151905081600014156134e357600080826001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015613410573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343491906155f1565b506001600160701b031691506001600160701b0316915088868151811061345d5761345d6154a2565b60200260200101516001600160a01b0316856001600160a01b0316111561348057905b6000878781518110613494576134946154a2565b602002602001015190506134b38383836145409092919063ffffffff16565b886134bf60018a61546e565b815181106134cf576134cf6154a2565b6020026020010181815250505050506135a6565b61357c818686815181106134f9576134f96154a2565b6020026020010151856001600160a01b0316846001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356d91906154d1565b6001600160a01b03161461103d565b508561358960018761546e565b81518110613599576135996154a2565b6020026020010181815250505b50505080806135b490615636565b915050613344565b50949350505050565b600080600080600080886000815181106135e1576135e16154a2565b6020026020010151905060005b8b5181101561102e578b8181518110613609576136096154a2565b602002602001015192508a8181518110613625576136256154a2565b60200260200101519450819550898160010181518110613647576136476154a2565b602002602001015191508b51816001011461367e578b8160010181518110613671576136716154a2565b6020026020010151613680565b875b93508461378d5788816001018151811061369c5761369c6154a2565b60200260200101519650816001600160a01b0316866001600160a01b031610156137405760405163022c0d9f60e01b8152600060048201819052602482018990526001600160a01b03868116604484015260806064840152608483019190915284169063022c0d9f9060a4015b600060405180830381600087803b15801561372357600080fd5b505af1158015613737573d6000803e3d6000fd5b5050505061388c565b60405163022c0d9f60e01b8152600481018890526000602482018190526001600160a01b03868116604484015260806064840152608483019190915284169063022c0d9f9060a401613709565b6000836001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f191906154d1565b60405163029e02cd60e51b81526001600160a01b039182168583161460048201819052878316602483015292506000918291908716906353c059a09060440160408051808303816000875af115801561384e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613872919061541c565b91509150821561388457809950613888565b8199505b5050505b6001016135ee565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146138e1576040519150601f19603f3d011682016040523d82523d6000602084013e6138e6565b606091505b505090508061146657604051631722e2bb60e21b81526001600160a01b038416600482015260248101839052604401610733565b600062ffffff8311156139405760405163163d8bab60e21b815260040160405180910390fd5b627fffff19830161395a81613954856145d2565b90614618565b949350505050565b600061396f848484614263565b90506001821b80613982576139826155a7565b8385091561398e576001015b9392505050565b60006139a2848484614216565b905081806139b2576139b26155a7565b6001841b85091561398e576001019392505050565b600082610120015162ffffff166127106139f485610140015162ffffff168561487190919063ffffffff16565b6139fe919061564d565b613a089190615456565b90508260e0015162ffffff168111613a205780613a26565b8260e001515b62ffffff16610100909301929092525050565b600080613a4584614888565b90506000613a5b82670de0b6b3a764000061546e565b905080600181613a6b858861564d565b613a759190615456565b613a7f919061546e565b6115a2919061566c565b801561146657604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b1790526000613af785836143de565b905060008151118015613b1b575080806020019051810190613b1991906155bd565b155b15613b395760405163197138bd60e11b815260040160405180910390fd5b5050505050565b60405163704037bd60e01b81526001600160a01b03848116600483015283811660248301526044820183905260009182917f0000000000000000000000000000000000000000000000000000000000000000169063704037bd90606401608060405180830381865afa158015613bba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bde919061568e565b6020015190506001600160a01b03811661395a57604051636b2471d160e11b81526001600160a01b0380871660048301528516602482015260448101849052606401610733565b600080876001600160a01b031663fba0ee64338a88886040518563ffffffff1660e01b8152600401613c5a9493929190615713565b600060405180830381600087803b158015613c7457600080fd5b505af1158015613c88573d6000803e3d6000fd5b5050604051630acd451d60e01b81526001600160a01b038b169250630acd451d9150613cbc90889088908890600401615751565b60408051808303816000875af1158015613cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cfe919061541c565b909250905086821080613d1057508581105b15613d46576040516318ccfb7760e11b815260048101889052602481018390526044810187905260648101829052608401610733565b965096945050505050565b606080836101a0013580421115613d845760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b613d92610140860186615790565b9050613da2610120870187615790565b905014158015613dd05750613dbb610160860186615790565b9050613dcb610120870187615790565b905014155b15613dee5760405163b91b4d4d60e01b815260040160405180910390fd5b62ffffff60e08601351180613e0a575062ffffff610100860135115b15613e395760405163197a55c760e11b815260e086013560048201526101008601356024820152604401610733565b6000846001600160a01b0316631b05b83e6040518163ffffffff1660e01b8152600401606060405180830381865afa158015613e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e9d91906152d7565b92505050808661010001358760e00135011080613ec457508560e001358661010001358201105b15613efa57604051637d50edab60e11b815260e08701356004820152610100870135602482015260448101829052606401610733565b613f08610120870187615790565b905067ffffffffffffffff811115613f2257613f22614c0b565b604051908082528060200260200182016040528015613f4b578160200160208202803683370190505b50935060005b8451811015613fde576000613f6a610120890189615790565b83818110613f7a57613f7a6154a2565b90506020020135830190506000811280613f96575062ffffff81115b15613fb7576040516370a82e6160e11b815260048101829052602401610733565b80868381518110613fca57613fca6154a2565b602090810291909101015250600101613f51565b506000806001600160a01b03871663714c8592876140006101408c018c615790565b61400e6101608e018e615790565b8e61018001602081019061402291906154ee565b6040518763ffffffff1660e01b8152600401614043969594939291906157da565b6000604051808303816000875af1158015614062573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261408a9190810190615832565b9650909250905060a08801358210806140a657508760c0013581105b156140e2576040516318ccfb7760e11b815260a089013560048201526024810183905260c0890135604482015260648101829052608401610733565b505050509250929050565b6000806140f9836145d2565b90506000614106826148b1565b61410f866148b1565b8161411c5761411c6155a7565b05628000000190506000811280614135575062ffffff81115b1561395a5760405163163d8bab60e21b815260040160405180910390fd5b8064ffffffffff8116811461417e576040516302dfd99760e01b815260048101839052602401610733565b919050565b60408051808201909152600080825260208201526141a0826149bc565b6001600160801b0316815260c08301516127109061ffff168302046001600160801b0316602082015292915050565b6000670de0b6b3a76400006001670de0b6b3a76400006141ee86614888565b6141f8908661564d565b6142029190615456565b61420c919061546e565b61201f919061566c565b600060ff83111561423d57604051630b72ecf560e41b815260048101849052602401610733565b83831b61010084900385901c614259866001871b8685856149e9565b9695505050505050565b600060ff82111561428a57604051630b72ecf560e41b815260048101839052602401610733565b6000806142978686614a9c565b91509150816000146142a95781841c92505b80156142e9576001841b81106142dc57604051633d90990f60e01b81526004810182905260248101859052604401610733565b836101000381901b830192505b50509392505050565b6000836143d35760405163e6a4390560e01b81526001600160a01b03848116600483015283811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063e6a4390590604401602060405180830381865afa158015614367573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438b91906154d1565b90506001600160a01b0381166143ce57604051636b2471d160e11b81526001600160a01b0380851660048301528316602482015260448101859052606401610733565b61398e565b61395a838386613b40565b6060600080846001600160a01b0316846040516143fb91906158d8565b6000604051808303816000865af19150503d8060008114614438576040519150601f19603f3d011682016040523d82523d6000602084013e61443d565b606091505b5091509150811561448257805115801561445f57506001600160a01b0385163b155b1561447d57604051635d97df8960e01b815260040160405180910390fd5b61395a565b80516144a157604051632407429160e01b815260040160405180910390fd5b805181602001fd5b6000836144c95760405163b229ed3360e01b815260040160405180910390fd5b8215806144d4575081155b156144f2576040516398c59a2960e01b815260040160405180910390fd5b6000614500856103e561564d565b9050600061450e848361564d565b905060008261451f876103e861564d565b6145299190615456565b9050614535818361566c565b979650505050505050565b6000836145605760405163b229ed3360e01b815260040160405180910390fd5b82158061456b575081155b15614589576040516398c59a2960e01b815260040160405180910390fd5b6000614595858561564d565b6145a1906103e861564d565b905060006145af868561546e565b6145bb906103e561564d565b90506145c7818361566c565b614259906001615456565b60008115806145e2575061271082115b15614603576040516374da1e1160e11b815260048101839052602401610733565b612710608083901b04600160801b0192915050565b60008080836146305750600160801b91506120229050565b50826000811215614642579015906000035b6210000081101561483257600160801b9250846001600160801b0381111561466c57911591600019045b600182161561467d5792830260801c925b800260801c60028216156146935792830260801c925b800260801c60048216156146a95792830260801c925b800260801c60088216156146bf5792830260801c925b800260801c60108216156146d55792830260801c925b800260801c60208216156146eb5792830260801c925b800260801c60408216156147015792830260801c925b8002608090811c908216156147185792830260801c925b800260801c61010082161561472f5792830260801c925b800260801c6102008216156147465792830260801c925b800260801c61040082161561475d5792830260801c925b800260801c6108008216156147745792830260801c925b800260801c61100082161561478b5792830260801c925b800260801c6120008216156147a25792830260801c925b800260801c6140008216156147b95792830260801c925b800260801c6180008216156147d05792830260801c925b800260801c620100008216156147e85792830260801c925b800260801c620200008216156148005792830260801c925b800260801c620400008216156148185792830260801c925b800260801c620800008216156148305792830260801c925b505b8261485a57604051630e9c7d6160e31b81526004810186905260248101859052604401610733565b8161486557826115a2565b6115a28360001961566c565b60008183116148825782820361201f565b50900390565b600061489382614abb565b8251602084015161ffff9182169116026402540be400020192915050565b600081600114156148c55750607f19919050565b816148e3576040516304c9fcb960e01b815260040160405180910390fd5b60019190911c9060006f80000000000000000000000000000000831061490b57506001614926565b5060001982600160fe1b81614922576149226155a7565b0492505b6000614935607f85901c614b0f565b707f80000000000000000000000000000000607f82901b16935060ff16905083811c6f8000000000000000000000000000000081146149b1576f400000000000000000000000000000005b60008113156149af57908002607f1c90600160801b82106149a7579384019360019190911c905b60011d614980565b505b50500260011b919050565b806001600160801b038116811461417e5760405163089f6cfb60e21b815260048101839052602401610733565b600081614a07578383816149ff576149ff6155a7565b0490506115a2565b838210614a315760405163656b542b60e11b81526004810183905260248101859052604401610733565b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b60008160a0015162ffffff1660001461417e576000826000015161ffff1683610100015162ffffff1602905060648360a0015162ffffff168283020260630181614b0757614b076155a7565b049392505050565b6000600160801b8210614b245750608090811c905b680100000000000000008210614b3c57604091821c91015b6401000000008210614b5057602091821c91015b620100008210614b6257601091821c91015b6101008210614b7357600891821c91015b60108210614b8357600491821c91015b60048210614b9357600291821c91015b6002821061417e57600101919050565b6001600160a01b0381168114614bb857600080fd5b50565b8015158114614bb857600080fd5b600080600060608486031215614bde57600080fd5b8335614be981614ba3565b9250602084013591506040840135614c0081614bbb565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b604051610180810167ffffffffffffffff81118282101715614c4557614c45614c0b565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614c7457614c74614c0b565b604052919050565b600067ffffffffffffffff821115614c9657614c96614c0b565b5060051b60200190565b600082601f830112614cb157600080fd5b81356020614cc6614cc183614c7c565b614c4b565b82815260059290921b84018101918181019086841115614ce557600080fd5b8286015b84811015614d005780358352918301918301614ce9565b509695505050505050565b803561417e81614ba3565b600082601f830112614d2757600080fd5b81356020614d37614cc183614c7c565b82815260059290921b84018101918181019086841115614d5657600080fd5b8286015b84811015614d00578035614d6d81614ba3565b8352918301918301614d5a565b60008060008060008060c08789031215614d9357600080fd5b8635955060208701359450604087013567ffffffffffffffff80821115614db957600080fd5b614dc58a838b01614ca0565b95506060890135915080821115614ddb57600080fd5b50614de889828a01614d16565b9350506080870135614df981614ba3565b8092505060a087013590509295509295509295565b600080600080600060a08688031215614e2657600080fd5b85359450602086013567ffffffffffffffff80821115614e4557600080fd5b614e5189838a01614ca0565b95506040880135915080821115614e6757600080fd5b50614e7488828901614d16565b9350506060860135614e8581614ba3565b949793965091946080013592915050565b600081518084526020808501945080840160005b83811015614ec657815187529582019590820190600101614eaa565b509495945050505050565b60208152600061201f6020830184614e96565b600080600060608486031215614ef957600080fd5b8335614f0481614ba3565b92506020840135614f1481614ba3565b929592945050506040919091013590565b62ffffff81168114614bb857600080fd5b61ffff81168114614bb857600080fd5b803561417e81614f36565b60008060008060808587031215614f6757600080fd5b8435614f7281614ba3565b93506020850135614f8281614ba3565b92506040850135614f9281614f25565b91506060850135614fa281614f36565b939692955090935050565b600080600080600080600080610100898b031215614fca57600080fd5b8835614fd581614ba3565b97506020890135614fe581614f36565b96506040890135955060608901359450608089013567ffffffffffffffff8082111561501057600080fd5b61501c8c838d01614ca0565b955060a08b013591508082111561503257600080fd5b5061503f8b828c01614ca0565b93505060c089013561505081614ba3565b8092505060e089013590509295985092959890939650565b60008060008060008060008060006101208a8c03121561508757600080fd5b893561509281614ba3565b985060208a01356150a281614ba3565b97506150b060408b01614f46565b965060608a0135955060808a0135945060a08a013567ffffffffffffffff808211156150db57600080fd5b6150e78d838e01614ca0565b955060c08c01359150808211156150fd57600080fd5b5061510a8c828d01614ca0565b93505061511960e08b01614d0b565b91506101008a013590509295985092959850929598565b6000806040838503121561514357600080fd5b823561514e81614ba3565b9150602083013561515e81614f25565b809150509250929050565b60006020828403121561517b57600080fd5b813567ffffffffffffffff81111561519257600080fd5b82016101c0818503121561398e57600080fd5b6040815260006151b86040830185614e96565b82810360208401526115a28185614e96565b60008083601f8401126151dc57600080fd5b50813567ffffffffffffffff8111156151f457600080fd5b6020830191508360208260051b850101111561520f57600080fd5b9250929050565b6000806000806000806080878903121561522f57600080fd5b863561523a81614ba3565b9550602087013561524a81614ba3565b9450604087013567ffffffffffffffff8082111561526757600080fd5b6152738a838b016151ca565b9096509450606089013591508082111561528c57600080fd5b5061529989828a016151ca565b979a9699509497509295939492505050565b600080604083850312156152be57600080fd5b82356152c981614ba3565b946020939093013593505050565b6000806000606084860312156152ec57600080fd5b8351925060208401519150604084015190509250925092565b805161417e81614f36565b805161417e81614f25565b805164ffffffffff8116811461417e57600080fd5b6000610180828403121561534357600080fd5b61534b614c21565b61535483615305565b815261536260208401615305565b602082015261537360408401615305565b604082015261538460608401615305565b606082015261539560808401615305565b60808201526153a660a08401615310565b60a08201526153b760c08401615305565b60c08201526153c860e08401615310565b60e08201526101006153db818501615310565b908201526101206153ed848201615310565b908201526101406153ff848201615310565b9082015261016061541184820161531b565b908201529392505050565b6000806040838503121561542f57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b6000821982111561546957615469615440565b500190565b60008282101561548057615480615440565b500390565b60006020828403121561549757600080fd5b815161398e81614f25565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156154ca57600080fd5b5051919050565b6000602082840312156154e357600080fd5b815161398e81614ba3565b60006020828403121561550057600080fd5b813561398e81614ba3565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561553d57600080fd5b8260051b8083602087013760009401602001938452509192915050565b60006001600160a01b0380891683528088166020840152506080604083015261558760808301868861550b565b828103606084015261559a81858761550b565b9998505050505050505050565b634e487b7160e01b600052601260045260246000fd5b6000602082840312156155cf57600080fd5b815161398e81614bbb565b80516001600160701b038116811461417e57600080fd5b60008060006060848603121561560657600080fd5b61560f846155da565b925061561d602085016155da565b9150604084015163ffffffff81168114614c0057600080fd5b60008161564557615645615440565b506000190190565b600081600019048311821515161561566757615667615440565b500290565b60008261568957634e487b7160e01b600052601260045260246000fd5b500490565b6000608082840312156156a057600080fd5b6040516080810181811067ffffffffffffffff821117156156c3576156c3614c0b565b60405282516156d181614f36565b815260208301516156e181614ba3565b602082015260408301516156f481614bbb565b6040820152606083015161570781614bbb565b60608201529392505050565b60006001600160a01b0380871683528086166020840152506080604083015261573f6080830185614e96565b82810360608401526145358185614e96565b6060815260006157646060830186614e96565b82810360208401526157768186614e96565b9150506001600160a01b0383166040830152949350505050565b6000808335601e198436030181126157a757600080fd5b83018035915067ffffffffffffffff8211156157c257600080fd5b6020019150600581901b360382131561520f57600080fd5b6080815260006157ed6080830189614e96565b828103602084015261580081888a61550b565b9050828103604084015261581581868861550b565b9150506001600160a01b0383166060830152979650505050505050565b60008060006060848603121561584757600080fd5b835192506020808501519250604085015167ffffffffffffffff81111561586d57600080fd5b8501601f8101871361587e57600080fd5b805161588c614cc182614c7c565b81815260059190911b820183019083810190898311156158ab57600080fd5b928401925b828410156158c9578351825292840192908401906158b0565b80955050505050509250925092565b6000825160005b818110156158f957602081860181015185830152016158df565b81811115615908576000828501525b50919091019291505056fea26469706673582212206c05eee95a8862d596ac1d3ea3c231cfdc4a1453951fddb1394e8ead624c932964736f6c634300080a00330000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982000000000000000000000000ae4ec9901c3076d0ddbe76a520f9e90a6227acb700000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1
Deployed Bytecode
0x6080604052600436106101845760003560e01c80636d3420ed116100d6578063d0e380f21161007f578063ea8f43d811610059578063ea8f43d8146104c9578063f96fe925146104dc578063fb321c701461051057600080fd5b8063d0e380f21461045b578063e324a3e41461047b578063e9361c08146104a957600080fd5b8063bcb1c957116100b0578063bcb1c957146103e7578063c22159b614610407578063c45a01551461042757600080fd5b80636d3420ed146103875780639a17e820146103a7578063a7b856d3146103c757600080fd5b806342f564a01161013857806362c067671161011257806362c0676714610327578063659ac74b146103475780636d0ff4951461036757600080fd5b806342f564a0146102d4578063440830bd146102f45780635bdd4b7c1461030757600080fd5b80632004b724116101695780632004b7241461025e578063212a1d9414610293578063264bb94e146102c157600080fd5b8063117be4c2146101d95780631bd6dfe11461022a57600080fd5b366101d457336001600160a01b037f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab116146101d2576040516310d0bef760e31b815260040160405180910390fd5b005b600080fd5b3480156101e557600080fd5b5061020d7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab181565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561023657600080fd5b5061020d7f000000000000000000000000ae4ec9901c3076d0ddbe76a520f9e90a6227acb781565b34801561026a57600080fd5b5061027e610279366004614bc9565b610530565b60408051928352602083019190915201610221565b34801561029f57600080fd5b506102b36102ae366004614d7a565b610839565b604051908152602001610221565b6102b36102cf366004614e0e565b610a57565b6102e76102e2366004614e0e565b610bd1565b6040516102219190614ed1565b6102b3610302366004614e0e565b610df8565b34801561031357600080fd5b5061027e610322366004614bc9565b61103d565b34801561033357600080fd5b506101d2610342366004614ee4565b61138e565b34801561035357600080fd5b5061020d610362366004614f51565b6114f4565b34801561037357600080fd5b506102b3610382366004614d7a565b6115ab565b34801561039357600080fd5b506102e76103a2366004614d7a565b61169e565b3480156103b357600080fd5b506102b36103c2366004614d7a565b6118dc565b3480156103d357600080fd5b506102e76103e2366004614d7a565b611bcc565b3480156103f357600080fd5b5061027e610402366004614fad565b611cdb565b34801561041357600080fd5b5061027e610422366004615068565b611eb3565b34801561043357600080fd5b5061020d7f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c298281565b34801561046757600080fd5b506102b3610476366004615130565b611fa7565b34801561048757600080fd5b5061049b610496366004615169565b612028565b6040516102219291906151a5565b3480156104b557600080fd5b506101d26104c4366004615216565b61214f565b61049b6104d7366004615169565b612272565b3480156104e857600080fd5b506104fc6104f73660046152ab565b612482565b60405162ffffff9091168152602001610221565b34801561051c57600080fd5b506102b361052b366004614d7a565b6124f5565b6000806000856001600160a01b0316631b05b83e6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610573573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059791906152d7565b925050506000866001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa1580156105dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106009190615330565b905061060c81836126a8565b6040805160808101825260008082526020820181905291810182905260608101919091525b604051630157d2d160e31b815262ffffff8416600482015260009081906001600160a01b038b1690630abe9688906024016040805180830381865afa15801561067e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a2919061541c565b604080516080810182526001600160701b03938416808252939092166020830152600090820181905260608201529350151591508190506106ef575060208101516001600160701b031615155b1561078557600080806107058486888c8e612765565b919450925090506001600160701b0383111561073c57604051633d9af4e160e11b8152600481018790526024015b60405180910390fd5b8051610751906001600160801b031684615456565b61075b908b61546e565b8151909a50610773906001600160801b031688615456565b965061077f8289615456565b97505050505b861561080c57604051638f919a8360e01b815262ffffff8416600482015286151560248201526001600160a01b03891690638f919a8390604401602060405180830381865afa1580156107dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108009190615485565b62ffffff169250610631565b861561082e576040516349cda5cd60e11b815260048101889052602401610733565b505050935093915050565b600081804211156108665760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610885575080518251610882906001615456565b14155b156108a35760405163b91b4d4d60e01b815260040160405180910390fd5b60006108af8989612889565b90506000888251815181106108c6576108c66154a2565b60209081029190910101516040516370a0823160e01b81526001600160a01b038a811660048301529192506000918316906370a0823190602401602060405180830381865afa15801561091d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094191906154b8565b9050610997338460008151811061095a5761095a6154a2565b60200260200101518f8d600081518110610976576109766154a2565b60200260200101516001600160a01b031661297e909392919063ffffffff16565b6109a3838c8c8c612a3f565b6040516370a0823160e01b81526001600160a01b038a811660048301528291908416906370a0823190602401602060405180830381865afa1580156109ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1091906154b8565b610a1a919061546e565b9650868c1115610a47576040516313fab00360e21b8152600481018d905260248101889052604401610733565b5050505050509695505050505050565b60008180421115610a845760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610aa3575080518251610aa0906001615456565b14155b15610ac15760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031687600081518110610afe57610afe6154a2565b60200260200101516001600160a01b031614610b585786600081518110610b2757610b276154a2565b602002602001015160405163cfec0e0160e01b815260040161073391906001600160a01b0391909116815260200190565b6000610b648989612889565b9050610b8a81600081518110610b7c57610b7c6154a2565b602002602001015134612eaa565b610b9734828b8b8b612f59565b9450848a1115610bc4576040516313fab00360e21b8152600481018b905260248101869052604401610733565b5050505095945050505050565b60608180421115610bfe5760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610c1d575080518251610c1a906001615456565b14155b15610c3b5760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031687600081518110610c7857610c786154a2565b60200260200101516001600160a01b031614610ca15786600081518110610b2757610b276154a2565b6000610cad8989612889565b9050610cbb89828a8d6132da565b94503485600081518110610cd157610cd16154a2565b60200260200101511115610d1f573485600081518110610cf357610cf36154a2565b602002602001015160405163194ee21960e31b8152600401610733929190918252602082015260400190565b610d5d81600081518110610d3557610d356154a2565b602002602001015186600081518110610d5057610d506154a2565b6020026020010151612eaa565b6000610d6c828b8b898c6135c5565b90508a811015610d99576040516313fab00360e21b8152600481018c905260248101829052604401610733565b85600081518110610dac57610dac6154a2565b6020026020010151341115610dea57610dea3387600081518110610dd257610dd26154a2565b602002602001015134610de5919061546e565b613894565b505050505095945050505050565b60008180421115610e255760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480610e44575080518251610e41906001615456565b14155b15610e625760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031687600081518110610e9f57610e9f6154a2565b60200260200101516001600160a01b031614610ec85786600081518110610b2757610b276154a2565b6000610ed48989612889565b9050600088825181518110610eeb57610eeb6154a2565b60209081029190910101516040516370a0823160e01b81526001600160a01b038a811660048301529192506000918316906370a0823190602401602060405180830381865afa158015610f42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f6691906154b8565b9050610f7e83600081518110610b7c57610b7c6154a2565b610f8a838c8c8c612a3f565b6040516370a0823160e01b81526001600160a01b038a811660048301528291908416906370a0823190602401602060405180830381865afa158015610fd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff791906154b8565b611001919061546e565b9650868c111561102e576040516313fab00360e21b8152600481018d905260248101889052604401610733565b50505050505095945050505050565b6000806000806000876001600160a01b0316631b05b83e6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a791906152d7565b92509250925086600014806110c85750856110c4578287116110c8565b8187115b156110fd5786866110d957836110db565b825b604051637c40a4df60e01b815260048101929092526024820152604401610733565b6000886001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa15801561113e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111629190615330565b905061116e81836126a8565b60008060005b604051630157d2d160e31b815262ffffff8616600482015260009081906001600160a01b038f1690630abe9688906024016040805180830381865afa1580156111c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e5919061541c565b915091508b6111f457816111f6565b805b92505050600061120e86866000015161ffff1661391a565b905081156112cd57818c1015611224578b611226565b815b935060008b6112405761123b82866080613962565b61124c565b61124c85608084613995565b905061125886886139c7565b60006112648783613a39565b90506112708183615456565b94506001600160701b036112848587615456565b11156112a65760405163ec44eb8960e01b815260048101899052602401610733565b6112b0858d615456565b9b506112bc818c615456565b9a506112c8868f61546e565b9d5050505b8b1561135457604051638f919a8360e01b815262ffffff871660048201528b151560248201526001600160a01b038e1690638f919a8390604401602060405180830381865afa158015611324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113489190615485565b62ffffff16955061135a565b50611360565b50611174565b8a1561137f576040516330b1335760e21b815260040160405180910390fd5b50505050505050935093915050565b7f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c29826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061141091906154d1565b6001600160a01b0316336001600160a01b03161461144157604051635d9515b960e11b815260040160405180910390fd5b6001600160a01b03831661146b5760001981141561145c5750475b6114668282613894565b505050565b6000198114156114e0576040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156114b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114dd91906154b8565b90505b6114666001600160a01b0384168383613a89565b60405163659ac74b60e01b81526001600160a01b038581166004830152848116602483015262ffffff8416604483015261ffff831660648301526000917f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c29829091169063659ac74b906084016020604051808303816000875af115801561157e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a291906154d1565b95945050505050565b600081804211156115d85760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b85858151600014806115f75750805182516115f4906001615456565b14155b156116155760405163b91b4d4d60e01b815260040160405180910390fd5b60006116218989612889565b9050611656338260008151811061163a5761163a6154a2565b60200260200101518d8b600081518110610976576109766154a2565b6116638b828b8b8b612f59565b9450848a1115611690576040516313fab00360e21b8152600481018b905260248101869052604401610733565b505050509695505050505050565b606081804211156116cb5760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b85858151600014806116ea5750805182516116e7906001615456565b14155b156117085760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031687895181518110611745576117456154a2565b60200260200101516001600160a01b03161461176e5786885181518110610b2757610b276154a2565b600061177a8989612889565b905061178889828a8e6132da565b9450898560008151811061179e5761179e6154a2565b602002602001015111156117c0578985600081518110610cf357610cf36154a2565b61180d33826000815181106117d7576117d76154a2565b6020026020010151876000815181106117f2576117f26154a2565b60200260200101518b600081518110610976576109766154a2565b600061181c828b8b89306135c5565b90508b811015611849576040516313fab00360e21b8152600481018d905260248101829052604401610733565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156118ab57600080fd5b505af11580156118bf573d6000803e3d6000fd5b505050506118cd8882613894565b50505050509695505050505050565b600081804211156119095760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480611928575080518251611925906001615456565b14155b156119465760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031687895181518110611983576119836154a2565b60200260200101516001600160a01b0316146119ac5786885181518110610b2757610b276154a2565b60006119b88989612889565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab116906370a0823190602401602060405180830381865afa158015611a22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4691906154b8565b9050611a7b3383600081518110611a5f57611a5f6154a2565b60200260200101518e8c600081518110610976576109766154a2565b611a87828b8b30612a3f565b6040516370a0823160e01b815230600482015281907f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b0316906370a0823190602401602060405180830381865afa158015611aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b1191906154b8565b611b1b919061546e565b9550858b1115611b48576040516313fab00360e21b8152600481018c905260248101879052604401610733565b604051632e1a7d4d60e01b8152600481018790527f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611baa57600080fd5b505af1158015611bbe573d6000803e3d6000fd5b505050506118cd8887613894565b60608180421115611bf95760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b8585815160001480611c18575080518251611c15906001615456565b14155b15611c365760405163b91b4d4d60e01b815260040160405180910390fd5b6000611c428989612889565b9050611c5089828a8e6132da565b94508985600081518110611c6657611c666154a2565b60200260200101511115611c88578985600081518110610cf357610cf36154a2565b611c9f33826000815181106117d7576117d76154a2565b6000611cae828b8b898c6135c5565b90508b8110156118cd576040516313fab00360e21b8152600481018d905260248101829052604401610733565b6000808280421115611d095760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b6000611d3a8c7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab18d61ffff16613b40565b90506000816001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da091906154d1565b6001600160a01b03167f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031614905080611ddf579899985b600080611df0848e8e8e8e30613c25565b9150915082611e00578082611e03565b81815b9097509550611e1f9150506001600160a01b038e168887613a89565b604051632e1a7d4d60e01b8152600481018590527f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611e8157600080fd5b505af1158015611e95573d6000803e3d6000fd5b50505050611ea38785613894565b5050509850989650505050505050565b6000808280421115611ee15760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b6000611ef28d8d8d61ffff16613b40565b90506000816001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5891906154d1565b6001600160a01b03168e6001600160a01b0316141590508015611f79579899985b611f87828c8c8c8c8c613c25565b90955093508015611f96579293925b505050995099975050505050505050565b600061201f8262ffffff16846001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa158015611ff1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120159190615330565b5161ffff1661391a565b90505b92915050565b606080600061205761203d60208601866154ee565b61204d60408701602088016154ee565b8660400135613b40565b9050806001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bb91906154d1565b6001600160a01b03166120d160208601866154ee565b6001600160a01b0316146120f85760405163b33f8ab960e01b815260040160405180910390fd5b6121203382606087013561210f60208901896154ee565b6001600160a01b031692919061297e565b61213a3382608087013561210f6040890160208a016154ee565b6121448482613d51565b909590945092505050565b7f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c29826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d191906154d1565b6001600160a01b0316336001600160a01b03161461220257604051635d9515b960e11b815260040160405180910390fd5b604051633ee83b9960e21b81526001600160a01b0387169063fba0ee64906122389030908990899089908990899060040161555a565b600060405180830381600087803b15801561225257600080fd5b505af1158015612266573d6000803e3d6000fd5b50505050505050505050565b606080600061228761203d60208601866154ee565b9050806001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122eb91906154d1565b6001600160a01b031661230160208601866154ee565b6001600160a01b0316146123285760405163b33f8ab960e01b815260040160405180910390fd5b6001600160a01b037f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab11661235f60208601866154ee565b6001600160a01b03161480156123785750348460600135145b156123a6576123878134612eaa565b6123a13382608087013561210f6040890160208a016154ee565b61213a565b6001600160a01b037f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1166123e060408601602087016154ee565b6001600160a01b03161480156123f95750348460800135145b1561241f576124153382606087013561210f60208901896154ee565b6123a18134612eaa565b61242c60208501856154ee565b61243c60408601602087016154ee565b60405163959ceb2b60e01b81526001600160a01b03928316600482015291166024820152606085013560448201526080850135606482015234608482015260a401610733565b600061201f82846001600160a01b03166398c7adf36040518163ffffffff1660e01b815260040161018060405180830381865afa1580156124c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124eb9190615330565b5161ffff166140ed565b600081804211156125225760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b858581516000148061254157508051825161253e906001615456565b14155b1561255f5760405163b91b4d4d60e01b815260040160405180910390fd5b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b03168789518151811061259c5761259c6154a2565b60200260200101516001600160a01b0316146125c55786885181518110610b2757610b276154a2565b60006125d18989612889565b90506125ea338260008151811061163a5761163a6154a2565b6125f78b828b8b30612f59565b9450848a1115612624576040516313fab00360e21b8152600481018b905260248101869052604401610733565b604051632e1a7d4d60e01b8152600481018690527f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561268657600080fd5b505af115801561269a573d6000803e3d6000fd5b505050506116908786613894565b600082610160015164ffffffffff16426126c2919061546e565b9050826040015161ffff16811015806126e5575061016083015164ffffffffff16155b156127455762ffffff8216610140840152606083015161ffff1681101561273c5761271083610100015162ffffff16846080015161ffff16028161272b5761272b6155a7565b0462ffffff16610120840152612745565b60006101208401525b61274e42614153565b64ffffffffff1661016084015261146683836139c7565b60408051808201909152600080825260208201819052908190600061279287896000015161ffff1661391a565b905060008087156127c05760208b01516001600160701b031691506127b982608085613995565b90506127dc565b8a516001600160701b031691506127d983836080613962565b90505b6127e68a8a6139c7565b6127fa6127f38b83613a39565b8b90614183565b80519094508790612814906001600160801b031683615456565b116128245780955081945061287b565b6128316127f38b896141cf565b8051909450612849906001600160801b03168861546e565b9550876128615761285c86608085614216565b61286d565b61286d83876080614263565b94508185111561287b578194505b505050955095509592505050565b6060825167ffffffffffffffff8111156128a5576128a5614c0b565b6040519080825280602002602001820160405280156128ce578160200160208202803683370190505b509050600080836000815181106128e7576128e76154a2565b6020026020010151905060005b835181101561297557819250848160010181518110612915576129156154a2565b60200260200101519150612943868281518110612934576129346154a2565b602002602001015184846142f2565b848281518110612955576129556154a2565b6001600160a01b03909216602092830291909101909101526001016128f4565b50505092915050565b8015612a3957604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b17905260006129f486836143de565b905060008151118015612a18575080806020019051810190612a1691906155bd565b155b15612a365760405163197138bd60e11b815260040160405180910390fd5b50505b50505050565b600080600080600086600081518110612a5a57612a5a6154a2565b6020026020010151905060005b895181101561226657898181518110612a8257612a826154a2565b60200260200101519250888181518110612a9e57612a9e6154a2565b60200260200101519450819550878160010181518110612ac057612ac06154a2565b6020026020010151915089518160010114612af757898160010181518110612aea57612aea6154a2565b6020026020010151612af9565b865b935084612da157600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015612b41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6591906155f1565b506001600160701b031691506001600160701b03169150836001600160a01b0316886001600160a01b03161015612c9a576040516370a0823160e01b81526001600160a01b03868116600483015260009184918b16906370a0823190602401602060405180830381865afa158015612be1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0591906154b8565b0390506000612c158285856144a9565b60405163022c0d9f60e01b8152600060048201819052602482018390526001600160a01b038b811660448401526080606484015260848301919091529192509088169063022c0d9f9060a401600060405180830381600087803b158015612c7b57600080fd5b505af1158015612c8f573d6000803e3d6000fd5b505050505050612d9a565b6040516370a0823160e01b81526001600160a01b03868116600483015260009183918b16906370a0823190602401602060405180830381865afa158015612ce5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0991906154b8565b0390506000612d198284866144a9565b60405163022c0d9f60e01b8152600481018290526000602482018190526001600160a01b038b811660448401526080606484015260848301919091529192509088169063022c0d9f9060a401600060405180830381600087803b158015612d7f57600080fd5b505af1158015612d93573d6000803e3d6000fd5b5050505050505b5050612ea2565b826001600160a01b03166353c059a0846001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1291906154d1565b60405160e083901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b03918216868316146004820152908716602482015260440160408051808303816000875af1158015612e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e9f919061541c565b50505b600101612a67565b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015612f0557600080fd5b505af1158015612f19573d6000803e3d6000fd5b50612f559350506001600160a01b037f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab116915084905083613a89565b5050565b60008060008060008087600081518110612f7557612f756154a2565b602002602001015190508a955060005b8a5181101561102e578a8181518110612fa057612fa06154a2565b60200260200101519250898181518110612fbc57612fbc6154a2565b60200260200101519450819550888160010181518110612fde57612fde6154a2565b602002602001015191508a518160010114613015578a8160010181518110613008576130086154a2565b6020026020010151613017565b875b9350846131d357600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561305f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061308391906155f1565b506001600160701b031691506001600160701b03169150836001600160a01b0316886001600160a01b03161015613142576130bf8983836144a9565b60405163022c0d9f60e01b8152600060048201819052602482018390526001600160a01b038981166044840152608060648401526084830191909152919a509086169063022c0d9f9060a401600060405180830381600087803b15801561312557600080fd5b505af1158015613139573d6000803e3d6000fd5b505050506131cc565b61314d8982846144a9565b60405163022c0d9f60e01b8152600481018290526000602482018190526001600160a01b038981166044840152608060648401526084830191909152919a509086169063022c0d9f9060a401600060405180830381600087803b1580156131b357600080fd5b505af11580156131c7573d6000803e3d6000fd5b505050505b50506132d2565b6000836001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa158015613213573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323791906154d1565b60405163029e02cd60e51b81526001600160a01b039182168583161460048201819052878316602483015292506000918291908716906353c059a09060440160408051808303816000875af1158015613294573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132b8919061541c565b9150915082156132ca578099506132ce565b8199505b5050505b600101612f85565b6060825167ffffffffffffffff8111156132f6576132f6614c0b565b60405190808252806020026020018201604052801561331f578160200160208202803683370190505b5090508181855181518110613336576133366154a2565b602090810291909101015283515b80156135bc5760008461335860018461546e565b81518110613368576133686154a2565b60200260200101519050600087600184613382919061546e565b81518110613392576133926154a2565b602002602001015190506000876001856133ac919061546e565b815181106133bc576133bc6154a2565b6020026020010151905081600014156134e357600080826001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015613410573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343491906155f1565b506001600160701b031691506001600160701b0316915088868151811061345d5761345d6154a2565b60200260200101516001600160a01b0316856001600160a01b0316111561348057905b6000878781518110613494576134946154a2565b602002602001015190506134b38383836145409092919063ffffffff16565b886134bf60018a61546e565b815181106134cf576134cf6154a2565b6020026020010181815250505050506135a6565b61357c818686815181106134f9576134f96154a2565b6020026020010151856001600160a01b0316846001600160a01b03166316dc165b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356d91906154d1565b6001600160a01b03161461103d565b508561358960018761546e565b81518110613599576135996154a2565b6020026020010181815250505b50505080806135b490615636565b915050613344565b50949350505050565b600080600080600080886000815181106135e1576135e16154a2565b6020026020010151905060005b8b5181101561102e578b8181518110613609576136096154a2565b602002602001015192508a8181518110613625576136256154a2565b60200260200101519450819550898160010181518110613647576136476154a2565b602002602001015191508b51816001011461367e578b8160010181518110613671576136716154a2565b6020026020010151613680565b875b93508461378d5788816001018151811061369c5761369c6154a2565b60200260200101519650816001600160a01b0316866001600160a01b031610156137405760405163022c0d9f60e01b8152600060048201819052602482018990526001600160a01b03868116604484015260806064840152608483019190915284169063022c0d9f9060a4015b600060405180830381600087803b15801561372357600080fd5b505af1158015613737573d6000803e3d6000fd5b5050505061388c565b60405163022c0d9f60e01b8152600481018890526000602482018190526001600160a01b03868116604484015260806064840152608483019190915284169063022c0d9f9060a401613709565b6000836001600160a01b031663b7d19fc46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f191906154d1565b60405163029e02cd60e51b81526001600160a01b039182168583161460048201819052878316602483015292506000918291908716906353c059a09060440160408051808303816000875af115801561384e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613872919061541c565b91509150821561388457809950613888565b8199505b5050505b6001016135ee565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146138e1576040519150601f19603f3d011682016040523d82523d6000602084013e6138e6565b606091505b505090508061146657604051631722e2bb60e21b81526001600160a01b038416600482015260248101839052604401610733565b600062ffffff8311156139405760405163163d8bab60e21b815260040160405180910390fd5b627fffff19830161395a81613954856145d2565b90614618565b949350505050565b600061396f848484614263565b90506001821b80613982576139826155a7565b8385091561398e576001015b9392505050565b60006139a2848484614216565b905081806139b2576139b26155a7565b6001841b85091561398e576001019392505050565b600082610120015162ffffff166127106139f485610140015162ffffff168561487190919063ffffffff16565b6139fe919061564d565b613a089190615456565b90508260e0015162ffffff168111613a205780613a26565b8260e001515b62ffffff16610100909301929092525050565b600080613a4584614888565b90506000613a5b82670de0b6b3a764000061546e565b905080600181613a6b858861564d565b613a759190615456565b613a7f919061546e565b6115a2919061566c565b801561146657604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b1790526000613af785836143de565b905060008151118015613b1b575080806020019051810190613b1991906155bd565b155b15613b395760405163197138bd60e11b815260040160405180910390fd5b5050505050565b60405163704037bd60e01b81526001600160a01b03848116600483015283811660248301526044820183905260009182917f0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982169063704037bd90606401608060405180830381865afa158015613bba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bde919061568e565b6020015190506001600160a01b03811661395a57604051636b2471d160e11b81526001600160a01b0380871660048301528516602482015260448101849052606401610733565b600080876001600160a01b031663fba0ee64338a88886040518563ffffffff1660e01b8152600401613c5a9493929190615713565b600060405180830381600087803b158015613c7457600080fd5b505af1158015613c88573d6000803e3d6000fd5b5050604051630acd451d60e01b81526001600160a01b038b169250630acd451d9150613cbc90889088908890600401615751565b60408051808303816000875af1158015613cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cfe919061541c565b909250905086821080613d1057508581105b15613d46576040516318ccfb7760e11b815260048101889052602481018390526044810187905260648101829052608401610733565b965096945050505050565b606080836101a0013580421115613d845760405163dae7ca7d60e01b815260048101829052426024820152604401610733565b613d92610140860186615790565b9050613da2610120870187615790565b905014158015613dd05750613dbb610160860186615790565b9050613dcb610120870187615790565b905014155b15613dee5760405163b91b4d4d60e01b815260040160405180910390fd5b62ffffff60e08601351180613e0a575062ffffff610100860135115b15613e395760405163197a55c760e11b815260e086013560048201526101008601356024820152604401610733565b6000846001600160a01b0316631b05b83e6040518163ffffffff1660e01b8152600401606060405180830381865afa158015613e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e9d91906152d7565b92505050808661010001358760e00135011080613ec457508560e001358661010001358201105b15613efa57604051637d50edab60e11b815260e08701356004820152610100870135602482015260448101829052606401610733565b613f08610120870187615790565b905067ffffffffffffffff811115613f2257613f22614c0b565b604051908082528060200260200182016040528015613f4b578160200160208202803683370190505b50935060005b8451811015613fde576000613f6a610120890189615790565b83818110613f7a57613f7a6154a2565b90506020020135830190506000811280613f96575062ffffff81115b15613fb7576040516370a82e6160e11b815260048101829052602401610733565b80868381518110613fca57613fca6154a2565b602090810291909101015250600101613f51565b506000806001600160a01b03871663714c8592876140006101408c018c615790565b61400e6101608e018e615790565b8e61018001602081019061402291906154ee565b6040518763ffffffff1660e01b8152600401614043969594939291906157da565b6000604051808303816000875af1158015614062573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261408a9190810190615832565b9650909250905060a08801358210806140a657508760c0013581105b156140e2576040516318ccfb7760e11b815260a089013560048201526024810183905260c0890135604482015260648101829052608401610733565b505050509250929050565b6000806140f9836145d2565b90506000614106826148b1565b61410f866148b1565b8161411c5761411c6155a7565b05628000000190506000811280614135575062ffffff81115b1561395a5760405163163d8bab60e21b815260040160405180910390fd5b8064ffffffffff8116811461417e576040516302dfd99760e01b815260048101839052602401610733565b919050565b60408051808201909152600080825260208201526141a0826149bc565b6001600160801b0316815260c08301516127109061ffff168302046001600160801b0316602082015292915050565b6000670de0b6b3a76400006001670de0b6b3a76400006141ee86614888565b6141f8908661564d565b6142029190615456565b61420c919061546e565b61201f919061566c565b600060ff83111561423d57604051630b72ecf560e41b815260048101849052602401610733565b83831b61010084900385901c614259866001871b8685856149e9565b9695505050505050565b600060ff82111561428a57604051630b72ecf560e41b815260048101839052602401610733565b6000806142978686614a9c565b91509150816000146142a95781841c92505b80156142e9576001841b81106142dc57604051633d90990f60e01b81526004810182905260248101859052604401610733565b836101000381901b830192505b50509392505050565b6000836143d35760405163e6a4390560e01b81526001600160a01b03848116600483015283811660248301527f000000000000000000000000ae4ec9901c3076d0ddbe76a520f9e90a6227acb7169063e6a4390590604401602060405180830381865afa158015614367573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438b91906154d1565b90506001600160a01b0381166143ce57604051636b2471d160e11b81526001600160a01b0380851660048301528316602482015260448101859052606401610733565b61398e565b61395a838386613b40565b6060600080846001600160a01b0316846040516143fb91906158d8565b6000604051808303816000865af19150503d8060008114614438576040519150601f19603f3d011682016040523d82523d6000602084013e61443d565b606091505b5091509150811561448257805115801561445f57506001600160a01b0385163b155b1561447d57604051635d97df8960e01b815260040160405180910390fd5b61395a565b80516144a157604051632407429160e01b815260040160405180910390fd5b805181602001fd5b6000836144c95760405163b229ed3360e01b815260040160405180910390fd5b8215806144d4575081155b156144f2576040516398c59a2960e01b815260040160405180910390fd5b6000614500856103e561564d565b9050600061450e848361564d565b905060008261451f876103e861564d565b6145299190615456565b9050614535818361566c565b979650505050505050565b6000836145605760405163b229ed3360e01b815260040160405180910390fd5b82158061456b575081155b15614589576040516398c59a2960e01b815260040160405180910390fd5b6000614595858561564d565b6145a1906103e861564d565b905060006145af868561546e565b6145bb906103e561564d565b90506145c7818361566c565b614259906001615456565b60008115806145e2575061271082115b15614603576040516374da1e1160e11b815260048101839052602401610733565b612710608083901b04600160801b0192915050565b60008080836146305750600160801b91506120229050565b50826000811215614642579015906000035b6210000081101561483257600160801b9250846001600160801b0381111561466c57911591600019045b600182161561467d5792830260801c925b800260801c60028216156146935792830260801c925b800260801c60048216156146a95792830260801c925b800260801c60088216156146bf5792830260801c925b800260801c60108216156146d55792830260801c925b800260801c60208216156146eb5792830260801c925b800260801c60408216156147015792830260801c925b8002608090811c908216156147185792830260801c925b800260801c61010082161561472f5792830260801c925b800260801c6102008216156147465792830260801c925b800260801c61040082161561475d5792830260801c925b800260801c6108008216156147745792830260801c925b800260801c61100082161561478b5792830260801c925b800260801c6120008216156147a25792830260801c925b800260801c6140008216156147b95792830260801c925b800260801c6180008216156147d05792830260801c925b800260801c620100008216156147e85792830260801c925b800260801c620200008216156148005792830260801c925b800260801c620400008216156148185792830260801c925b800260801c620800008216156148305792830260801c925b505b8261485a57604051630e9c7d6160e31b81526004810186905260248101859052604401610733565b8161486557826115a2565b6115a28360001961566c565b60008183116148825782820361201f565b50900390565b600061489382614abb565b8251602084015161ffff9182169116026402540be400020192915050565b600081600114156148c55750607f19919050565b816148e3576040516304c9fcb960e01b815260040160405180910390fd5b60019190911c9060006f80000000000000000000000000000000831061490b57506001614926565b5060001982600160fe1b81614922576149226155a7565b0492505b6000614935607f85901c614b0f565b707f80000000000000000000000000000000607f82901b16935060ff16905083811c6f8000000000000000000000000000000081146149b1576f400000000000000000000000000000005b60008113156149af57908002607f1c90600160801b82106149a7579384019360019190911c905b60011d614980565b505b50500260011b919050565b806001600160801b038116811461417e5760405163089f6cfb60e21b815260048101839052602401610733565b600081614a07578383816149ff576149ff6155a7565b0490506115a2565b838210614a315760405163656b542b60e11b81526004810183905260248101859052604401610733565b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000806000198385098385029250828110838203039150509250929050565b60008160a0015162ffffff1660001461417e576000826000015161ffff1683610100015162ffffff1602905060648360a0015162ffffff168283020260630181614b0757614b076155a7565b049392505050565b6000600160801b8210614b245750608090811c905b680100000000000000008210614b3c57604091821c91015b6401000000008210614b5057602091821c91015b620100008210614b6257601091821c91015b6101008210614b7357600891821c91015b60108210614b8357600491821c91015b60048210614b9357600291821c91015b6002821061417e57600101919050565b6001600160a01b0381168114614bb857600080fd5b50565b8015158114614bb857600080fd5b600080600060608486031215614bde57600080fd5b8335614be981614ba3565b9250602084013591506040840135614c0081614bbb565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b604051610180810167ffffffffffffffff81118282101715614c4557614c45614c0b565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614c7457614c74614c0b565b604052919050565b600067ffffffffffffffff821115614c9657614c96614c0b565b5060051b60200190565b600082601f830112614cb157600080fd5b81356020614cc6614cc183614c7c565b614c4b565b82815260059290921b84018101918181019086841115614ce557600080fd5b8286015b84811015614d005780358352918301918301614ce9565b509695505050505050565b803561417e81614ba3565b600082601f830112614d2757600080fd5b81356020614d37614cc183614c7c565b82815260059290921b84018101918181019086841115614d5657600080fd5b8286015b84811015614d00578035614d6d81614ba3565b8352918301918301614d5a565b60008060008060008060c08789031215614d9357600080fd5b8635955060208701359450604087013567ffffffffffffffff80821115614db957600080fd5b614dc58a838b01614ca0565b95506060890135915080821115614ddb57600080fd5b50614de889828a01614d16565b9350506080870135614df981614ba3565b8092505060a087013590509295509295509295565b600080600080600060a08688031215614e2657600080fd5b85359450602086013567ffffffffffffffff80821115614e4557600080fd5b614e5189838a01614ca0565b95506040880135915080821115614e6757600080fd5b50614e7488828901614d16565b9350506060860135614e8581614ba3565b949793965091946080013592915050565b600081518084526020808501945080840160005b83811015614ec657815187529582019590820190600101614eaa565b509495945050505050565b60208152600061201f6020830184614e96565b600080600060608486031215614ef957600080fd5b8335614f0481614ba3565b92506020840135614f1481614ba3565b929592945050506040919091013590565b62ffffff81168114614bb857600080fd5b61ffff81168114614bb857600080fd5b803561417e81614f36565b60008060008060808587031215614f6757600080fd5b8435614f7281614ba3565b93506020850135614f8281614ba3565b92506040850135614f9281614f25565b91506060850135614fa281614f36565b939692955090935050565b600080600080600080600080610100898b031215614fca57600080fd5b8835614fd581614ba3565b97506020890135614fe581614f36565b96506040890135955060608901359450608089013567ffffffffffffffff8082111561501057600080fd5b61501c8c838d01614ca0565b955060a08b013591508082111561503257600080fd5b5061503f8b828c01614ca0565b93505060c089013561505081614ba3565b8092505060e089013590509295985092959890939650565b60008060008060008060008060006101208a8c03121561508757600080fd5b893561509281614ba3565b985060208a01356150a281614ba3565b97506150b060408b01614f46565b965060608a0135955060808a0135945060a08a013567ffffffffffffffff808211156150db57600080fd5b6150e78d838e01614ca0565b955060c08c01359150808211156150fd57600080fd5b5061510a8c828d01614ca0565b93505061511960e08b01614d0b565b91506101008a013590509295985092959850929598565b6000806040838503121561514357600080fd5b823561514e81614ba3565b9150602083013561515e81614f25565b809150509250929050565b60006020828403121561517b57600080fd5b813567ffffffffffffffff81111561519257600080fd5b82016101c0818503121561398e57600080fd5b6040815260006151b86040830185614e96565b82810360208401526115a28185614e96565b60008083601f8401126151dc57600080fd5b50813567ffffffffffffffff8111156151f457600080fd5b6020830191508360208260051b850101111561520f57600080fd5b9250929050565b6000806000806000806080878903121561522f57600080fd5b863561523a81614ba3565b9550602087013561524a81614ba3565b9450604087013567ffffffffffffffff8082111561526757600080fd5b6152738a838b016151ca565b9096509450606089013591508082111561528c57600080fd5b5061529989828a016151ca565b979a9699509497509295939492505050565b600080604083850312156152be57600080fd5b82356152c981614ba3565b946020939093013593505050565b6000806000606084860312156152ec57600080fd5b8351925060208401519150604084015190509250925092565b805161417e81614f36565b805161417e81614f25565b805164ffffffffff8116811461417e57600080fd5b6000610180828403121561534357600080fd5b61534b614c21565b61535483615305565b815261536260208401615305565b602082015261537360408401615305565b604082015261538460608401615305565b606082015261539560808401615305565b60808201526153a660a08401615310565b60a08201526153b760c08401615305565b60c08201526153c860e08401615310565b60e08201526101006153db818501615310565b908201526101206153ed848201615310565b908201526101406153ff848201615310565b9082015261016061541184820161531b565b908201529392505050565b6000806040838503121561542f57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b6000821982111561546957615469615440565b500190565b60008282101561548057615480615440565b500390565b60006020828403121561549757600080fd5b815161398e81614f25565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156154ca57600080fd5b5051919050565b6000602082840312156154e357600080fd5b815161398e81614ba3565b60006020828403121561550057600080fd5b813561398e81614ba3565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561553d57600080fd5b8260051b8083602087013760009401602001938452509192915050565b60006001600160a01b0380891683528088166020840152506080604083015261558760808301868861550b565b828103606084015261559a81858761550b565b9998505050505050505050565b634e487b7160e01b600052601260045260246000fd5b6000602082840312156155cf57600080fd5b815161398e81614bbb565b80516001600160701b038116811461417e57600080fd5b60008060006060848603121561560657600080fd5b61560f846155da565b925061561d602085016155da565b9150604084015163ffffffff81168114614c0057600080fd5b60008161564557615645615440565b506000190190565b600081600019048311821515161561566757615667615440565b500290565b60008261568957634e487b7160e01b600052601260045260246000fd5b500490565b6000608082840312156156a057600080fd5b6040516080810181811067ffffffffffffffff821117156156c3576156c3614c0b565b60405282516156d181614f36565b815260208301516156e181614ba3565b602082015260408301516156f481614bbb565b6040820152606083015161570781614bbb565b60608201529392505050565b60006001600160a01b0380871683528086166020840152506080604083015261573f6080830185614e96565b82810360608401526145358185614e96565b6060815260006157646060830186614e96565b82810360208401526157768186614e96565b9150506001600160a01b0383166040830152949350505050565b6000808335601e198436030181126157a757600080fd5b83018035915067ffffffffffffffff8211156157c257600080fd5b6020019150600581901b360382131561520f57600080fd5b6080815260006157ed6080830189614e96565b828103602084015261580081888a61550b565b9050828103604084015261581581868861550b565b9150506001600160a01b0383166060830152979650505050505050565b60008060006060848603121561584757600080fd5b835192506020808501519250604085015167ffffffffffffffff81111561586d57600080fd5b8501601f8101871361587e57600080fd5b805161588c614cc182614c7c565b81815260059190911b820183019083810190898311156158ab57600080fd5b928401925b828410156158c9578351825292840192908401906158b0565b80955050505050509250925092565b6000825160005b818110156158f957602081860181015185830152016158df565b81811115615908576000828501525b50919091019291505056fea26469706673582212206c05eee95a8862d596ac1d3ea3c231cfdc4a1453951fddb1394e8ead624c932964736f6c634300080a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982000000000000000000000000ae4ec9901c3076d0ddbe76a520f9e90a6227acb700000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1
-----Decoded View---------------
Arg [0] : _factory (address): 0x1886D09C9Ade0c5DB822D85D21678Db67B6c2982
Arg [1] : _oldFactory (address): 0xaE4EC9901c3076D0DdBe76A520F9E90a6227aCB7
Arg [2] : _wavax (address): 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000001886d09c9ade0c5db822d85d21678db67b6c2982
Arg [1] : 000000000000000000000000ae4ec9901c3076d0ddbe76a520f9e90a6227acb7
Arg [2] : 00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1
Loading...
Loading
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.