Overview
S Balance
0 S
S Value
-More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
LiquidityHelperContract
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 800 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {ILBPair} from "joe-v2/interfaces/ILBPair.sol"; import {LiquidityHelper} from "./periphery/LiquidityHelper.sol"; import {NonEmptyBinHelper} from "./periphery/NonEmptyBinHelper.sol"; /** * @title Liquidity Book periphery contract for Liquidity, Fees Amounts and bin fetching. * This contract can waste a lot of gas and is not meant to be used for on-chain calls. * @notice Periphery contract to help compute liquidity, fees amounts from amounts and ids and fetch bins. * @dev The caller must ensure that the parameters are valid following the comments. */ contract LiquidityHelperContract { /** * @dev Return the shares of the receipt token for a given user and ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @return balances The balance of the receipt token for each id */ function getSharesOf(ILBPair lbPair, address user, uint256[] memory ids) external view returns (uint256[] memory balances) { return LiquidityHelper.getSharesOf(lbPair, user, ids); } /** * @dev Return the liquidity (calculated using the constant sum formula: p*x + y) for a given user and ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @return liquidities The liquidity for each id */ function getLiquiditiesOf(ILBPair lbPair, address user, uint256[] memory ids) external view returns (uint256[] memory liquidities) { return LiquidityHelper.getLiquiditiesOf(lbPair, user, ids); } /** * @dev Return the amounts of x and y for a given user and ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @return amountsX The list of amounts of token X * @return amountsY The list of amounts of token Y */ function getAmountsOf(ILBPair lbPair, address user, uint256[] memory ids) external view returns (uint256[] memory amountsX, uint256[] memory amountsY) { return LiquidityHelper.getAmountsOf(lbPair, user, ids); } /** * @dev Return the shares minted for a given list of amounts of x and y * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param amountsX The list of amounts of token X * @param amountsY The list of amounts of token Y * @return shares The amount of shares of the receipt token */ function getSharesForAmounts( ILBPair lbPair, uint256[] memory ids, uint256[] memory amountsX, uint256[] memory amountsY ) external view returns (uint256[] memory shares) { return LiquidityHelper.getSharesForAmounts(lbPair, ids, amountsX, amountsY); } /** * @dev Return the liquidities for a given list of amounts of x and y * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param amountsX The list of amounts of token X * @param amountsY The list of amounts of token Y * @return liquidities The liquidity for each id */ function getLiquiditiesForAmounts( ILBPair lbPair, uint256[] memory ids, uint256[] memory amountsX, uint256[] memory amountsY ) external view returns (uint256[] memory liquidities) { return LiquidityHelper.getLiquiditiesForAmounts(lbPair, ids, amountsX, amountsY); } /** * @dev Return the liquidities for a given list of shares * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param shares The list of shares * @return liquidities The liquidity for each id */ function getLiquiditiesForShares(ILBPair lbPair, uint256[] memory ids, uint256[] memory shares) external view returns (uint256[] memory liquidities) { return LiquidityHelper.getLiquiditiesForShares(lbPair, ids, shares); } /** * @dev Return the amounts of x and y for a list of shares * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param shares The list of shares * @return amountsX The amount of token X for each id * @return amountsY The amount of token Y for each id */ function getAmountsForShares(ILBPair lbPair, uint256[] memory ids, uint256[] memory shares) external view returns (uint256[] memory amountsX, uint256[] memory amountsY) { return LiquidityHelper.getAmountsForShares(lbPair, ids, shares); } /** * @dev Return the amounts of x and y for a list of liquidities * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param liquidities The list of liquidities * @return amountsX The amount of token X for each id * @return amountsY The amount of token Y for each id */ function getAmountsForLiquidities(ILBPair lbPair, uint256[] memory ids, uint256[] memory liquidities) external view returns (uint256[] memory amountsX, uint256[] memory amountsY) { return LiquidityHelper.getAmountsForLiquidities(lbPair, ids, liquidities); } /** * @dev Return the shares for a given list of liquidities * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param liquidities The list of liquidities * @return shares The share for each id */ function getSharesForLiquidities(ILBPair lbPair, uint256[] memory ids, uint256[] memory liquidities) external view returns (uint256[] memory shares) { return LiquidityHelper.getSharesForLiquidities(lbPair, ids, liquidities); } /** * @dev Return the amounts of x and y and fees earned of a given user for a list of ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @param previousX The list of previous amounts of token X * @param previousY The list of previous amounts of token Y * @return amountsX The amount of token X for each id (including fees) * @return amountsY The amount of token Y for each id (including fees) * @return feesX The fees of token X for each id * @return feesY The fees of token Y for each id */ function getAmountsAndFeesEarnedOf( ILBPair lbPair, address user, uint256[] memory ids, uint256[] memory previousX, uint256[] memory previousY ) external view returns (uint256[] memory amountsX, uint256[] memory amountsY, uint256[] memory feesX, uint256[] memory feesY) { return LiquidityHelper.getAmountsAndFeesEarnedOf(lbPair, user, ids, previousX, previousY); } /** * @dev Return the fee shares and fees earned of a given user for a list of ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @param previousLiquidities The list of previous liquidities * @return feeShares The fee shares for each id. This is the amount to burn to receive the fees, * in 128.128 fixed point number * @return feesX The fees of token X for each id * @return feesY The fees of token Y for each id */ function getFeeSharesAndFeesEarnedOf( ILBPair lbPair, address user, uint256[] memory ids, uint256[] memory previousLiquidities ) external view returns (uint256[] memory feeShares, uint256[] memory feesX, uint256[] memory feesY) { return LiquidityHelper.getFeeSharesAndFeesEarnedOf(lbPair, user, ids, previousLiquidities); } /** * @dev Fetch the non-empty bins ids of a liquidity book pair from [start, end]. * If length is specified, it will return the first `length` non-empty bins. * Returns the ids in a packed bytes array, where each id is 3 bytes. * @param pair The liquidity book pair. * @param start The start bin id. * @param end The end bin id. (inclusive) * @param length The number of non-empty bins to fetch. (optional) * @return ids The non-empty bins ids. */ function getPopulatedBinsId(ILBPair pair, uint24 start, uint24 end, uint24 length) external view returns (bytes memory) { return NonEmptyBinHelper.getPopulatedBinsId(pair, start, end, length); } /** * @notice Fetches the non-empty bins reserves of a liquidity book pair from [start, end]. * If length is specified, it will return the first `length` non-empty bins. * @param pair The liquidity book pair. * @param start The start bin id. * @param end The end bin id. (inclusive) * @param length The number of non-empty bins to fetch. (optional) * @return The array of populated bins with (id, reserveX, reserveY) */ function getPopulatedBinsReserves(ILBPair pair, uint24 start, uint24 end, uint24 length) external view returns (NonEmptyBinHelper.PopulatedBin[] memory) { return NonEmptyBinHelper.getPopulatedBinsReserves(pair, start, end, length); } /** * @notice Fetches the non-empty bins reserves of a liquidity book pair from [id-lengthLeft, id+lengthRight] where the user has liquidity. * If id is not specified, it will use the active bin id of the pair. * Will check `lengthLeft` non-empty bins on the left and `lengthRight` non-empty bins on the right, so if the user * has liquidity only after the `lengthLeft + 1` bin on the left and `lengthRight + 1` bin on the right, it will return * an empty array. * @param pair The liquidity book pair. * @param user The user. * @param id The specific bin id. (optional) * @param lengthLeft The number of non-empty bins to fetch on the left. * @param lengthRight The number of non-empty bins to fetch on the right. * @return id The bin id used. (if id was not specified, will return the active bin id) * @return The array of populated bins with (id, reserveX, reserveY, shares, totalShares) * The user amounts can be calculated as (shares * reserve{X,Y}) / totalShares. */ function getBinsReserveOf(ILBPair pair, address user, uint24 id, uint24 lengthLeft, uint24 lengthRight) external view returns (uint24, NonEmptyBinHelper.PopulatedBinUser[] memory) { return NonEmptyBinHelper.getBinsReserveOf(pair, user, id, lengthLeft, lengthRight); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Hooks} from "../libraries/Hooks.sol"; import {ILBFactory} from "./ILBFactory.sol"; import {ILBFlashLoanCallback} from "./ILBFlashLoanCallback.sol"; import {ILBToken} from "./ILBToken.sol"; interface ILBPair is ILBToken { error LBPair__ZeroBorrowAmount(); error LBPair__AddressZero(); error LBPair__EmptyMarketConfigs(); error LBPair__FlashLoanCallbackFailed(); error LBPair__FlashLoanInsufficientAmount(); error LBPair__InsufficientAmountIn(); error LBPair__InsufficientAmountOut(); error LBPair__InvalidInput(); error LBPair__InvalidStaticFeeParameters(); error LBPair__OnlyFactory(); error LBPair__OnlyProtocolFeeRecipient(); error LBPair__OutOfLiquidity(); error LBPair__TokenNotSupported(); error LBPair__ZeroAmount(uint24 id); error LBPair__ZeroAmountsOut(uint24 id); error LBPair__ZeroShares(uint24 id); error LBPair__MaxTotalFeeExceeded(); error LBPair__InvalidHooks(); struct MintArrays { uint256[] ids; bytes32[] amounts; uint256[] liquidityMinted; } event DepositedToBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts); event WithdrawnFromBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts); event CompositionFees(address indexed sender, uint24 id, bytes32 totalFees, bytes32 protocolFees); event CollectedProtocolFees(address indexed feeRecipient, bytes32 protocolFees); event Swap( address indexed sender, address indexed to, uint24 id, bytes32 amountsIn, bytes32 amountsOut, uint24 volatilityAccumulator, bytes32 totalFees, bytes32 protocolFees ); event StaticFeeParametersSet( address indexed sender, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ); event HooksParametersSet(address indexed sender, bytes32 hooksParameters); event FlashLoan( address indexed sender, ILBFlashLoanCallback indexed receiver, uint24 activeId, bytes32 amounts, bytes32 totalFees, bytes32 protocolFees ); event OracleLengthIncreased(address indexed sender, uint16 oracleLength); event ForcedDecay(address indexed sender, uint24 idReference, uint24 volatilityReference); function initialize( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator, uint24 activeId ) external; function implementation() external view returns (address); function getFactory() external view returns (ILBFactory factory); function getTokenX() external view returns (IERC20 tokenX); function getTokenY() external view returns (IERC20 tokenY); function getBinStep() external view returns (uint16 binStep); function getReserves() external view returns (uint128 reserveX, uint128 reserveY); function getActiveId() external view returns (uint24 activeId); function getBin(uint24 id) external view returns (uint128 binReserveX, uint128 binReserveY); function getNextNonEmptyBin(bool swapForY, uint24 id) external view returns (uint24 nextId); function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY); function getStaticFeeParameters() external view returns ( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ); function getLBHooksParameters() external view returns (bytes32 hooksParameters); function getVariableFeeParameters() external view returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate); function getOracleParameters() external view returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp); function getOracleSampleAt(uint40 lookupTimestamp) external view returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed); function getPriceFromId(uint24 id) external view returns (uint256 price); function getIdFromPrice(uint256 price) external view returns (uint24 id); function getSwapIn(uint128 amountOut, bool swapForY) external view returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee); function getSwapOut(uint128 amountIn, bool swapForY) external view returns (uint128 amountInLeft, uint128 amountOut, uint128 fee); function swap(bool swapForY, address to) external returns (bytes32 amountsOut); function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external; function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo) external returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted); function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn) external returns (bytes32[] memory amounts); function collectProtocolFees() external returns (bytes32 collectedProtocolFees); function increaseOracleLength(uint16 newLength) external; function setStaticFeeParameters( uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) external; function setHooksParameters(bytes32 hooksParameters, bytes calldata onHooksSetData) external; function forceDecay() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Uint256x256Math} from "joe-v2/libraries/math/Uint256x256Math.sol"; import {PriceHelper} from "joe-v2/libraries/PriceHelper.sol"; import {BinHelper} from "joe-v2/libraries/BinHelper.sol"; import {SafeCast} from "joe-v2/libraries/math/SafeCast.sol"; import {ILBPair} from "joe-v2/interfaces/ILBPair.sol"; /** * @title Liquidity Book periphery library for Liquidity and Fees Amounts * @notice Periphery library to help compute liquidity and fees amounts from amounts and ids. * @dev The caller must ensure that the parameters are valid following the comments. */ library LiquidityHelper { using Uint256x256Math for uint256; using SafeCast for uint256; error FeesAmounts__LengthMismatch(); /** * @dev Return the shares of the receipt token for a given user and ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @return balances The balance of the receipt token for each id */ function getSharesOf(ILBPair lbPair, address user, uint256[] memory ids) internal view returns (uint256[] memory balances) { balances = new uint256[](ids.length); for (uint256 i; i < ids.length;) { balances[i] = lbPair.balanceOf(user, ids[i].safe24()); unchecked { ++i; } } } /** * @dev Return the liquidity (calculated using the constant sum formula: p*x + y) for a given user and ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @return liquidities The liquidity for each id */ function getLiquiditiesOf(ILBPair lbPair, address user, uint256[] memory ids) internal view returns (uint256[] memory liquidities) { liquidities = new uint256[](ids.length); uint16 binStep = ILBPair(lbPair).getBinStep(); for (uint256 i; i < ids.length;) { uint24 id = ids[i].safe24(); (uint256 amountX, uint256 amountY) = getAmountsOfAtId(lbPair, user, id); liquidities[i] = getLiquidityFromId(amountX, amountY, id, binStep); unchecked { ++i; } } } /** * @dev Return the amounts of x and y for a given user and ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @return amountsX The list of amounts of token X * @return amountsY The list of amounts of token Y */ function getAmountsOf(ILBPair lbPair, address user, uint256[] memory ids) internal view returns (uint256[] memory amountsX, uint256[] memory amountsY) { amountsX = new uint256[](ids.length); amountsY = new uint256[](ids.length); for (uint256 i; i < ids.length;) { (amountsX[i], amountsY[i]) = getAmountsOfAtId(lbPair, user, ids[i].safe24()); unchecked { ++i; } } } /** * @dev Return the shares minted for a given list of amounts of x and y * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param amountsX The list of amounts of token X * @param amountsY The list of amounts of token Y * @return shares The amount of shares of the receipt token */ function getSharesForAmounts( ILBPair lbPair, uint256[] memory ids, uint256[] memory amountsX, uint256[] memory amountsY ) internal view returns (uint256[] memory shares) { if (ids.length != amountsX.length || ids.length != amountsY.length) revert FeesAmounts__LengthMismatch(); shares = new uint256[](ids.length); uint16 binStep = ILBPair(lbPair).getBinStep(); for (uint256 i; i < ids.length;) { uint24 id = ids[i].safe24(); uint256 price = PriceHelper.getPriceFromId(id, binStep); uint256 liquidity = getLiquidityFromPrice(amountsX[i], amountsY[i], price); shares[i] = getShareForLiquidity(lbPair, id, liquidity, price); unchecked { ++i; } } } /** * @dev Return the liquidities for a given list of amounts of x and y * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param amountsX The list of amounts of token X * @param amountsY The list of amounts of token Y * @return liquidities The liquidity for each id */ function getLiquiditiesForAmounts( ILBPair lbPair, uint256[] memory ids, uint256[] memory amountsX, uint256[] memory amountsY ) internal view returns (uint256[] memory liquidities) { if (ids.length != amountsX.length || ids.length != amountsY.length) revert FeesAmounts__LengthMismatch(); liquidities = new uint256[](ids.length); uint16 binStep = lbPair.getBinStep(); for (uint256 i; i < ids.length;) { liquidities[i] = getLiquidityFromId(amountsX[i], amountsY[i], ids[i].safe24(), binStep); unchecked { ++i; } } } /** * @dev Return the liquidities for a given list of shares * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param shares The list of shares * @return liquidities The liquidity for each id */ function getLiquiditiesForShares(ILBPair lbPair, uint256[] memory ids, uint256[] memory shares) internal view returns (uint256[] memory liquidities) { if (ids.length != shares.length) revert FeesAmounts__LengthMismatch(); liquidities = new uint256[](ids.length); uint16 binStep = ILBPair(lbPair).getBinStep(); for (uint256 i; i < ids.length;) { uint24 id = ids[i].safe24(); (uint256 amountX, uint256 amountY) = getAmountsForShare(lbPair, id, shares[i]); liquidities[i] = getLiquidityFromId(amountX, amountY, id, binStep); unchecked { ++i; } } } /** * @dev Return the amounts of x and y for a list of shares * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param shares The list of shares * @return amountsX The amount of token X for each id * @return amountsY The amount of token Y for each id */ function getAmountsForShares(ILBPair lbPair, uint256[] memory ids, uint256[] memory shares) internal view returns (uint256[] memory amountsX, uint256[] memory amountsY) { if (ids.length != shares.length) revert FeesAmounts__LengthMismatch(); amountsX = new uint256[](ids.length); amountsY = new uint256[](ids.length); for (uint256 i; i < ids.length;) { (uint256 amountX, uint256 amountY) = getAmountsForShare(lbPair, ids[i], shares[i]); amountsX[i] = amountX; amountsY[i] = amountY; unchecked { ++i; } } } /** * @dev Return the amounts of x and y for a list of liquidities * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param liquidities The list of liquidities * @return amountsX The amount of token X for each id * @return amountsY The amount of token Y for each id */ function getAmountsForLiquidities(ILBPair lbPair, uint256[] memory ids, uint256[] memory liquidities) internal view returns (uint256[] memory amountsX, uint256[] memory amountsY) { if (ids.length != liquidities.length) revert FeesAmounts__LengthMismatch(); amountsX = new uint256[](ids.length); amountsY = new uint256[](ids.length); uint16 binStep = ILBPair(lbPair).getBinStep(); for (uint256 i; i < ids.length;) { uint256 price = PriceHelper.getPriceFromId(ids[i].safe24(), binStep); (uint256 binReserveX, uint256 binReserveY) = lbPair.getBin(ids[i].safe24()); uint256 binLiquidity = getLiquidityFromPrice(binReserveX, binReserveY, price); uint256 liquidity = liquidities[i]; (amountsX[i], amountsY[i]) = binLiquidity == 0 ? (0, 0) : ( liquidity.mulDivRoundDown(binReserveX, binLiquidity), liquidity.mulDivRoundDown(binReserveY, binLiquidity) ); unchecked { ++i; } } } /** * @dev Return the shares for a given list of liquidities * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param ids The list of ids * @param liquidities The list of liquidities * @return shares The share for each id */ function getSharesForLiquidities(ILBPair lbPair, uint256[] memory ids, uint256[] memory liquidities) internal view returns (uint256[] memory shares) { if (ids.length != liquidities.length) revert FeesAmounts__LengthMismatch(); shares = new uint256[](ids.length); uint16 binStep = ILBPair(lbPair).getBinStep(); for (uint256 i; i < ids.length;) { uint256 price = PriceHelper.getPriceFromId(ids[i].safe24(), binStep); shares[i] = getShareForLiquidity(lbPair, ids[i], liquidities[i], price); unchecked { ++i; } } } /** * @dev Return the amounts of x and y and fees earned of a given user for a list of ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @param previousX The list of previous amounts of token X * @param previousY The list of previous amounts of token Y * @return amountsX The amount of token X for each id (including fees) * @return amountsY The amount of token Y for each id (including fees) * @return feesX The fees of token X for each id * @return feesY The fees of token Y for each id */ function getAmountsAndFeesEarnedOf( ILBPair lbPair, address user, uint256[] memory ids, uint256[] memory previousX, uint256[] memory previousY ) internal view returns (uint256[] memory amountsX, uint256[] memory amountsY, uint256[] memory feesX, uint256[] memory feesY) { if (ids.length != previousX.length || ids.length != previousY.length) revert FeesAmounts__LengthMismatch(); amountsX = new uint256[](ids.length); amountsY = new uint256[](ids.length); feesX = new uint256[](ids.length); feesY = new uint256[](ids.length); uint16 binStep = ILBPair(lbPair).getBinStep(); for (uint256 i; i < ids.length;) { uint24 id = ids[i].safe24(); (uint256 amountX, uint256 amountY) = getAmountsOfAtId(lbPair, user, id); amountsX[i] = amountX; amountsY[i] = amountY; (uint256 feeX, uint256 feeY) = getFeesAtId(binStep, id, previousX[i], previousY[i], amountX, amountY); feesX[i] = feeX; feesY[i] = feeY; unchecked { ++i; } } } /** * @dev Return the fee shares and fees earned of a given user for a list of ids * The ids must be unique, if not, the result will be wrong. * @param lbPair The pair * @param user The user * @param ids The list of ids * @param previousLiquidities The list of previous liquidities * @return feeShares The fee shares for each id. This is the amount to burn to receive the fees, * in 128.128 fixed point number * @return feesX The fees of token X for each id * @return feesY The fees of token Y for each id */ function getFeeSharesAndFeesEarnedOf( ILBPair lbPair, address user, uint256[] memory ids, uint256[] memory previousLiquidities ) internal view returns (uint256[] memory feeShares, uint256[] memory feesX, uint256[] memory feesY) { if (ids.length != previousLiquidities.length) revert FeesAmounts__LengthMismatch(); feeShares = new uint256[](ids.length); feesX = new uint256[](ids.length); feesY = new uint256[](ids.length); uint16 binStep = ILBPair(lbPair).getBinStep(); for (uint256 i; i < ids.length;) { uint24 id = ids[i].safe24(); uint256 share = getShareOfAtId(lbPair, user, id); (uint256 binReserveX, uint256 binReserveY) = lbPair.getBin(id); uint256 totalShares = lbPair.totalSupply(id); (uint256 amountX, uint256 amountY) = totalShares == 0 ? (0, 0) : (share.mulDivRoundDown(binReserveX, totalShares), share.mulDivRoundDown(binReserveY, totalShares)); uint256 previousLiquidity = previousLiquidities[i]; uint256 currentLiquidity = getLiquidityFromPrice(amountX, amountY, PriceHelper.getPriceFromId(id, binStep)); uint256 feeShare = currentLiquidity > previousLiquidity ? (currentLiquidity - previousLiquidity).mulDivRoundDown(share, currentLiquidity) : 0; feeShares[i] = feeShare; (feesX[i], feesY[i]) = totalShares == 0 ? (0, 0) : (feeShare.mulDivRoundDown(binReserveX, totalShares), feeShare.mulDivRoundDown(binReserveY, totalShares)); unchecked { ++i; } } } /** * @dev Return the amounts of x and y for a given share amount * @param lbPair The pair * @param id The id * @param share The share amount * @return amountX The amount of token X * @return amountY The amount of token Y */ function getAmountsForShare(ILBPair lbPair, uint256 id, uint256 share) internal view returns (uint256 amountX, uint256 amountY) { (uint256 binReserveX, uint256 binReserveY) = lbPair.getBin(id.safe24()); uint256 totalShares = lbPair.totalSupply(id); (amountX, amountY) = totalShares == 0 ? (0, 0) : (share.mulDivRoundDown(binReserveX, totalShares), share.mulDivRoundDown(binReserveY, totalShares)); } /** * @dev Return the share amount of a given user at a given id * @param lbPair The pair * @param user The user * @param id The id * @return the share amount of the user at the given id */ function getShareOfAtId(ILBPair lbPair, address user, uint24 id) internal view returns (uint256) { return lbPair.balanceOf(user, id); } /** * @dev Return the amounts of x and y of a given user at a given id * @param lbPair The pair * @param user The user * @param id The id * @return amountX The amount of token X * @return amountY The amount of token Y */ function getAmountsOfAtId(ILBPair lbPair, address user, uint24 id) internal view returns (uint256 amountX, uint256 amountY) { uint256 share = getShareOfAtId(lbPair, user, id); (uint256 binReserveX, uint256 binReserveY) = lbPair.getBin(id); uint256 totalShares = lbPair.totalSupply(id); (amountX, amountY) = totalShares == 0 ? (0, 0) : (share.mulDivRoundDown(binReserveX, totalShares), share.mulDivRoundDown(binReserveY, totalShares)); } /** * @dev Return the fees earned of a given user at a given id from a given amounts of x and y * @param binStep The binStep of the pair * @param id The id * @param previousX The previous amount of token X * @param previousY The previous amount of token Y * @param amountX The current amount of token X * @param amountY The current amount of token Y * @return feesX The fees of token X * @return feesY The fees of token Y */ function getFeesAtId( uint16 binStep, uint24 id, uint256 previousX, uint256 previousY, uint256 amountX, uint256 amountY ) internal pure returns (uint256 feesX, uint256 feesY) { uint256 price = PriceHelper.getPriceFromId(id, binStep); uint256 previousLiquidity = getLiquidityFromPrice(previousX, previousY, price); uint256 currentLiquidity = getLiquidityFromPrice(amountX, amountY, price); return getFeesFromLiquidities(previousLiquidity, currentLiquidity, amountX, amountY); } /** * @dev Return the fees earned of a given user at a given id from a given liquidity position * @param previousLiquidity The previous liquidity * @param currentLiquidity The current liquidity * @param amountX The current amount of token X * @param amountY The current amount of token Y * @return feesX The fees of token X * @return feesY The fees of token Y */ function getFeesFromLiquidities( uint256 previousLiquidity, uint256 currentLiquidity, uint256 amountX, uint256 amountY ) internal pure returns (uint256 feesX, uint256 feesY) { if (currentLiquidity > previousLiquidity) { uint256 feesinL = (currentLiquidity - previousLiquidity); feesX = feesinL.mulDivRoundDown(amountX, currentLiquidity); feesY = feesinL.mulDivRoundDown(amountY, currentLiquidity); } } /** * @dev Return the share amount for a given liquidity at a given id * @param lbPair The pair * @param id The id * @param liquidity The liquidity * @param price The price * @return The share amount */ function getShareForLiquidity(ILBPair lbPair, uint256 id, uint256 liquidity, uint256 price) internal view returns (uint256) { (uint256 binReserveX, uint256 binReserveY) = lbPair.getBin(id.safe24()); uint256 binLiquidity = getLiquidityFromPrice(binReserveX, binReserveY, price); uint256 totalShares = lbPair.totalSupply(id); return binLiquidity == 0 ? 0 : liquidity.mulDivRoundDown(totalShares, binLiquidity); } /** * @dev Return the liquidity of a given amount of x and y at a given id * @param amountX The amount of token X * @param amountY The amount of token Y * @param id The id * @param binStep The binStep of the pair * @return liquidity The liquidity */ function getLiquidityFromId(uint256 amountX, uint256 amountY, uint24 id, uint16 binStep) internal pure returns (uint256 liquidity) { return getLiquidityFromPrice(amountX, amountY, PriceHelper.getPriceFromId(id, binStep)); } /** * @dev Return the liquidity of a given amount of x and y at a given price * The amount is returned as a 128.128 fixed point number * @param amountX The amount of token X * @param amountY The amount of token Y * @param price The price * @return liquidity The liquidity */ function getLiquidityFromPrice(uint256 amountX, uint256 amountY, uint256 price) internal pure returns (uint256 liquidity) { return BinHelper.getLiquidity(amountX, amountY, price) >> 128; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Uint256x256Math} from "joe-v2/libraries/math/Uint256x256Math.sol"; import {ILBPair} from "joe-v2/interfaces/ILBPair.sol"; /** * @title Liquidity Book periphery library for fetching non-empty bins. * @notice Periphery library to help fetch the non-empty bins of a liquidity book. * @dev The caller must ensure that the parameters are valid following the comments. */ library NonEmptyBinHelper { struct PopulatedBin { uint24 id; uint128 reserveX; uint128 reserveY; } struct PopulatedBinUser { uint24 id; uint128 reserveX; uint128 reserveY; uint256 shares; uint256 totalShares; } /** * @dev Fetch the non-empty bins ids of a liquidity book pair from [start, end]. * If length is specified, it will return the first `length` non-empty bins. * Returns the ids in a packed bytes array, where each id is 3 bytes. * @param pair The liquidity book pair. * @param start The start bin id. * @param end The end bin id. (inclusive) * @param length The number of non-empty bins to fetch. (optional) * @return ids The non-empty bins ids. */ function getPopulatedBinsId(ILBPair pair, uint24 start, uint24 end, uint24 length) internal view returns (bytes memory) { (start, end) = start < end ? (start == 0 ? (0, end) : (start - 1, end)) : (start == type(uint24).max ? (end, start) : (start + 1, end)); length = length == 0 ? (end > start ? end - start : start - end) : length; bytes memory ids = new bytes(uint256(length) * 3); // pessimistic memory allocation of 3 bytes per id uint256 populatedBinCount = 0; uint256 memValue = ids.length; uint256 memSlot; assembly { memSlot := ids } uint24 id = start; bool swapForY = start > end; for (uint256 i; i < length && populatedBinCount < length; ++i) { id = pair.getNextNonEmptyBin(swapForY, id); if (swapForY ? id < end || id == type(uint24).max : id > end || id == 0) break; ++populatedBinCount; assembly { memValue := or(shl(24, memValue), id) memSlot := add(memSlot, 3) mstore(memSlot, memValue) } } assembly { mstore(ids, mul(3, populatedBinCount)) } return ids; } /** * @notice Fetches the non-empty bins reserves of a liquidity book pair from [start, end]. * If length is specified, it will return the first `length` non-empty bins. * @param pair The liquidity book pair. * @param start The start bin id. * @param end The end bin id. (inclusive) * @param length The number of non-empty bins to fetch. (optional) * @return The array of populated bins with (id, reserveX, reserveY) */ function getPopulatedBinsReserves(ILBPair pair, uint24 start, uint24 end, uint24 length) internal view returns (PopulatedBin[] memory) { bytes memory ids = getPopulatedBinsId(pair, start, end, length); uint256 populatedBinCount = ids.length / 3; PopulatedBin[] memory populatedBins = new PopulatedBin[](populatedBinCount); uint256 memSlot; assembly { memSlot := add(ids, 0x1d) } uint24 id; for (uint256 i; i < populatedBinCount; ++i) { assembly { memSlot := add(memSlot, 3) id := shr(232, mload(memSlot)) } (uint128 reserveX, uint128 reserveY) = pair.getBin(id); populatedBins[i] = PopulatedBin(id, reserveX, reserveY); } return populatedBins; } /** * @notice Fetches the non-empty bins reserves of a liquidity book pair from [id-lengthLeft, id+lengthRight] where the user has liquidity. * If id is not specified, it will use the active bin id of the pair. * Will check `lengthLeft` non-empty bins on the left and `lengthRight` non-empty bins on the right, so if the user * has liquidity only after the `lengthLeft + 1` bin on the left and `lengthRight + 1` bin on the right, it will return * an empty array. * @param pair The liquidity book pair. * @param user The user. * @param id The specific bin id. (optional) * @param lengthLeft The number of non-empty bins to fetch on the left. * @param lengthRight The number of non-empty bins to fetch on the right. * @return The bin id used. (if id was not specified, it will return the active bin id) * @return The array of populated bins with (id, reserveX, reserveY, shares, totalShares) * The user amounts can be calculated as (shares * reserve{X,Y}) / totalShares. */ function getBinsReserveOf(ILBPair pair, address user, uint24 id, uint24 lengthLeft, uint24 lengthRight) internal view returns (uint24, PopulatedBinUser[] memory) { if (id == 0) id = pair.getActiveId(); bytes memory idsLeft = lengthLeft == 0 ? new bytes(0) : getPopulatedBinsId(pair, lengthRight == 0 ? id : id - 1, 0, lengthLeft); bytes memory idsRight = lengthRight == 0 ? new bytes(0) : getPopulatedBinsId(pair, id, type(uint24).max, lengthRight); uint256 populatedBinCountLeft = idsLeft.length / 3; uint256 populatedBinCountRight = idsRight.length / 3; uint256 populatedBinCount = populatedBinCountLeft + populatedBinCountRight; PopulatedBinUser[] memory userBins = new PopulatedBinUser[](populatedBinCount); uint256 memSlot; assembly { memSlot := add(add(idsLeft, 0x20), mul(populatedBinCountLeft, 3)) // Start at the end to reorder the ids } ILBPair pair_ = pair; // Avoid stack too deep error address user_ = user; uint256 i; while (i < populatedBinCountLeft) { uint24 binId; assembly { memSlot := sub(memSlot, 3) binId := shr(232, mload(memSlot)) } uint256 shares = pair_.balanceOf(user_, binId); if (shares > 0) { (uint128 reserveX, uint128 reserveY) = pair_.getBin(binId); uint256 totalShares = pair_.totalSupply(binId); userBins[i++] = PopulatedBinUser(binId, reserveX, reserveY, shares, totalShares); } else { --populatedBinCountLeft; } } populatedBinCount = populatedBinCountLeft + populatedBinCountRight; assembly { memSlot := add(idsRight, 0x1d) } while (i < populatedBinCount) { uint24 binId; assembly { memSlot := add(memSlot, 3) binId := shr(232, mload(memSlot)) } uint256 shares = pair_.balanceOf(user_, binId); if (shares > 0) { (uint128 reserveX, uint128 reserveY) = pair_.getBin(binId); uint256 totalShares = pair_.totalSupply(binId); userBins[i++] = PopulatedBinUser(binId, reserveX, reserveY, shares, totalShares); } else { --populatedBinCount; } } assembly { mstore(userBins, populatedBinCount) } return (id, userBins); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {ILBHooks} from "../interfaces/ILBHooks.sol"; /** * @title Hooks library * @notice This library contains functions that should be used to interact with hooks */ library Hooks { error Hooks__CallFailed(); bytes32 internal constant BEFORE_SWAP_FLAG = bytes32(uint256(1 << 160)); bytes32 internal constant AFTER_SWAP_FLAG = bytes32(uint256(1 << 161)); bytes32 internal constant BEFORE_FLASH_LOAN_FLAG = bytes32(uint256(1 << 162)); bytes32 internal constant AFTER_FLASH_LOAN_FLAG = bytes32(uint256(1 << 163)); bytes32 internal constant BEFORE_MINT_FLAG = bytes32(uint256(1 << 164)); bytes32 internal constant AFTER_MINT_FLAG = bytes32(uint256(1 << 165)); bytes32 internal constant BEFORE_BURN_FLAG = bytes32(uint256(1 << 166)); bytes32 internal constant AFTER_BURN_FLAG = bytes32(uint256(1 << 167)); bytes32 internal constant BEFORE_TRANSFER_FLAG = bytes32(uint256(1 << 168)); bytes32 internal constant AFTER_TRANSFER_FLAG = bytes32(uint256(1 << 169)); struct Parameters { address hooks; bool beforeSwap; bool afterSwap; bool beforeFlashLoan; bool afterFlashLoan; bool beforeMint; bool afterMint; bool beforeBurn; bool afterBurn; bool beforeBatchTransferFrom; bool afterBatchTransferFrom; } /** * @dev Helper function to encode the hooks parameters to a single bytes32 value * @param parameters The hooks parameters * @return hooksParameters The encoded hooks parameters */ function encode(Parameters memory parameters) internal pure returns (bytes32 hooksParameters) { hooksParameters = bytes32(uint256(uint160(address(parameters.hooks)))); if (parameters.beforeSwap) hooksParameters |= BEFORE_SWAP_FLAG; if (parameters.afterSwap) hooksParameters |= AFTER_SWAP_FLAG; if (parameters.beforeFlashLoan) hooksParameters |= BEFORE_FLASH_LOAN_FLAG; if (parameters.afterFlashLoan) hooksParameters |= AFTER_FLASH_LOAN_FLAG; if (parameters.beforeMint) hooksParameters |= BEFORE_MINT_FLAG; if (parameters.afterMint) hooksParameters |= AFTER_MINT_FLAG; if (parameters.beforeBurn) hooksParameters |= BEFORE_BURN_FLAG; if (parameters.afterBurn) hooksParameters |= AFTER_BURN_FLAG; if (parameters.beforeBatchTransferFrom) hooksParameters |= BEFORE_TRANSFER_FLAG; if (parameters.afterBatchTransferFrom) hooksParameters |= AFTER_TRANSFER_FLAG; } /** * @dev Helper function to decode the hooks parameters from a single bytes32 value * @param hooksParameters The encoded hooks parameters * @return parameters The hooks parameters */ function decode(bytes32 hooksParameters) internal pure returns (Parameters memory parameters) { parameters.hooks = getHooks(hooksParameters); parameters.beforeSwap = (hooksParameters & BEFORE_SWAP_FLAG) != 0; parameters.afterSwap = (hooksParameters & AFTER_SWAP_FLAG) != 0; parameters.beforeFlashLoan = (hooksParameters & BEFORE_FLASH_LOAN_FLAG) != 0; parameters.afterFlashLoan = (hooksParameters & AFTER_FLASH_LOAN_FLAG) != 0; parameters.beforeMint = (hooksParameters & BEFORE_MINT_FLAG) != 0; parameters.afterMint = (hooksParameters & AFTER_MINT_FLAG) != 0; parameters.beforeBurn = (hooksParameters & BEFORE_BURN_FLAG) != 0; parameters.afterBurn = (hooksParameters & AFTER_BURN_FLAG) != 0; parameters.beforeBatchTransferFrom = (hooksParameters & BEFORE_TRANSFER_FLAG) != 0; parameters.afterBatchTransferFrom = (hooksParameters & AFTER_TRANSFER_FLAG) != 0; } /** * @dev Helper function to get the hooks address from the encoded hooks parameters * @param hooksParameters The encoded hooks parameters * @return hooks The hooks address */ function getHooks(bytes32 hooksParameters) internal pure returns (address hooks) { hooks = address(uint160(uint256(hooksParameters))); } /** * @dev Helper function to set the hooks address in the encoded hooks parameters * @param hooksParameters The encoded hooks parameters * @param newHooks The new hooks address * @return hooksParameters The updated hooks parameters */ function setHooks(bytes32 hooksParameters, address newHooks) internal pure returns (bytes32) { return bytes32(bytes12(hooksParameters)) | bytes32(uint256(uint160(newHooks))); } /** * @dev Helper function to get the flags from the encoded hooks parameters * @param hooksParameters The encoded hooks parameters * @return flags The flags */ function getFlags(bytes32 hooksParameters) internal pure returns (bytes12 flags) { flags = bytes12(hooksParameters); } /** * @dev Helper function call the onHooksSet function on the hooks contract, only if the * hooksParameters is not 0 * @param hooksParameters The encoded hooks parameters * @param onHooksSetData The data to pass to the onHooksSet function */ function onHooksSet(bytes32 hooksParameters, bytes calldata onHooksSetData) internal { if (hooksParameters != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.onHooksSet.selector, hooksParameters, onHooksSetData) ); } } /** * @dev Helper function to call the beforeSwap function on the hooks contract, only if the * BEFORE_SWAP_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param swapForY Whether the swap is for Y * @param amountsIn The amounts in */ function beforeSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsIn) internal { if ((hooksParameters & BEFORE_SWAP_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeSwap.selector, sender, to, swapForY, amountsIn) ); } } /** * @dev Helper function to call the afterSwap function on the hooks contract, only if the * AFTER_SWAP_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param swapForY Whether the swap is for Y * @param amountsOut The amounts out */ function afterSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsOut) internal { if ((hooksParameters & AFTER_SWAP_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterSwap.selector, sender, to, swapForY, amountsOut) ); } } /** * @dev Helper function to call the beforeFlashLoan function on the hooks contract, only if the * BEFORE_FLASH_LOAN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param amounts The amounts */ function beforeFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 amounts) internal { if ((hooksParameters & BEFORE_FLASH_LOAN_FLAG) != 0) { _safeCall(hooksParameters, abi.encodeWithSelector(ILBHooks.beforeFlashLoan.selector, sender, to, amounts)); } } /** * @dev Helper function to call the afterFlashLoan function on the hooks contract, only if the * AFTER_FLASH_LOAN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param fees The fees * @param feesReceived The fees received */ function afterFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 fees, bytes32 feesReceived) internal { if ((hooksParameters & AFTER_FLASH_LOAN_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterFlashLoan.selector, sender, to, fees, feesReceived) ); } } /** * @dev Helper function to call the beforeMint function on the hooks contract, only if the * BEFORE_MINT_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param liquidityConfigs The liquidity configs * @param amountsReceived The amounts received */ function beforeMint( bytes32 hooksParameters, address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsReceived ) internal { if ((hooksParameters & BEFORE_MINT_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeMint.selector, sender, to, liquidityConfigs, amountsReceived) ); } } /** * @dev Helper function to call the afterMint function on the hooks contract, only if the * AFTER_MINT_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param to The recipient * @param liquidityConfigs The liquidity configs * @param amountsIn The amounts in */ function afterMint( bytes32 hooksParameters, address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsIn ) internal { if ((hooksParameters & AFTER_MINT_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterMint.selector, sender, to, liquidityConfigs, amountsIn) ); } } /** * @dev Helper function to call the beforeBurn function on the hooks contract, only if the * BEFORE_BURN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The ids * @param amountsToBurn The amounts to burn */ function beforeBurn( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) internal { if ((hooksParameters & BEFORE_BURN_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeBurn.selector, sender, from, to, ids, amountsToBurn) ); } } /** * @dev Helper function to call the afterBurn function on the hooks contract, only if the * AFTER_BURN_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The ids * @param amountsToBurn The amounts to burn */ function afterBurn( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) internal { if ((hooksParameters & AFTER_BURN_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterBurn.selector, sender, from, to, ids, amountsToBurn) ); } } /** * @dev Helper function to call the beforeTransferFrom function on the hooks contract, only if the * BEFORE_TRANSFER_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The list of ids * @param amounts The list of amounts */ function beforeBatchTransferFrom( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) internal { if ((hooksParameters & BEFORE_TRANSFER_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.beforeBatchTransferFrom.selector, sender, from, to, ids, amounts) ); } } /** * @dev Helper function to call the afterTransferFrom function on the hooks contract, only if the * AFTER_TRANSFER_FLAG is set in the hooksParameters * @param hooksParameters The encoded hooks parameters * @param sender The sender * @param from The sender * @param to The recipient * @param ids The list of ids * @param amounts The list of amounts */ function afterBatchTransferFrom( bytes32 hooksParameters, address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) internal { if ((hooksParameters & AFTER_TRANSFER_FLAG) != 0) { _safeCall( hooksParameters, abi.encodeWithSelector(ILBHooks.afterBatchTransferFrom.selector, sender, from, to, ids, amounts) ); } } /** * @dev Helper function to call the hooks contract and verify the call was successful * by matching the expected selector with the returned data * @param hooksParameters The encoded hooks parameters * @param data The data to pass to the hooks contract */ function _safeCall(bytes32 hooksParameters, bytes memory data) private { bool success; address hooks = getHooks(hooksParameters); assembly { let expectedSelector := shr(224, mload(add(data, 0x20))) success := call(gas(), hooks, 0, add(data, 0x20), mload(data), 0, 0x20) if and(iszero(success), iszero(iszero(returndatasize()))) { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } success := and(success, and(gt(returndatasize(), 0x1f), eq(shr(224, mload(0)), expectedSelector))) } if (!success) revert Hooks__CallFailed(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ILBHooks} from "./ILBHooks.sol"; import {ILBPair} from "./ILBPair.sol"; /** * @title Liquidity Book Factory Interface * @author Trader Joe * @notice Required interface of LBFactory contract */ interface ILBFactory { error LBFactory__IdenticalAddresses(IERC20 token); error LBFactory__QuoteAssetNotWhitelisted(IERC20 quoteAsset); error LBFactory__QuoteAssetAlreadyWhitelisted(IERC20 quoteAsset); error LBFactory__AddressZero(); error LBFactory__LBPairAlreadyExists(IERC20 tokenX, IERC20 tokenY, uint256 _binStep); error LBFactory__LBPairDoesNotExist(IERC20 tokenX, IERC20 tokenY, uint256 binStep); error LBFactory__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep); error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees); error LBFactory__BinStepTooLow(uint256 binStep); error LBFactory__PresetIsLockedForUsers(address user, uint256 binStep); error LBFactory__LBPairIgnoredIsAlreadyInTheSameState(); error LBFactory__BinStepHasNoPreset(uint256 binStep); error LBFactory__PresetOpenStateIsAlreadyInTheSameState(); error LBFactory__SameFeeRecipient(address feeRecipient); error LBFactory__SameFlashLoanFee(uint256 flashLoanFee); error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation); error LBFactory__SameImplementation(address LBPairImplementation); error LBFactory__ImplementationNotSet(); error LBFactory__SameHooksImplementation(address hooksImplementation); error LBFactory__SameHooksParameters(bytes32 hooksParameters); error LBFactory__InvalidHooksParameters(); error LBFactory__CannotGrantDefaultAdminRole(); /** * @dev Structure to store the LBPair information, such as: * binStep: The bin step of the LBPair * LBPair: The address of the LBPair * createdByOwner: Whether the pair was created by the owner of the factory * ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding */ struct LBPairInformation { uint16 binStep; ILBPair LBPair; bool createdByOwner; bool ignoredForRouting; } event LBPairCreated( IERC20 indexed tokenX, IERC20 indexed tokenY, uint256 indexed binStep, ILBPair LBPair, uint256 pid ); event FeeRecipientSet(address oldRecipient, address newRecipient); event FlashLoanFeeSet(uint256 oldFlashLoanFee, uint256 newFlashLoanFee); event LBPairImplementationSet(address oldLBPairImplementation, address LBPairImplementation); event LBPairIgnoredStateChanged(ILBPair indexed LBPair, bool ignored); event PresetSet( uint256 indexed binStep, uint256 baseFactor, uint256 filterPeriod, uint256 decayPeriod, uint256 reductionFactor, uint256 variableFeeControl, uint256 protocolShare, uint256 maxVolatilityAccumulator ); event PresetOpenStateChanged(uint256 indexed binStep, bool indexed isOpen); event PresetRemoved(uint256 indexed binStep); event QuoteAssetAdded(IERC20 indexed quoteAsset); event QuoteAssetRemoved(IERC20 indexed quoteAsset); function getMinBinStep() external pure returns (uint256); function getFeeRecipient() external view returns (address); function getMaxFlashLoanFee() external pure returns (uint256); function getFlashLoanFee() external view returns (uint256); function getLBPairImplementation() external view returns (address); function getNumberOfLBPairs() external view returns (uint256); function getLBPairAtIndex(uint256 id) external returns (ILBPair); function getNumberOfQuoteAssets() external view returns (uint256); function getQuoteAssetAtIndex(uint256 index) external view returns (IERC20); function isQuoteAsset(IERC20 token) external view returns (bool); function getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep) external view returns (LBPairInformation memory); function getPreset(uint256 binStep) external view returns ( uint256 baseFactor, uint256 filterPeriod, uint256 decayPeriod, uint256 reductionFactor, uint256 variableFeeControl, uint256 protocolShare, uint256 maxAccumulator, bool isOpen ); function getAllBinSteps() external view returns (uint256[] memory presetsBinStep); function getOpenBinSteps() external view returns (uint256[] memory openBinStep); function getAllLBPairs(IERC20 tokenX, IERC20 tokenY) external view returns (LBPairInformation[] memory LBPairsBinStep); function setLBPairImplementation(address lbPairImplementation) external; function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep) external returns (ILBPair pair); function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external; function setPreset( uint16 binStep, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator, bool isOpen ) external; function setPresetOpenState(uint16 binStep, bool isOpen) external; function removePreset(uint16 binStep) external; function setFeesParametersOnPair( IERC20 tokenX, IERC20 tokenY, uint16 binStep, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) external; function setLBHooksParametersOnPair( IERC20 tokenX, IERC20 tokenY, uint16 binStep, bytes32 hooksParameters, bytes memory onHooksSetData ) external; function removeLBHooksOnPair(IERC20 tokenX, IERC20 tokenY, uint16 binStep) external; function setFeeRecipient(address feeRecipient) external; function setFlashLoanFee(uint256 flashLoanFee) external; function addQuoteAsset(IERC20 quoteAsset) external; function removeQuoteAsset(IERC20 quoteAsset) external; function forceDecay(ILBPair lbPair) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title Liquidity Book Flashloan Callback Interface /// @author Trader Joe /// @notice Required interface to interact with LB flash loans interface ILBFlashLoanCallback { function LBFlashLoanCallback( address sender, IERC20 tokenX, IERC20 tokenY, bytes32 amounts, bytes32 totalFees, bytes calldata data ) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Token Interface * @author Trader Joe * @notice Interface to interact with the LBToken. */ interface ILBToken { error LBToken__AddressThisOrZero(); error LBToken__InvalidLength(); error LBToken__SelfApproval(address owner); error LBToken__SpenderNotApproved(address from, address spender); error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount); error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount); event TransferBatch( address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts ); event ApprovalForAll(address indexed account, address indexed sender, bool approved); function name() external view returns (string memory); function symbol() external view returns (string memory); function totalSupply(uint256 id) external view returns (uint256); function balanceOf(address account, uint256 id) external view returns (uint256); function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); function isApprovedForAll(address owner, address spender) external view returns (bool); function approveForAll(address spender, bool approved) external; function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {BitMath} from "./BitMath.sol"; /** * @title Liquidity Book Uint256x256 Math Library * @author Trader Joe * @notice Helper contract used for full precision calculations */ library Uint256x256Math { error Uint256x256Math__MulShiftOverflow(); error Uint256x256Math__MulDivOverflow(); /** * @notice Calculates floor(x*y/denominator) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The denominator cannot be zero * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function mulDivRoundDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { (uint256 prod0, uint256 prod1) = _getMulProds(x, y); return _getEndOfDivRoundDown(x, y, denominator, prod0, prod1); } /** * @notice Calculates ceil(x*y/denominator) with full precision * The result will be rounded up * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The denominator cannot be zero * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function mulDivRoundUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { result = mulDivRoundDown(x, y, denominator); if (mulmod(x, y, denominator) != 0) result += 1; } /** * @notice Calculates floor(x * y / 2**offset) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param offset The offset as an uint256, can't be greater than 256 * @return result The result as an uint256 */ function mulShiftRoundDown(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) { (uint256 prod0, uint256 prod1) = _getMulProds(x, y); if (prod0 != 0) result = prod0 >> offset; if (prod1 != 0) { // Make sure the result is less than 2^256. if (prod1 >= 1 << offset) revert Uint256x256Math__MulShiftOverflow(); unchecked { result += prod1 << (256 - offset); } } } /** * @notice Calculates floor(x * y / 2**offset) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param offset The offset as an uint256, can't be greater than 256 * @return result The result as an uint256 */ function mulShiftRoundUp(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) { result = mulShiftRoundDown(x, y, offset); if (mulmod(x, y, 1 << offset) != 0) result += 1; } /** * @notice Calculates floor(x << offset / y) with full precision * The result will be rounded down * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param offset The number of bit to shift x as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function shiftDivRoundDown(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) { uint256 prod0; uint256 prod1; prod0 = x << offset; // Least significant 256 bits of the product unchecked { prod1 = x >> (256 - offset); // Most significant 256 bits of the product } return _getEndOfDivRoundDown(x, 1 << offset, denominator, prod0, prod1); } /** * @notice Calculates ceil(x << offset / y) with full precision * The result will be rounded up * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv * Requirements: * - The offset needs to be strictly lower than 256 * - The result must fit within uint256 * Caveats: * - This function does not work with fixed-point numbers * @param x The multiplicand as an uint256 * @param offset The number of bit to shift x as an uint256 * @param denominator The divisor as an uint256 * @return result The result as an uint256 */ function shiftDivRoundUp(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) { result = shiftDivRoundDown(x, offset, denominator); if (mulmod(x, 1 << offset, denominator) != 0) result += 1; } /** * @notice Helper function to return the result of `x * y` as 2 uint256 * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @return prod0 The least significant 256 bits of the product * @return prod1 The most significant 256 bits of the product */ function _getMulProds(uint256 x, uint256 y) private pure returns (uint256 prod0, uint256 prod1) { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } } /** * @notice Helper function to return the result of `x * y / denominator` with full precision * @param x The multiplicand as an uint256 * @param y The multiplier as an uint256 * @param denominator The divisor as an uint256 * @param prod0 The least significant 256 bits of the product * @param prod1 The most significant 256 bits of the product * @return result The result as an uint256 */ function _getEndOfDivRoundDown(uint256 x, uint256 y, uint256 denominator, uint256 prod0, uint256 prod1) private pure returns (uint256 result) { // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { unchecked { result = prod0 / denominator; } } else { // Make sure the result is less than 2^256. Also prevents denominator == 0 if (prod1 >= denominator) revert Uint256x256Math__MulDivOverflow(); // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1 // See https://cs.stackexchange.com/q/138556/92363 unchecked { // Does not overflow because the denominator cannot be zero at this stage in the function uint256 lpotdod = denominator & (~denominator + 1); assembly { // Divide denominator by lpotdod. denominator := div(denominator, lpotdod) // Divide [prod1 prod0] by lpotdod. prod0 := div(prod0, lpotdod) // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one lpotdod := add(div(sub(0, lpotdod), lpotdod), 1) } // Shift in bits from prod1 into prod0 prod0 |= prod1 * lpotdod; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4 uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; } } } /** * @notice Calculates the square root of x * @dev Credit to OpenZeppelin's Math library under MIT license */ function sqrt(uint256 x) internal pure returns (uint256 sqrtX) { if (x == 0) return 0; uint256 msb = BitMath.mostSignificantBit(x); assembly { sqrtX := shl(shr(1, msb), 1) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) sqrtX := shr(1, add(sqrtX, div(x, sqrtX))) x := div(x, sqrtX) } return sqrtX < x ? sqrtX : x; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {Uint128x128Math} from "./math/Uint128x128Math.sol"; import {Uint256x256Math} from "./math/Uint256x256Math.sol"; import {SafeCast} from "./math/SafeCast.sol"; import {Constants} from "./Constants.sol"; /** * @title Liquidity Book Price Helper Library * @author Trader Joe * @notice This library contains functions to calculate prices */ library PriceHelper { using Uint128x128Math for uint256; using Uint256x256Math for uint256; using SafeCast for uint256; int256 private constant REAL_ID_SHIFT = 1 << 23; /** * @dev Calculates the price from the id and the bin step * @param id The id * @param binStep The bin step * @return price The price as a 128.128-binary fixed-point number */ function getPriceFromId(uint24 id, uint16 binStep) internal pure returns (uint256 price) { uint256 base = getBase(binStep); int256 exponent = getExponent(id); price = base.pow(exponent); } /** * @dev Calculates the id from the price and the bin step * @param price The price as a 128.128-binary fixed-point number * @param binStep The bin step * @return id The id */ function getIdFromPrice(uint256 price, uint16 binStep) internal pure returns (uint24 id) { uint256 base = getBase(binStep); int256 realId = price.log2() / base.log2(); unchecked { id = uint256(REAL_ID_SHIFT + realId).safe24(); } } /** * @dev Calculates the base from the bin step, which is `1 + binStep / BASIS_POINT_MAX` * @param binStep The bin step * @return base The base */ function getBase(uint16 binStep) internal pure returns (uint256) { unchecked { return Constants.SCALE + (uint256(binStep) << Constants.SCALE_OFFSET) / Constants.BASIS_POINT_MAX; } } /** * @dev Calculates the exponent from the id, which is `id - REAL_ID_SHIFT` * @param id The id * @return exponent The exponent */ function getExponent(uint24 id) internal pure returns (int256) { unchecked { return int256(uint256(id)) - REAL_ID_SHIFT; } } /** * @dev Converts a price with 18 decimals to a 128.128-binary fixed-point number * @param price The price with 18 decimals * @return price128x128 The 128.128-binary fixed-point number */ function convertDecimalPriceTo128x128(uint256 price) internal pure returns (uint256) { return price.shiftDivRoundDown(Constants.SCALE_OFFSET, Constants.PRECISION); } /** * @dev Converts a 128.128-binary fixed-point number to a price with 18 decimals * @param price128x128 The 128.128-binary fixed-point number * @return price The price with 18 decimals */ function convert128x128PriceToDecimal(uint256 price128x128) internal pure returns (uint256) { return price128x128.mulShiftRoundDown(Constants.PRECISION, Constants.SCALE_OFFSET); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {PackedUint128Math} from "./math/PackedUint128Math.sol"; import {Uint256x256Math} from "./math/Uint256x256Math.sol"; import {SafeCast} from "./math/SafeCast.sol"; import {Constants} from "./Constants.sol"; import {PairParameterHelper} from "./PairParameterHelper.sol"; import {FeeHelper} from "./FeeHelper.sol"; import {PriceHelper} from "./PriceHelper.sol"; /** * @title Liquidity Book Bin Helper Library * @author Trader Joe * @notice This library contains functions to help interaction with bins. */ library BinHelper { using PackedUint128Math for bytes32; using PackedUint128Math for uint128; using Uint256x256Math for uint256; using PriceHelper for uint24; using SafeCast for uint256; using PairParameterHelper for bytes32; using FeeHelper for uint128; using SafeERC20 for IERC20; error BinHelper__CompositionFactorFlawed(uint24 id); error BinHelper__LiquidityOverflow(); /** * @dev Returns the amount of tokens that will be received when burning the given amount of liquidity * @param binReserves The reserves of the bin * @param amountToBurn The amount of liquidity to burn * @param totalSupply The total supply of the liquidity book * @return amountsOut The encoded amount of tokens that will be received */ function getAmountOutOfBin(bytes32 binReserves, uint256 amountToBurn, uint256 totalSupply) internal pure returns (bytes32 amountsOut) { (uint128 binReserveX, uint128 binReserveY) = binReserves.decode(); uint128 amountXOutFromBin; uint128 amountYOutFromBin; if (binReserveX > 0) { amountXOutFromBin = (amountToBurn.mulDivRoundDown(binReserveX, totalSupply)).safe128(); } if (binReserveY > 0) { amountYOutFromBin = (amountToBurn.mulDivRoundDown(binReserveY, totalSupply)).safe128(); } amountsOut = amountXOutFromBin.encode(amountYOutFromBin); } /** * @dev Returns the share and the effective amounts in when adding liquidity * @param binReserves The reserves of the bin * @param amountsIn The amounts of tokens to add * @param price The price of the bin * @param totalSupply The total supply of the liquidity book * @return shares The share of the liquidity book that the user will receive * @return effectiveAmountsIn The encoded effective amounts of tokens that the user will add. * This is the amount of tokens that the user will actually add to the liquidity book, * and will always be less than or equal to the amountsIn. */ function getSharesAndEffectiveAmountsIn(bytes32 binReserves, bytes32 amountsIn, uint256 price, uint256 totalSupply) internal pure returns (uint256 shares, bytes32 effectiveAmountsIn) { (uint256 x, uint256 y) = amountsIn.decode(); uint256 userLiquidity = getLiquidity(x, y, price); if (userLiquidity == 0) return (0, 0); uint256 binLiquidity = getLiquidity(binReserves, price); if (binLiquidity == 0 || totalSupply == 0) return (userLiquidity.sqrt(), amountsIn); shares = userLiquidity.mulDivRoundDown(totalSupply, binLiquidity); uint256 effectiveLiquidity = shares.mulDivRoundUp(binLiquidity, totalSupply); if (userLiquidity > effectiveLiquidity) { uint256 deltaLiquidity = userLiquidity - effectiveLiquidity; // The other way might be more efficient, but as y is the quote asset, it is more valuable if (deltaLiquidity >= Constants.SCALE) { uint256 deltaY = deltaLiquidity >> Constants.SCALE_OFFSET; deltaY = deltaY > y ? y : deltaY; y -= deltaY; deltaLiquidity -= deltaY << Constants.SCALE_OFFSET; } if (deltaLiquidity >= price) { uint256 deltaX = deltaLiquidity / price; deltaX = deltaX > x ? x : deltaX; x -= deltaX; } amountsIn = uint128(x).encode(uint128(y)); } return (shares, amountsIn); } /** * @dev Returns the amount of liquidity following the constant sum formula `L = price * x + y` * @param amounts The amounts of tokens * @param price The price of the bin * @return liquidity The amount of liquidity */ function getLiquidity(bytes32 amounts, uint256 price) internal pure returns (uint256 liquidity) { (uint256 x, uint256 y) = amounts.decode(); return getLiquidity(x, y, price); } /** * @dev Returns the amount of liquidity following the constant sum formula `L = price * x + y` * @param x The amount of the token X * @param y The amount of the token Y * @param price The price of the bin * @return liquidity The amount of liquidity */ function getLiquidity(uint256 x, uint256 y, uint256 price) internal pure returns (uint256 liquidity) { if (x > 0) { unchecked { liquidity = price * x; if (liquidity / x != price) revert BinHelper__LiquidityOverflow(); } } if (y > 0) { unchecked { y <<= Constants.SCALE_OFFSET; liquidity += y; if (liquidity < y) revert BinHelper__LiquidityOverflow(); } } return liquidity; } /** * @dev Verify that the amounts are correct and that the composition factor is not flawed * @param amounts The amounts of tokens * @param activeId The id of the active bin * @param id The id of the bin */ function verifyAmounts(bytes32 amounts, uint24 activeId, uint24 id) internal pure { if (id < activeId && (amounts << 128) > 0 || id > activeId && uint256(amounts) > type(uint128).max) { revert BinHelper__CompositionFactorFlawed(id); } } /** * @dev Returns the composition fees when adding liquidity to the active bin with a different * composition factor than the bin's one, as it does an implicit swap * @param binReserves The reserves of the bin * @param parameters The parameters of the liquidity book * @param binStep The step of the bin * @param amountsIn The amounts of tokens to add * @param totalSupply The total supply of the liquidity book * @param shares The share of the liquidity book that the user will receive * @return fees The encoded fees that will be charged */ function getCompositionFees( bytes32 binReserves, bytes32 parameters, uint16 binStep, bytes32 amountsIn, uint256 totalSupply, uint256 shares ) internal pure returns (bytes32 fees) { if (shares == 0) return 0; (uint128 amountX, uint128 amountY) = amountsIn.decode(); (uint128 receivedAmountX, uint128 receivedAmountY) = getAmountOutOfBin(binReserves.add(amountsIn), shares, totalSupply + shares).decode(); if (receivedAmountX > amountX) { uint128 feeY = (amountY - receivedAmountY).getCompositionFee(parameters.getTotalFee(binStep)); fees = feeY.encodeSecond(); } else if (receivedAmountY > amountY) { uint128 feeX = (amountX - receivedAmountX).getCompositionFee(parameters.getTotalFee(binStep)); fees = feeX.encodeFirst(); } } /** * @dev Returns whether the bin is empty (true) or not (false) * @param binReserves The reserves of the bin * @param isX Whether the reserve to check is the X reserve (true) or the Y reserve (false) * @return Whether the bin is empty (true) or not (false) */ function isEmpty(bytes32 binReserves, bool isX) internal pure returns (bool) { return isX ? binReserves.decodeX() == 0 : binReserves.decodeY() == 0; } /** * @dev Returns the amounts of tokens that will be added and removed from the bin during a swap * along with the fees that will be charged * @param binReserves The reserves of the bin * @param parameters The parameters of the liquidity book * @param binStep The step of the bin * @param swapForY Whether the swap is for Y (true) or for X (false) * @param activeId The id of the active bin * @param amountsInLeft The amounts of tokens left to swap * @return amountsInWithFees The encoded amounts of tokens that will be added to the bin, including fees * @return amountsOutOfBin The encoded amounts of tokens that will be removed from the bin * @return totalFees The encoded fees that will be charged */ function getAmounts( bytes32 binReserves, bytes32 parameters, uint16 binStep, bool swapForY, // swap `swapForY` and `activeId` to avoid stack too deep uint24 activeId, bytes32 amountsInLeft ) internal pure returns (bytes32 amountsInWithFees, bytes32 amountsOutOfBin, bytes32 totalFees) { uint256 price = activeId.getPriceFromId(binStep); uint128 binReserveOut = binReserves.decode(!swapForY); uint128 maxAmountIn = swapForY ? uint256(binReserveOut).shiftDivRoundUp(Constants.SCALE_OFFSET, price).safe128() : uint256(binReserveOut).mulShiftRoundUp(price, Constants.SCALE_OFFSET).safe128(); uint128 totalFee = parameters.getTotalFee(binStep); uint128 maxFee = maxAmountIn.getFeeAmount(totalFee); maxAmountIn += maxFee; uint128 amountIn128 = amountsInLeft.decode(swapForY); uint128 fee128; uint128 amountOut128; if (amountIn128 >= maxAmountIn) { fee128 = maxFee; amountIn128 = maxAmountIn; amountOut128 = binReserveOut; } else { fee128 = amountIn128.getFeeAmountFrom(totalFee); uint256 amountIn = amountIn128 - fee128; amountOut128 = swapForY ? uint256(amountIn).mulShiftRoundDown(price, Constants.SCALE_OFFSET).safe128() : uint256(amountIn).shiftDivRoundDown(Constants.SCALE_OFFSET, price).safe128(); if (amountOut128 > binReserveOut) amountOut128 = binReserveOut; } (amountsInWithFees, amountsOutOfBin, totalFees) = swapForY ? (amountIn128.encodeFirst(), amountOut128.encodeSecond(), fee128.encodeFirst()) : (amountIn128.encodeSecond(), amountOut128.encodeFirst(), fee128.encodeSecond()); } /** * @dev Returns the encoded amounts that were transferred to the contract * @param reserves The reserves * @param tokenX The token X * @param tokenY The token Y * @return amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: amountY */ function received(bytes32 reserves, IERC20 tokenX, IERC20 tokenY) internal view returns (bytes32 amounts) { amounts = _balanceOf(tokenX).encode(_balanceOf(tokenY)).sub(reserves); } /** * @dev Returns the encoded amounts that were transferred to the contract, only for token X * @param reserves The reserves * @param tokenX The token X * @return amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: empty */ function receivedX(bytes32 reserves, IERC20 tokenX) internal view returns (bytes32) { uint128 reserveX = reserves.decodeX(); return (_balanceOf(tokenX) - reserveX).encodeFirst(); } /** * @dev Returns the encoded amounts that were transferred to the contract, only for token Y * @param reserves The reserves * @param tokenY The token Y * @return amounts The amounts, encoded as follows: * [0 - 128[: empty * [128 - 256[: amountY */ function receivedY(bytes32 reserves, IERC20 tokenY) internal view returns (bytes32) { uint128 reserveY = reserves.decodeY(); return (_balanceOf(tokenY) - reserveY).encodeSecond(); } /** * @dev Transfers the encoded amounts to the recipient * @param amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: amountY * @param tokenX The token X * @param tokenY The token Y * @param recipient The recipient */ function transfer(bytes32 amounts, IERC20 tokenX, IERC20 tokenY, address recipient) internal { (uint128 amountX, uint128 amountY) = amounts.decode(); if (amountX > 0) tokenX.safeTransfer(recipient, amountX); if (amountY > 0) tokenY.safeTransfer(recipient, amountY); } /** * @dev Transfers the encoded amounts to the recipient, only for token X * @param amounts The amounts, encoded as follows: * [0 - 128[: amountX * [128 - 256[: empty * @param tokenX The token X * @param recipient The recipient */ function transferX(bytes32 amounts, IERC20 tokenX, address recipient) internal { uint128 amountX = amounts.decodeX(); if (amountX > 0) tokenX.safeTransfer(recipient, amountX); } /** * @dev Transfers the encoded amounts to the recipient, only for token Y * @param amounts The amounts, encoded as follows: * [0 - 128[: empty * [128 - 256[: amountY * @param tokenY The token Y * @param recipient The recipient */ function transferY(bytes32 amounts, IERC20 tokenY, address recipient) internal { uint128 amountY = amounts.decodeY(); if (amountY > 0) tokenY.safeTransfer(recipient, amountY); } function _balanceOf(IERC20 token) private view returns (uint128) { return token.balanceOf(address(this)).safe128(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Safe Cast Library * @author Trader Joe * @notice This library contains functions to safely cast uint256 to different uint types. */ library SafeCast { error SafeCast__Exceeds248Bits(); error SafeCast__Exceeds240Bits(); error SafeCast__Exceeds232Bits(); error SafeCast__Exceeds224Bits(); error SafeCast__Exceeds216Bits(); error SafeCast__Exceeds208Bits(); error SafeCast__Exceeds200Bits(); error SafeCast__Exceeds192Bits(); error SafeCast__Exceeds184Bits(); error SafeCast__Exceeds176Bits(); error SafeCast__Exceeds168Bits(); error SafeCast__Exceeds160Bits(); error SafeCast__Exceeds152Bits(); error SafeCast__Exceeds144Bits(); error SafeCast__Exceeds136Bits(); error SafeCast__Exceeds128Bits(); error SafeCast__Exceeds120Bits(); error SafeCast__Exceeds112Bits(); error SafeCast__Exceeds104Bits(); error SafeCast__Exceeds96Bits(); error SafeCast__Exceeds88Bits(); error SafeCast__Exceeds80Bits(); error SafeCast__Exceeds72Bits(); error SafeCast__Exceeds64Bits(); error SafeCast__Exceeds56Bits(); error SafeCast__Exceeds48Bits(); error SafeCast__Exceeds40Bits(); error SafeCast__Exceeds32Bits(); error SafeCast__Exceeds24Bits(); error SafeCast__Exceeds16Bits(); error SafeCast__Exceeds8Bits(); /** * @dev Returns x on uint248 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint248 */ function safe248(uint256 x) internal pure returns (uint248 y) { if ((y = uint248(x)) != x) revert SafeCast__Exceeds248Bits(); } /** * @dev Returns x on uint240 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint240 */ function safe240(uint256 x) internal pure returns (uint240 y) { if ((y = uint240(x)) != x) revert SafeCast__Exceeds240Bits(); } /** * @dev Returns x on uint232 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint232 */ function safe232(uint256 x) internal pure returns (uint232 y) { if ((y = uint232(x)) != x) revert SafeCast__Exceeds232Bits(); } /** * @dev Returns x on uint224 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint224 */ function safe224(uint256 x) internal pure returns (uint224 y) { if ((y = uint224(x)) != x) revert SafeCast__Exceeds224Bits(); } /** * @dev Returns x on uint216 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint216 */ function safe216(uint256 x) internal pure returns (uint216 y) { if ((y = uint216(x)) != x) revert SafeCast__Exceeds216Bits(); } /** * @dev Returns x on uint208 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint208 */ function safe208(uint256 x) internal pure returns (uint208 y) { if ((y = uint208(x)) != x) revert SafeCast__Exceeds208Bits(); } /** * @dev Returns x on uint200 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint200 */ function safe200(uint256 x) internal pure returns (uint200 y) { if ((y = uint200(x)) != x) revert SafeCast__Exceeds200Bits(); } /** * @dev Returns x on uint192 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint192 */ function safe192(uint256 x) internal pure returns (uint192 y) { if ((y = uint192(x)) != x) revert SafeCast__Exceeds192Bits(); } /** * @dev Returns x on uint184 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint184 */ function safe184(uint256 x) internal pure returns (uint184 y) { if ((y = uint184(x)) != x) revert SafeCast__Exceeds184Bits(); } /** * @dev Returns x on uint176 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint176 */ function safe176(uint256 x) internal pure returns (uint176 y) { if ((y = uint176(x)) != x) revert SafeCast__Exceeds176Bits(); } /** * @dev Returns x on uint168 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint168 */ function safe168(uint256 x) internal pure returns (uint168 y) { if ((y = uint168(x)) != x) revert SafeCast__Exceeds168Bits(); } /** * @dev Returns x on uint160 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint160 */ function safe160(uint256 x) internal pure returns (uint160 y) { if ((y = uint160(x)) != x) revert SafeCast__Exceeds160Bits(); } /** * @dev Returns x on uint152 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint152 */ function safe152(uint256 x) internal pure returns (uint152 y) { if ((y = uint152(x)) != x) revert SafeCast__Exceeds152Bits(); } /** * @dev Returns x on uint144 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint144 */ function safe144(uint256 x) internal pure returns (uint144 y) { if ((y = uint144(x)) != x) revert SafeCast__Exceeds144Bits(); } /** * @dev Returns x on uint136 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint136 */ function safe136(uint256 x) internal pure returns (uint136 y) { if ((y = uint136(x)) != x) revert SafeCast__Exceeds136Bits(); } /** * @dev Returns x on uint128 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint128 */ function safe128(uint256 x) internal pure returns (uint128 y) { if ((y = uint128(x)) != x) revert SafeCast__Exceeds128Bits(); } /** * @dev Returns x on uint120 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint120 */ function safe120(uint256 x) internal pure returns (uint120 y) { if ((y = uint120(x)) != x) revert SafeCast__Exceeds120Bits(); } /** * @dev Returns x on uint112 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint112 */ function safe112(uint256 x) internal pure returns (uint112 y) { if ((y = uint112(x)) != x) revert SafeCast__Exceeds112Bits(); } /** * @dev Returns x on uint104 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint104 */ function safe104(uint256 x) internal pure returns (uint104 y) { if ((y = uint104(x)) != x) revert SafeCast__Exceeds104Bits(); } /** * @dev Returns x on uint96 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint96 */ function safe96(uint256 x) internal pure returns (uint96 y) { if ((y = uint96(x)) != x) revert SafeCast__Exceeds96Bits(); } /** * @dev Returns x on uint88 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint88 */ function safe88(uint256 x) internal pure returns (uint88 y) { if ((y = uint88(x)) != x) revert SafeCast__Exceeds88Bits(); } /** * @dev Returns x on uint80 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint80 */ function safe80(uint256 x) internal pure returns (uint80 y) { if ((y = uint80(x)) != x) revert SafeCast__Exceeds80Bits(); } /** * @dev Returns x on uint72 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint72 */ function safe72(uint256 x) internal pure returns (uint72 y) { if ((y = uint72(x)) != x) revert SafeCast__Exceeds72Bits(); } /** * @dev Returns x on uint64 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint64 */ function safe64(uint256 x) internal pure returns (uint64 y) { if ((y = uint64(x)) != x) revert SafeCast__Exceeds64Bits(); } /** * @dev Returns x on uint56 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint56 */ function safe56(uint256 x) internal pure returns (uint56 y) { if ((y = uint56(x)) != x) revert SafeCast__Exceeds56Bits(); } /** * @dev Returns x on uint48 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint48 */ function safe48(uint256 x) internal pure returns (uint48 y) { if ((y = uint48(x)) != x) revert SafeCast__Exceeds48Bits(); } /** * @dev Returns x on uint40 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint40 */ function safe40(uint256 x) internal pure returns (uint40 y) { if ((y = uint40(x)) != x) revert SafeCast__Exceeds40Bits(); } /** * @dev Returns x on uint32 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint32 */ function safe32(uint256 x) internal pure returns (uint32 y) { if ((y = uint32(x)) != x) revert SafeCast__Exceeds32Bits(); } /** * @dev Returns x on uint24 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint24 */ function safe24(uint256 x) internal pure returns (uint24 y) { if ((y = uint24(x)) != x) revert SafeCast__Exceeds24Bits(); } /** * @dev Returns x on uint16 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint16 */ function safe16(uint256 x) internal pure returns (uint16 y) { if ((y = uint16(x)) != x) revert SafeCast__Exceeds16Bits(); } /** * @dev Returns x on uint8 and check that it does not overflow * @param x The value as an uint256 * @return y The value as an uint8 */ function safe8(uint256 x) internal pure returns (uint8 y) { if ((y = uint8(x)) != x) revert SafeCast__Exceeds8Bits(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {ILBPair} from "./ILBPair.sol"; import {Hooks} from "../libraries/Hooks.sol"; interface ILBHooks { function getLBPair() external view returns (ILBPair); function isLinked() external view returns (bool); function onHooksSet(bytes32 hooksParameters, bytes calldata onHooksSetData) external returns (bytes4); function beforeSwap(address sender, address to, bool swapForY, bytes32 amountsIn) external returns (bytes4); function afterSwap(address sender, address to, bool swapForY, bytes32 amountsOut) external returns (bytes4); function beforeFlashLoan(address sender, address to, bytes32 amounts) external returns (bytes4); function afterFlashLoan(address sender, address to, bytes32 fees, bytes32 feesReceived) external returns (bytes4); function beforeMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsReceived) external returns (bytes4); function afterMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsIn) external returns (bytes4); function beforeBurn( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) external returns (bytes4); function afterBurn( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn ) external returns (bytes4); function beforeBatchTransferFrom( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) external returns (bytes4); function afterBatchTransferFrom( address sender, address from, address to, uint256[] calldata ids, uint256[] calldata amounts ) external returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Bit Math Library * @author Trader Joe * @notice Helper contract used for bit calculations */ library BitMath { /** * @dev Returns the index of the closest bit on the right of x that is non null * @param x The value as a uint256 * @param bit The index of the bit to start searching at * @return id The index of the closest non null bit on the right of x. * If there is no closest bit, it returns max(uint256) */ function closestBitRight(uint256 x, uint8 bit) internal pure returns (uint256 id) { unchecked { uint256 shift = 255 - bit; x <<= shift; // can't overflow as it's non-zero and we shifted it by `_shift` return (x == 0) ? type(uint256).max : mostSignificantBit(x) - shift; } } /** * @dev Returns the index of the closest bit on the left of x that is non null * @param x The value as a uint256 * @param bit The index of the bit to start searching at * @return id The index of the closest non null bit on the left of x. * If there is no closest bit, it returns max(uint256) */ function closestBitLeft(uint256 x, uint8 bit) internal pure returns (uint256 id) { unchecked { x >>= bit; return (x == 0) ? type(uint256).max : leastSignificantBit(x) + bit; } } /** * @dev Returns the index of the most significant bit of x * This function returns 0 if x is 0 * @param x The value as a uint256 * @return msb The index of the most significant bit of x */ function mostSignificantBit(uint256 x) internal pure returns (uint8 msb) { assembly { if gt(x, 0xffffffffffffffffffffffffffffffff) { x := shr(128, x) msb := 128 } if gt(x, 0xffffffffffffffff) { x := shr(64, x) msb := add(msb, 64) } if gt(x, 0xffffffff) { x := shr(32, x) msb := add(msb, 32) } if gt(x, 0xffff) { x := shr(16, x) msb := add(msb, 16) } if gt(x, 0xff) { x := shr(8, x) msb := add(msb, 8) } if gt(x, 0xf) { x := shr(4, x) msb := add(msb, 4) } if gt(x, 0x3) { x := shr(2, x) msb := add(msb, 2) } if gt(x, 0x1) { msb := add(msb, 1) } } } /** * @dev Returns the index of the least significant bit of x * This function returns 255 if x is 0 * @param x The value as a uint256 * @return lsb The index of the least significant bit of x */ function leastSignificantBit(uint256 x) internal pure returns (uint8 lsb) { assembly { let sx := shl(128, x) if iszero(iszero(sx)) { lsb := 128 x := sx } sx := shl(64, x) if iszero(iszero(sx)) { x := sx lsb := add(lsb, 64) } sx := shl(32, x) if iszero(iszero(sx)) { x := sx lsb := add(lsb, 32) } sx := shl(16, x) if iszero(iszero(sx)) { x := sx lsb := add(lsb, 16) } sx := shl(8, x) if iszero(iszero(sx)) { x := sx lsb := add(lsb, 8) } sx := shl(4, x) if iszero(iszero(sx)) { x := sx lsb := add(lsb, 4) } sx := shl(2, x) if iszero(iszero(sx)) { x := sx lsb := add(lsb, 2) } if iszero(iszero(shl(1, x))) { lsb := add(lsb, 1) } lsb := sub(255, lsb) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {Constants} from "../Constants.sol"; import {BitMath} from "./BitMath.sol"; /** * @title Liquidity Book Uint128x128 Math Library * @author Trader Joe * @notice Helper contract used for power and log calculations */ library Uint128x128Math { using BitMath for uint256; error Uint128x128Math__LogUnderflow(); error Uint128x128Math__PowUnderflow(uint256 x, int256 y); uint256 constant LOG_SCALE_OFFSET = 127; uint256 constant LOG_SCALE = 1 << LOG_SCALE_OFFSET; uint256 constant LOG_SCALE_SQUARED = LOG_SCALE * LOG_SCALE; /** * @notice Calculates the binary logarithm of x. * @dev Based on the iterative approximation algorithm. * https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation * Requirements: * - x must be greater than zero. * Caveats: * - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation * Also because x is converted to an unsigned 129.127-binary fixed-point number during the operation to optimize the multiplication * @param x The unsigned 128.128-binary fixed-point number for which to calculate the binary logarithm. * @return result The binary logarithm as a signed 128.128-binary fixed-point number. */ function log2(uint256 x) internal pure returns (int256 result) { // Convert x to a unsigned 129.127-binary fixed-point number to optimize the multiplication. // If we use an offset of 128 bits, y would need 129 bits and y**2 would would overflow and we would have to // use mulDiv, by reducing x to 129.127-binary fixed-point number we assert that y will use 128 bits, and we // can use the regular multiplication if (x == 1) return -128; if (x == 0) revert Uint128x128Math__LogUnderflow(); x >>= 1; unchecked { // This works because log2(x) = -log2(1/x). int256 sign; if (x >= LOG_SCALE) { sign = 1; } else { sign = -1; // Do the fixed-point inversion inline to save gas x = LOG_SCALE_SQUARED / x; } // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n). uint256 n = (x >> LOG_SCALE_OFFSET).mostSignificantBit(); // The integer part of the logarithm as a signed 129.127-binary fixed-point number. The operation can't overflow // because n is maximum 255, LOG_SCALE_OFFSET is 127 bits and sign is either 1 or -1. result = int256(n) << LOG_SCALE_OFFSET; // This is y = x * 2^(-n). uint256 y = x >> n; // If y = 1, the fractional part is zero. if (y != LOG_SCALE) { // Calculate the fractional part via the iterative approximation. // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. for (int256 delta = int256(1 << (LOG_SCALE_OFFSET - 1)); delta > 0; delta >>= 1) { y = (y * y) >> LOG_SCALE_OFFSET; // Is y^2 > 2 and so in the range [2,4)? if (y >= 1 << (LOG_SCALE_OFFSET + 1)) { // Add the 2^(-m) factor to the logarithm. result += delta; // Corresponds to z/2 on Wikipedia. y >>= 1; } } } // Convert x back to unsigned 128.128-binary fixed-point number result = (result * sign) << 1; } } /** * @notice Returns the value of x^y. It calculates `1 / x^abs(y)` if x is bigger than 2^128. * At the end of the operations, we invert the result if needed. * @param x The unsigned 128.128-binary fixed-point number for which to calculate the power * @param y A relative number without any decimals, needs to be between ]-2^21; 2^21[ */ function pow(uint256 x, int256 y) internal pure returns (uint256 result) { bool invert; uint256 absY; if (y == 0) return Constants.SCALE; assembly { absY := y if slt(absY, 0) { absY := sub(0, absY) invert := iszero(invert) } } if (absY < 0x100000) { result = Constants.SCALE; assembly { let squared := x if gt(x, 0xffffffffffffffffffffffffffffffff) { squared := div(not(0), squared) invert := iszero(invert) } if and(absY, 0x1) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x2) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x4) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x8) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x10) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x20) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x40) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x80) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x100) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x200) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x400) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x800) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x1000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x2000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x4000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x8000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x10000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x20000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x40000) { result := shr(128, mul(result, squared)) } squared := shr(128, mul(squared, squared)) if and(absY, 0x80000) { result := shr(128, mul(result, squared)) } } } // revert if y is too big or if x^y underflowed if (result == 0) revert Uint128x128Math__PowUnderflow(x, y); return invert ? type(uint256).max / result : result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Constants Library * @author Trader Joe * @notice Set of constants for Liquidity Book contracts */ library Constants { uint8 internal constant SCALE_OFFSET = 128; uint256 internal constant SCALE = 1 << SCALE_OFFSET; uint256 internal constant PRECISION = 1e18; uint256 internal constant SQUARED_PRECISION = PRECISION * PRECISION; uint256 internal constant MAX_FEE = 0.1e18; // 10% uint256 internal constant MAX_PROTOCOL_SHARE = 2_500; // 25% of the fee uint256 internal constant BASIS_POINT_MAX = 10_000; /// @dev The expected return after a successful flash loan bytes32 internal constant CALLBACK_SUCCESS = keccak256("LBPair.onFlashLoan"); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {Constants} from "../Constants.sol"; /** * @title Liquidity Book Packed Uint128 Math Library * @author Trader Joe * @notice This library contains functions to encode and decode two uint128 into a single bytes32 * and interact with the encoded bytes32. */ library PackedUint128Math { error PackedUint128Math__AddOverflow(); error PackedUint128Math__SubUnderflow(); error PackedUint128Math__MultiplierTooLarge(); uint256 private constant OFFSET = 128; uint256 private constant MASK_128 = 0xffffffffffffffffffffffffffffffff; uint256 private constant MASK_128_PLUS_ONE = MASK_128 + 1; /** * @dev Encodes two uint128 into a single bytes32 * @param x1 The first uint128 * @param x2 The second uint128 * @return z The encoded bytes32 as follows: * [0 - 128[: x1 * [128 - 256[: x2 */ function encode(uint128 x1, uint128 x2) internal pure returns (bytes32 z) { assembly { z := or(and(x1, MASK_128), shl(OFFSET, x2)) } } /** * @dev Encodes a uint128 into a single bytes32 as the first uint128 * @param x1 The uint128 * @return z The encoded bytes32 as follows: * [0 - 128[: x1 * [128 - 256[: empty */ function encodeFirst(uint128 x1) internal pure returns (bytes32 z) { assembly { z := and(x1, MASK_128) } } /** * @dev Encodes a uint128 into a single bytes32 as the second uint128 * @param x2 The uint128 * @return z The encoded bytes32 as follows: * [0 - 128[: empty * [128 - 256[: x2 */ function encodeSecond(uint128 x2) internal pure returns (bytes32 z) { assembly { z := shl(OFFSET, x2) } } /** * @dev Encodes a uint128 into a single bytes32 as the first or second uint128 * @param x The uint128 * @param first Whether to encode as the first or second uint128 * @return z The encoded bytes32 as follows: * if first: * [0 - 128[: x * [128 - 256[: empty * else: * [0 - 128[: empty * [128 - 256[: x */ function encode(uint128 x, bool first) internal pure returns (bytes32 z) { return first ? encodeFirst(x) : encodeSecond(x); } /** * @dev Decodes a bytes32 into two uint128 * @param z The encoded bytes32 as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @return x1 The first uint128 * @return x2 The second uint128 */ function decode(bytes32 z) internal pure returns (uint128 x1, uint128 x2) { assembly { x1 := and(z, MASK_128) x2 := shr(OFFSET, z) } } /** * @dev Decodes a bytes32 into a uint128 as the first uint128 * @param z The encoded bytes32 as follows: * [0 - 128[: x * [128 - 256[: any * @return x The first uint128 */ function decodeX(bytes32 z) internal pure returns (uint128 x) { assembly { x := and(z, MASK_128) } } /** * @dev Decodes a bytes32 into a uint128 as the second uint128 * @param z The encoded bytes32 as follows: * [0 - 128[: any * [128 - 256[: y * @return y The second uint128 */ function decodeY(bytes32 z) internal pure returns (uint128 y) { assembly { y := shr(OFFSET, z) } } /** * @dev Decodes a bytes32 into a uint128 as the first or second uint128 * @param z The encoded bytes32 as follows: * if first: * [0 - 128[: x1 * [128 - 256[: empty * else: * [0 - 128[: empty * [128 - 256[: x2 * @param first Whether to decode as the first or second uint128 * @return x The decoded uint128 */ function decode(bytes32 z, bool first) internal pure returns (uint128 x) { return first ? decodeX(z) : decodeY(z); } /** * @dev Adds two encoded bytes32, reverting on overflow on any of the uint128 * @param x The first bytes32 encoded as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @param y The second bytes32 encoded as follows: * [0 - 128[: y1 * [128 - 256[: y2 * @return z The sum of x and y encoded as follows: * [0 - 128[: x1 + y1 * [128 - 256[: x2 + y2 */ function add(bytes32 x, bytes32 y) internal pure returns (bytes32 z) { assembly { z := add(x, y) } if (z < x || uint128(uint256(z)) < uint128(uint256(x))) { revert PackedUint128Math__AddOverflow(); } } /** * @dev Adds an encoded bytes32 and two uint128, reverting on overflow on any of the uint128 * @param x The bytes32 encoded as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @param y1 The first uint128 * @param y2 The second uint128 * @return z The sum of x and y encoded as follows: * [0 - 128[: x1 + y1 * [128 - 256[: x2 + y2 */ function add(bytes32 x, uint128 y1, uint128 y2) internal pure returns (bytes32) { return add(x, encode(y1, y2)); } /** * @dev Subtracts two encoded bytes32, reverting on underflow on any of the uint128 * @param x The first bytes32 encoded as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @param y The second bytes32 encoded as follows: * [0 - 128[: y1 * [128 - 256[: y2 * @return z The difference of x and y encoded as follows: * [0 - 128[: x1 - y1 * [128 - 256[: x2 - y2 */ function sub(bytes32 x, bytes32 y) internal pure returns (bytes32 z) { assembly { z := sub(x, y) } if (z > x || uint128(uint256(z)) > uint128(uint256(x))) { revert PackedUint128Math__SubUnderflow(); } } /** * @dev Subtracts an encoded bytes32 and two uint128, reverting on underflow on any of the uint128 * @param x The bytes32 encoded as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @param y1 The first uint128 * @param y2 The second uint128 * @return z The difference of x and y encoded as follows: * [0 - 128[: x1 - y1 * [128 - 256[: x2 - y2 */ function sub(bytes32 x, uint128 y1, uint128 y2) internal pure returns (bytes32) { return sub(x, encode(y1, y2)); } /** * @dev Returns whether any of the uint128 of x is strictly greater than the corresponding uint128 of y * @param x The first bytes32 encoded as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @param y The second bytes32 encoded as follows: * [0 - 128[: y1 * [128 - 256[: y2 * @return x1 < y1 || x2 < y2 */ function lt(bytes32 x, bytes32 y) internal pure returns (bool) { (uint128 x1, uint128 x2) = decode(x); (uint128 y1, uint128 y2) = decode(y); return x1 < y1 || x2 < y2; } /** * @dev Returns whether any of the uint128 of x is strictly greater than the corresponding uint128 of y * @param x The first bytes32 encoded as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @param y The second bytes32 encoded as follows: * [0 - 128[: y1 * [128 - 256[: y2 * @return x1 < y1 || x2 < y2 */ function gt(bytes32 x, bytes32 y) internal pure returns (bool) { (uint128 x1, uint128 x2) = decode(x); (uint128 y1, uint128 y2) = decode(y); return x1 > y1 || x2 > y2; } /** * @dev Multiplies an encoded bytes32 by a uint128 then divides the result by 10_000, rounding down * The result can't overflow as the multiplier needs to be smaller or equal to 10_000 * @param x The bytes32 encoded as follows: * [0 - 128[: x1 * [128 - 256[: x2 * @param multiplier The uint128 to multiply by (must be smaller or equal to 10_000) * @return z The product of x and multiplier encoded as follows: * [0 - 128[: floor((x1 * multiplier) / 10_000) * [128 - 256[: floor((x2 * multiplier) / 10_000) */ function scalarMulDivBasisPointRoundDown(bytes32 x, uint128 multiplier) internal pure returns (bytes32 z) { if (multiplier == 0) return 0; uint256 BASIS_POINT_MAX = Constants.BASIS_POINT_MAX; if (multiplier > BASIS_POINT_MAX) revert PackedUint128Math__MultiplierTooLarge(); (uint128 x1, uint128 x2) = decode(x); assembly { x1 := div(mul(x1, multiplier), BASIS_POINT_MAX) x2 := div(mul(x2, multiplier), BASIS_POINT_MAX) } return encode(x1, x2); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {Constants} from "./Constants.sol"; import {SafeCast} from "./math/SafeCast.sol"; import {Encoded} from "./math/Encoded.sol"; /** * @title Liquidity Book Pair Parameter Helper Library * @author Trader Joe * @dev This library contains functions to get and set parameters of a pair * The parameters are stored in a single bytes32 variable in the following format: * [0 - 16[: base factor (16 bits) * [16 - 28[: filter period (12 bits) * [28 - 40[: decay period (12 bits) * [40 - 54[: reduction factor (14 bits) * [54 - 78[: variable fee control (24 bits) * [78 - 92[: protocol share (14 bits) * [92 - 112[: max volatility accumulator (20 bits) * [112 - 132[: volatility accumulator (20 bits) * [132 - 152[: volatility reference (20 bits) * [152 - 176[: index reference (24 bits) * [176 - 216[: time of last update (40 bits) * [216 - 232[: oracle index (16 bits) * [232 - 256[: active index (24 bits) */ library PairParameterHelper { using SafeCast for uint256; using Encoded for bytes32; error PairParametersHelper__InvalidParameter(); uint256 internal constant OFFSET_BASE_FACTOR = 0; uint256 internal constant OFFSET_FILTER_PERIOD = 16; uint256 internal constant OFFSET_DECAY_PERIOD = 28; uint256 internal constant OFFSET_REDUCTION_FACTOR = 40; uint256 internal constant OFFSET_VAR_FEE_CONTROL = 54; uint256 internal constant OFFSET_PROTOCOL_SHARE = 78; uint256 internal constant OFFSET_MAX_VOL_ACC = 92; uint256 internal constant OFFSET_VOL_ACC = 112; uint256 internal constant OFFSET_VOL_REF = 132; uint256 internal constant OFFSET_ID_REF = 152; uint256 internal constant OFFSET_TIME_LAST_UPDATE = 176; uint256 internal constant OFFSET_ORACLE_ID = 216; uint256 internal constant OFFSET_ACTIVE_ID = 232; uint256 internal constant MASK_STATIC_PARAMETER = 0xffffffffffffffffffffffffffff; /** * @dev Get the base factor from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 16[: base factor (16 bits) * [16 - 256[: other parameters * @return baseFactor The base factor */ function getBaseFactor(bytes32 params) internal pure returns (uint16 baseFactor) { baseFactor = params.decodeUint16(OFFSET_BASE_FACTOR); } /** * @dev Get the filter period from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 16[: other parameters * [16 - 28[: filter period (12 bits) * [28 - 256[: other parameters * @return filterPeriod The filter period */ function getFilterPeriod(bytes32 params) internal pure returns (uint16 filterPeriod) { filterPeriod = params.decodeUint12(OFFSET_FILTER_PERIOD); } /** * @dev Get the decay period from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 28[: other parameters * [28 - 40[: decay period (12 bits) * [40 - 256[: other parameters * @return decayPeriod The decay period */ function getDecayPeriod(bytes32 params) internal pure returns (uint16 decayPeriod) { decayPeriod = params.decodeUint12(OFFSET_DECAY_PERIOD); } /** * @dev Get the reduction factor from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 40[: other parameters * [40 - 54[: reduction factor (14 bits) * [54 - 256[: other parameters * @return reductionFactor The reduction factor */ function getReductionFactor(bytes32 params) internal pure returns (uint16 reductionFactor) { reductionFactor = params.decodeUint14(OFFSET_REDUCTION_FACTOR); } /** * @dev Get the variable fee control from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 54[: other parameters * [54 - 78[: variable fee control (24 bits) * [78 - 256[: other parameters * @return variableFeeControl The variable fee control */ function getVariableFeeControl(bytes32 params) internal pure returns (uint24 variableFeeControl) { variableFeeControl = params.decodeUint24(OFFSET_VAR_FEE_CONTROL); } /** * @dev Get the protocol share from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 78[: other parameters * [78 - 92[: protocol share (14 bits) * [92 - 256[: other parameters * @return protocolShare The protocol share */ function getProtocolShare(bytes32 params) internal pure returns (uint16 protocolShare) { protocolShare = params.decodeUint14(OFFSET_PROTOCOL_SHARE); } /** * @dev Get the max volatility accumulator from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 92[: other parameters * [92 - 112[: max volatility accumulator (20 bits) * [112 - 256[: other parameters * @return maxVolatilityAccumulator The max volatility accumulator */ function getMaxVolatilityAccumulator(bytes32 params) internal pure returns (uint24 maxVolatilityAccumulator) { maxVolatilityAccumulator = params.decodeUint20(OFFSET_MAX_VOL_ACC); } /** * @dev Get the volatility accumulator from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 112[: other parameters * [112 - 132[: volatility accumulator (20 bits) * [132 - 256[: other parameters * @return volatilityAccumulator The volatility accumulator */ function getVolatilityAccumulator(bytes32 params) internal pure returns (uint24 volatilityAccumulator) { volatilityAccumulator = params.decodeUint20(OFFSET_VOL_ACC); } /** * @dev Get the volatility reference from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 132[: other parameters * [132 - 152[: volatility reference (20 bits) * [152 - 256[: other parameters * @return volatilityReference The volatility reference */ function getVolatilityReference(bytes32 params) internal pure returns (uint24 volatilityReference) { volatilityReference = params.decodeUint20(OFFSET_VOL_REF); } /** * @dev Get the index reference from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 152[: other parameters * [152 - 176[: index reference (24 bits) * [176 - 256[: other parameters * @return idReference The index reference */ function getIdReference(bytes32 params) internal pure returns (uint24 idReference) { idReference = params.decodeUint24(OFFSET_ID_REF); } /** * @dev Get the time of last update from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 176[: other parameters * [176 - 216[: time of last update (40 bits) * [216 - 256[: other parameters * @return timeOflastUpdate The time of last update */ function getTimeOfLastUpdate(bytes32 params) internal pure returns (uint40 timeOflastUpdate) { timeOflastUpdate = params.decodeUint40(OFFSET_TIME_LAST_UPDATE); } /** * @dev Get the oracle id from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 216[: other parameters * [216 - 232[: oracle id (16 bits) * [232 - 256[: other parameters * @return oracleId The oracle id */ function getOracleId(bytes32 params) internal pure returns (uint16 oracleId) { oracleId = params.decodeUint16(OFFSET_ORACLE_ID); } /** * @dev Get the active index from the encoded pair parameters * @param params The encoded pair parameters, as follows: * [0 - 232[: other parameters * [232 - 256[: active index (24 bits) * @return activeId The active index */ function getActiveId(bytes32 params) internal pure returns (uint24 activeId) { activeId = params.decodeUint24(OFFSET_ACTIVE_ID); } /** * @dev Get the delta between the current active index and the cached active index * @param params The encoded pair parameters, as follows: * [0 - 232[: other parameters * [232 - 256[: active index (24 bits) * @param activeId The current active index * @return The delta */ function getDeltaId(bytes32 params, uint24 activeId) internal pure returns (uint24) { uint24 id = getActiveId(params); unchecked { return activeId > id ? activeId - id : id - activeId; } } /** * @dev Calculates the base fee, with 18 decimals * @param params The encoded pair parameters * @param binStep The bin step (in basis points) * @return baseFee The base fee */ function getBaseFee(bytes32 params, uint16 binStep) internal pure returns (uint256) { unchecked { // Base factor is in basis points, binStep is in basis points, so we multiply by 1e10 return uint256(getBaseFactor(params)) * binStep * 1e10; } } /** * @dev Calculates the variable fee * @param params The encoded pair parameters * @param binStep The bin step (in basis points) * @return variableFee The variable fee */ function getVariableFee(bytes32 params, uint16 binStep) internal pure returns (uint256 variableFee) { uint256 variableFeeControl = getVariableFeeControl(params); if (variableFeeControl != 0) { unchecked { // The volatility accumulator is in basis points, binStep is in basis points, // and the variable fee control is in basis points, so the result is in 100e18th uint256 prod = uint256(getVolatilityAccumulator(params)) * binStep; variableFee = (prod * prod * variableFeeControl + 99) / 100; } } } /** * @dev Calculates the total fee, which is the sum of the base fee and the variable fee * @param params The encoded pair parameters * @param binStep The bin step (in basis points) * @return totalFee The total fee */ function getTotalFee(bytes32 params, uint16 binStep) internal pure returns (uint128) { unchecked { return (getBaseFee(params, binStep) + getVariableFee(params, binStep)).safe128(); } } /** * @dev Set the oracle id in the encoded pair parameters * @param params The encoded pair parameters * @param oracleId The oracle id * @return The updated encoded pair parameters */ function setOracleId(bytes32 params, uint16 oracleId) internal pure returns (bytes32) { return params.set(oracleId, Encoded.MASK_UINT16, OFFSET_ORACLE_ID); } /** * @dev Set the volatility reference in the encoded pair parameters * @param params The encoded pair parameters * @param volRef The volatility reference * @return The updated encoded pair parameters */ function setVolatilityReference(bytes32 params, uint24 volRef) internal pure returns (bytes32) { if (volRef > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter(); return params.set(volRef, Encoded.MASK_UINT20, OFFSET_VOL_REF); } /** * @dev Set the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param volAcc The volatility accumulator * @return The updated encoded pair parameters */ function setVolatilityAccumulator(bytes32 params, uint24 volAcc) internal pure returns (bytes32) { if (volAcc > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter(); return params.set(volAcc, Encoded.MASK_UINT20, OFFSET_VOL_ACC); } /** * @dev Set the active id in the encoded pair parameters * @param params The encoded pair parameters * @param activeId The active id * @return newParams The updated encoded pair parameters */ function setActiveId(bytes32 params, uint24 activeId) internal pure returns (bytes32 newParams) { return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ACTIVE_ID); } /** * @dev Sets the static fee parameters in the encoded pair parameters * @param params The encoded pair parameters * @param baseFactor The base factor * @param filterPeriod The filter period * @param decayPeriod The decay period * @param reductionFactor The reduction factor * @param variableFeeControl The variable fee control * @param protocolShare The protocol share * @param maxVolatilityAccumulator The max volatility accumulator * @return newParams The updated encoded pair parameters */ function setStaticFeeParameters( bytes32 params, uint16 baseFactor, uint16 filterPeriod, uint16 decayPeriod, uint16 reductionFactor, uint24 variableFeeControl, uint16 protocolShare, uint24 maxVolatilityAccumulator ) internal pure returns (bytes32 newParams) { if ( filterPeriod > decayPeriod || decayPeriod > Encoded.MASK_UINT12 || reductionFactor > Constants.BASIS_POINT_MAX || protocolShare > Constants.MAX_PROTOCOL_SHARE || maxVolatilityAccumulator > Encoded.MASK_UINT20 ) revert PairParametersHelper__InvalidParameter(); newParams = newParams.set(baseFactor, Encoded.MASK_UINT16, OFFSET_BASE_FACTOR); newParams = newParams.set(filterPeriod, Encoded.MASK_UINT12, OFFSET_FILTER_PERIOD); newParams = newParams.set(decayPeriod, Encoded.MASK_UINT12, OFFSET_DECAY_PERIOD); newParams = newParams.set(reductionFactor, Encoded.MASK_UINT14, OFFSET_REDUCTION_FACTOR); newParams = newParams.set(variableFeeControl, Encoded.MASK_UINT24, OFFSET_VAR_FEE_CONTROL); newParams = newParams.set(protocolShare, Encoded.MASK_UINT14, OFFSET_PROTOCOL_SHARE); newParams = newParams.set(maxVolatilityAccumulator, Encoded.MASK_UINT20, OFFSET_MAX_VOL_ACC); return params.set(uint256(newParams), MASK_STATIC_PARAMETER, 0); } /** * @dev Updates the index reference in the encoded pair parameters * @param params The encoded pair parameters * @return newParams The updated encoded pair parameters */ function updateIdReference(bytes32 params) internal pure returns (bytes32 newParams) { uint24 activeId = getActiveId(params); return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ID_REF); } /** * @dev Updates the time of last update in the encoded pair parameters * @param params The encoded pair parameters * @param timestamp The timestamp * @return newParams The updated encoded pair parameters */ function updateTimeOfLastUpdate(bytes32 params, uint256 timestamp) internal pure returns (bytes32 newParams) { uint40 currentTime = timestamp.safe40(); return params.set(currentTime, Encoded.MASK_UINT40, OFFSET_TIME_LAST_UPDATE); } /** * @dev Updates the volatility reference in the encoded pair parameters * @param params The encoded pair parameters * @return The updated encoded pair parameters */ function updateVolatilityReference(bytes32 params) internal pure returns (bytes32) { uint256 volAcc = getVolatilityAccumulator(params); uint256 reductionFactor = getReductionFactor(params); uint24 volRef; unchecked { volRef = uint24(volAcc * reductionFactor / Constants.BASIS_POINT_MAX); } return setVolatilityReference(params, volRef); } /** * @dev Updates the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param activeId The active id * @return The updated encoded pair parameters */ function updateVolatilityAccumulator(bytes32 params, uint24 activeId) internal pure returns (bytes32) { uint256 idReference = getIdReference(params); uint256 deltaId; uint256 volAcc; unchecked { deltaId = activeId > idReference ? activeId - idReference : idReference - activeId; volAcc = (uint256(getVolatilityReference(params)) + deltaId * Constants.BASIS_POINT_MAX); } uint256 maxVolAcc = getMaxVolatilityAccumulator(params); volAcc = volAcc > maxVolAcc ? maxVolAcc : volAcc; return setVolatilityAccumulator(params, uint24(volAcc)); } /** * @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param timestamp The timestamp * @return The updated encoded pair parameters */ function updateReferences(bytes32 params, uint256 timestamp) internal pure returns (bytes32) { uint256 dt = timestamp - getTimeOfLastUpdate(params); if (dt >= getFilterPeriod(params)) { params = updateIdReference(params); params = dt < getDecayPeriod(params) ? updateVolatilityReference(params) : setVolatilityReference(params, 0); } return updateTimeOfLastUpdate(params, timestamp); } /** * @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters * @param params The encoded pair parameters * @param activeId The active id * @param timestamp The timestamp * @return The updated encoded pair parameters */ function updateVolatilityParameters(bytes32 params, uint24 activeId, uint256 timestamp) internal pure returns (bytes32) { params = updateReferences(params, timestamp); return updateVolatilityAccumulator(params, activeId); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {Constants} from "./Constants.sol"; /** * @title Liquidity Book Fee Helper Library * @author Trader Joe * @notice This library contains functions to calculate fees */ library FeeHelper { error FeeHelper__FeeTooLarge(); error FeeHelper__ProtocolShareTooLarge(); /** * @dev Modifier to check that the fee is not too large * @param fee The fee */ modifier verifyFee(uint128 fee) { if (fee > Constants.MAX_FEE) revert FeeHelper__FeeTooLarge(); _; } /** * @dev Modifier to check that the protocol share is not too large * @param protocolShare The protocol share */ modifier verifyProtocolShare(uint128 protocolShare) { if (protocolShare > Constants.MAX_PROTOCOL_SHARE) revert FeeHelper__ProtocolShareTooLarge(); _; } /** * @dev Calculates the fee amount from the amount with fees, rounding up * @param amountWithFees The amount with fees * @param totalFee The total fee * @return feeAmount The fee amount */ function getFeeAmountFrom(uint128 amountWithFees, uint128 totalFee) internal pure verifyFee(totalFee) returns (uint128) { unchecked { // Can't overflow, max(result) = (type(uint128).max * 0.1e18 + 1e18 - 1) / 1e18 < 2^128 return uint128((uint256(amountWithFees) * totalFee + Constants.PRECISION - 1) / Constants.PRECISION); } } /** * @dev Calculates the fee amount that will be charged, rounding up * @param amount The amount * @param totalFee The total fee * @return feeAmount The fee amount */ function getFeeAmount(uint128 amount, uint128 totalFee) internal pure verifyFee(totalFee) returns (uint128) { unchecked { uint256 denominator = Constants.PRECISION - totalFee; // Can't overflow, max(result) = (type(uint128).max * 0.1e18 + (1e18 - 1)) / 0.9e18 < 2^128 return uint128((uint256(amount) * totalFee + denominator - 1) / denominator); } } /** * @dev Calculates the composition fee amount from the amount with fees, rounding down * @param amountWithFees The amount with fees * @param totalFee The total fee * @return The amount with fees */ function getCompositionFee(uint128 amountWithFees, uint128 totalFee) internal pure verifyFee(totalFee) returns (uint128) { unchecked { uint256 denominator = Constants.SQUARED_PRECISION; // Can't overflow, max(result) = type(uint128).max * 0.1e18 * 1.1e18 / 1e36 <= 2^128 * 0.11e36 / 1e36 < 2^128 return uint128(uint256(amountWithFees) * totalFee * (uint256(totalFee) + Constants.PRECISION) / denominator); } } /** * @dev Calculates the protocol fee amount from the fee amount and the protocol share, rounding down * @param feeAmount The fee amount * @param protocolShare The protocol share * @return protocolFeeAmount The protocol fee amount */ function getProtocolFeeAmount(uint128 feeAmount, uint128 protocolShare) internal pure verifyProtocolShare(protocolShare) returns (uint128) { unchecked { return uint128(uint256(feeAmount) * protocolShare / Constants.BASIS_POINT_MAX); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; /** * @title Liquidity Book Encoded Library * @author Trader Joe * @notice Helper contract used for decoding bytes32 sample */ library Encoded { uint256 internal constant MASK_UINT1 = 0x1; uint256 internal constant MASK_UINT8 = 0xff; uint256 internal constant MASK_UINT12 = 0xfff; uint256 internal constant MASK_UINT14 = 0x3fff; uint256 internal constant MASK_UINT16 = 0xffff; uint256 internal constant MASK_UINT20 = 0xfffff; uint256 internal constant MASK_UINT24 = 0xffffff; uint256 internal constant MASK_UINT40 = 0xffffffffff; uint256 internal constant MASK_UINT64 = 0xffffffffffffffff; uint256 internal constant MASK_UINT128 = 0xffffffffffffffffffffffffffffffff; /** * @notice Internal function to set a value in an encoded bytes32 using a mask and offset * @dev This function can overflow * @param encoded The previous encoded value * @param value The value to encode * @param mask The mask * @param offset The offset * @return newEncoded The new encoded value */ function set(bytes32 encoded, uint256 value, uint256 mask, uint256 offset) internal pure returns (bytes32 newEncoded) { assembly { newEncoded := and(encoded, not(shl(offset, mask))) newEncoded := or(newEncoded, shl(offset, and(value, mask))) } } /** * @notice Internal function to set a bool in an encoded bytes32 using an offset * @dev This function can overflow * @param encoded The previous encoded value * @param boolean The bool to encode * @param offset The offset * @return newEncoded The new encoded value */ function setBool(bytes32 encoded, bool boolean, uint256 offset) internal pure returns (bytes32 newEncoded) { return set(encoded, boolean ? 1 : 0, MASK_UINT1, offset); } /** * @notice Internal function to decode a bytes32 sample using a mask and offset * @dev This function can overflow * @param encoded The encoded value * @param mask The mask * @param offset The offset * @return value The decoded value */ function decode(bytes32 encoded, uint256 mask, uint256 offset) internal pure returns (uint256 value) { assembly { value := and(shr(offset, encoded), mask) } } /** * @notice Internal function to decode a bytes32 sample into a bool using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return boolean The decoded value as a bool */ function decodeBool(bytes32 encoded, uint256 offset) internal pure returns (bool boolean) { assembly { boolean := and(shr(offset, encoded), MASK_UINT1) } } /** * @notice Internal function to decode a bytes32 sample into a uint8 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint8(bytes32 encoded, uint256 offset) internal pure returns (uint8 value) { assembly { value := and(shr(offset, encoded), MASK_UINT8) } } /** * @notice Internal function to decode a bytes32 sample into a uint12 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value as a uint16, since uint12 is not supported */ function decodeUint12(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) { assembly { value := and(shr(offset, encoded), MASK_UINT12) } } /** * @notice Internal function to decode a bytes32 sample into a uint14 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value as a uint16, since uint14 is not supported */ function decodeUint14(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) { assembly { value := and(shr(offset, encoded), MASK_UINT14) } } /** * @notice Internal function to decode a bytes32 sample into a uint16 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint16(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) { assembly { value := and(shr(offset, encoded), MASK_UINT16) } } /** * @notice Internal function to decode a bytes32 sample into a uint20 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value as a uint24, since uint20 is not supported */ function decodeUint20(bytes32 encoded, uint256 offset) internal pure returns (uint24 value) { assembly { value := and(shr(offset, encoded), MASK_UINT20) } } /** * @notice Internal function to decode a bytes32 sample into a uint24 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint24(bytes32 encoded, uint256 offset) internal pure returns (uint24 value) { assembly { value := and(shr(offset, encoded), MASK_UINT24) } } /** * @notice Internal function to decode a bytes32 sample into a uint40 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint40(bytes32 encoded, uint256 offset) internal pure returns (uint40 value) { assembly { value := and(shr(offset, encoded), MASK_UINT40) } } /** * @notice Internal function to decode a bytes32 sample into a uint64 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint64(bytes32 encoded, uint256 offset) internal pure returns (uint64 value) { assembly { value := and(shr(offset, encoded), MASK_UINT64) } } /** * @notice Internal function to decode a bytes32 sample into a uint128 using an offset * @dev This function can overflow * @param encoded The encoded value * @param offset The offset * @return value The decoded value */ function decodeUint128(bytes32 encoded, uint256 offset) internal pure returns (uint128 value) { assembly { value := and(shr(offset, encoded), MASK_UINT128) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "joe-v2/=lib/joe-v2/src/", "@openzeppelin/contracts-upgradeable/=lib/joe-v2/lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/joe-v2/lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 800 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"BinHelper__LiquidityOverflow","type":"error"},{"inputs":[],"name":"FeesAmounts__LengthMismatch","type":"error"},{"inputs":[],"name":"SafeCast__Exceeds24Bits","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"Uint128x128Math__PowUnderflow","type":"error"},{"inputs":[],"name":"Uint256x256Math__MulDivOverflow","type":"error"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"previousX","type":"uint256[]"},{"internalType":"uint256[]","name":"previousY","type":"uint256[]"}],"name":"getAmountsAndFeesEarnedOf","outputs":[{"internalType":"uint256[]","name":"amountsX","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsY","type":"uint256[]"},{"internalType":"uint256[]","name":"feesX","type":"uint256[]"},{"internalType":"uint256[]","name":"feesY","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"liquidities","type":"uint256[]"}],"name":"getAmountsForLiquidities","outputs":[{"internalType":"uint256[]","name":"amountsX","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsY","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"name":"getAmountsForShares","outputs":[{"internalType":"uint256[]","name":"amountsX","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsY","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"getAmountsOf","outputs":[{"internalType":"uint256[]","name":"amountsX","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsY","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"pair","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint24","name":"id","type":"uint24"},{"internalType":"uint24","name":"lengthLeft","type":"uint24"},{"internalType":"uint24","name":"lengthRight","type":"uint24"}],"name":"getBinsReserveOf","outputs":[{"internalType":"uint24","name":"","type":"uint24"},{"components":[{"internalType":"uint24","name":"id","type":"uint24"},{"internalType":"uint128","name":"reserveX","type":"uint128"},{"internalType":"uint128","name":"reserveY","type":"uint128"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"}],"internalType":"struct NonEmptyBinHelper.PopulatedBinUser[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"previousLiquidities","type":"uint256[]"}],"name":"getFeeSharesAndFeesEarnedOf","outputs":[{"internalType":"uint256[]","name":"feeShares","type":"uint256[]"},{"internalType":"uint256[]","name":"feesX","type":"uint256[]"},{"internalType":"uint256[]","name":"feesY","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsX","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsY","type":"uint256[]"}],"name":"getLiquiditiesForAmounts","outputs":[{"internalType":"uint256[]","name":"liquidities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"name":"getLiquiditiesForShares","outputs":[{"internalType":"uint256[]","name":"liquidities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"getLiquiditiesOf","outputs":[{"internalType":"uint256[]","name":"liquidities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"pair","type":"address"},{"internalType":"uint24","name":"start","type":"uint24"},{"internalType":"uint24","name":"end","type":"uint24"},{"internalType":"uint24","name":"length","type":"uint24"}],"name":"getPopulatedBinsId","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"pair","type":"address"},{"internalType":"uint24","name":"start","type":"uint24"},{"internalType":"uint24","name":"end","type":"uint24"},{"internalType":"uint24","name":"length","type":"uint24"}],"name":"getPopulatedBinsReserves","outputs":[{"components":[{"internalType":"uint24","name":"id","type":"uint24"},{"internalType":"uint128","name":"reserveX","type":"uint128"},{"internalType":"uint128","name":"reserveY","type":"uint128"}],"internalType":"struct NonEmptyBinHelper.PopulatedBin[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsX","type":"uint256[]"},{"internalType":"uint256[]","name":"amountsY","type":"uint256[]"}],"name":"getSharesForAmounts","outputs":[{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"liquidities","type":"uint256[]"}],"name":"getSharesForLiquidities","outputs":[{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILBPair","name":"lbPair","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"getSharesOf","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50613194806100206000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063868d56db1161008c578063b7b5c78811610066578063b7b5c78814610208578063bfeb53e61461022b578063c192492c1461023e578063d4ac4cb21461025157600080fd5b8063868d56db146101a7578063a61a2790146101c8578063a8dad9d5146101e857600080fd5b80635749657a116100c85780635749657a1461014d5780635919fd721461016e578063710d9f5a1461018157806381fdfecc1461019457600080fd5b806313eba6f3146100ef57806319b1b94a146101185780633c6edb451461012b575b600080fd5b6101026100fd366004612a23565b610264565b60405161010f9190612afa565b60405180910390f35b610102610126366004612b0d565b61027b565b61013e610139366004612b83565b610292565b60405161010f93929190612bd1565b61016061015b366004612b0d565b6102b3565b60405161010f929190612c0a565b61016061017c366004612b0d565b6102cd565b61010261018f366004612b0d565b6102db565b6101026101a2366004612a23565b6102e8565b6101ba6101b5366004612c40565b6102f6565b60405161010f929190612cb1565b6101db6101d6366004612d41565b610315565b60405161010f9190612d9d565b6101fb6101f6366004612d41565b610323565b60405161010f9190612dec565b61021b610216366004612e57565b610331565b60405161010f9493929190612f05565b610160610239366004612f52565b610357565b61010261024c366004612f52565b610365565b61010261025f366004612f52565b610372565b60606102728585858561037f565b95945050505050565b6060610288848484610526565b90505b9392505050565b60608060606102a387878787610691565b9250925092509450945094915050565b6060806102c1858585610a43565b91509150935093915050565b6060806102c1858585610b94565b6060610288848484610e1d565b606061027285858585610f6d565b6000606061030787878787876110cf565b915091509550959350505050565b6060610272858585856116a6565b6060610272858585856118c1565b6060806060806103448989898989611a4a565b929c919b50995090975095505050505050565b6060806102c1858585611d17565b6060610288848484611e13565b6060610288848484611f30565b60608251845114158061039457508151845114155b156103b257604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff8111156103cc576103cc61296f565b6040519080825280602002602001820160405280156103f5578160200160208202803683370190505b5090506000856001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190612faa565b905060005b855181101561051c57600061048e87838151811061048157610481612fce565b6020026020010151612047565b9050600061049c8285612072565b905060006104dd8885815181106104b5576104b5612fce565b60200260200101518886815181106104cf576104cf612fce565b6020026020010151846120ad565b90506104f08a8462ffffff1683856120c6565b86858151811061050257610502612fce565b602002602001018181525050836001019350505050610461565b5050949350505050565b6060815183511461054a57604051634a19546360e11b815260040160405180910390fd5b825167ffffffffffffffff8111156105645761056461296f565b60405190808252806020026020018201604052801561058d578160200160208202803683370190505b5090506000846001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f49190612faa565b905060005b845181101561068857600061062261061c87848151811061048157610481612fce565b84612072565b90506106628787848151811061063a5761063a612fce565b602002602001015187858151811061065457610654612fce565b6020026020010151846120c6565b84838151811061067457610674612fce565b6020908102919091010152506001016105f9565b50509392505050565b606080606083518551146106b857604051634a19546360e11b815260040160405180910390fd5b845167ffffffffffffffff8111156106d2576106d261296f565b6040519080825280602002602001820160405280156106fb578160200160208202803683370190505b509250845167ffffffffffffffff8111156107185761071861296f565b604051908082528060200260200182016040528015610741578160200160208202803683370190505b509150845167ffffffffffffffff81111561075e5761075e61296f565b604051908082528060200260200182016040528015610787578160200160208202803683370190505b5090506000876001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ee9190612faa565b905060005b8651811015610a3757600061081388838151811061048157610481612fce565b905060006108228b8b84612200565b604051630157d2d160e31b815262ffffff8416600482015290915060009081906001600160a01b038e1690630abe9688906024016040805180830381865afa158015610872573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108969190612ffb565b60405163bd85b03960e01b815262ffffff871660048201526001600160801b039283169450911691506000906001600160a01b038f169063bd85b03990602401602060405180830381865afa1580156108f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610917919061302e565b9050600080821561093d5761092d868685612277565b610938878686612277565b610941565b6000805b9150915060008d898151811061095957610959612fce565b60200260200101519050600061097984846109748c8f612072565b6120ad565b9050600082821161098b5760006109a1565b6109a1898361099a868261305d565b9190612277565b9050808f8c815181106109b6576109b6612fce565b602090810291909101015285156109e2576109d2818988612277565b6109dd828989612277565b6109e6565b6000805b8f8d815181106109f8576109f8612fce565b602002602001018f8e81518110610a1157610a11612fce565b60200260200101828152508281525050508a6001019a50505050505050505050506107f3565b50509450945094915050565b6060808251845114610a6857604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff811115610a8257610a8261296f565b604051908082528060200260200182016040528015610aab578160200160208202803683370190505b509150835167ffffffffffffffff811115610ac857610ac861296f565b604051908082528060200260200182016040528015610af1578160200160208202803683370190505b50905060005b8451811015610b8b57600080610b4088888581518110610b1957610b19612fce565b6020026020010151888681518110610b3357610b33612fce565b60200260200101516122a1565b9150915081858481518110610b5757610b57612fce565b60200260200101818152505080848481518110610b7657610b76612fce565b60209081029190910101525050600101610af7565b50935093915050565b6060808251845114610bb957604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff811115610bd357610bd361296f565b604051908082528060200260200182016040528015610bfc578160200160208202803683370190505b509150835167ffffffffffffffff811115610c1957610c1961296f565b604051908082528060200260200182016040528015610c42578160200160208202803683370190505b5090506000856001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca99190612faa565b905060005b8551811015610e13576000610cd161061c88848151811061048157610481612fce565b9050600080896001600160a01b0316630abe9688610cfa8b878151811061048157610481612fce565b6040516001600160e01b031960e084901b16815262ffffff90911660048201526024016040805180830381865afa158015610d39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5d9190612ffb565b6001600160801b031691506001600160801b031691506000610d808383866120ad565b90506000898681518110610d9657610d96612fce565b6020026020010151905081600014610dc357610db3818584612277565b610dbe828585612277565b610dc7565b6000805b8a8881518110610dd957610dd9612fce565b602002602001018a8981518110610df257610df2612fce565b60200260200101828152508281525050508560010195505050505050610cae565b5050935093915050565b60608151835114610e4157604051634a19546360e11b815260040160405180910390fd5b825167ffffffffffffffff811115610e5b57610e5b61296f565b604051908082528060200260200182016040528015610e84578160200160208202803683370190505b5090506000846001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ec7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eeb9190612faa565b905060005b8451811015610688576000610f1086838151811061048157610481612fce565b9050600080610f31898462ffffff16898781518110610b3357610b33612fce565b91509150610f41828285886123df565b868581518110610f5357610f53612fce565b602002602001018181525050836001019350505050610ef0565b606082518451141580610f8257508151845114155b15610fa057604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff811115610fba57610fba61296f565b604051908082528060200260200182016040528015610fe3578160200160208202803683370190505b5090506000856001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611026573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104a9190612faa565b905060005b855181101561051c576110aa85828151811061106d5761106d612fce565b602002602001015185838151811061108757611087612fce565b60200260200101516110a489858151811061048157610481612fce565b856123df565b8382815181106110bc576110bc612fce565b602090810291909101015260010161104f565b600060608462ffffff1660000361114557866001600160a01b031663dbe65edc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561111e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111429190613070565b94505b600062ffffff8516156111805761117b8862ffffff8616156111715761116c60018961308d565b611173565b875b6000886116a6565b611190565b6040805160008152602081019091525b9050600062ffffff8516156111b3576111ae898862ffffff886116a6565b6111c3565b6040805160008152602081019091525b90506000600383516111d591906130c6565b90506000600383516111e791906130c6565b905060006111f582846130e8565b905060008167ffffffffffffffff8111156112125761121261296f565b60405190808252806020026020018201604052801561126b57816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282526000199092019101816112305790505b5090506003840286016020018d8d60005b8781101561147b576002199093018051604051627eeac760e11b81526001600160a01b03848116600483015260e89290921c60248201819052929592916000919086169062fdd58e90604401602060405180830381865afa1580156112e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611309919061302e565b9050801561146857604051630157d2d160e31b815262ffffff8316600482015260009081906001600160a01b03881690630abe9688906024016040805180830381865afa15801561135e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113829190612ffb565b60405163bd85b03960e01b815262ffffff8716600482015291935091506000906001600160a01b0389169063bd85b03990602401602060405180830381865afa1580156113d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f7919061302e565b90506040518060a001604052808662ffffff168152602001846001600160801b03168152602001836001600160801b03168152602001858152602001828152508a8780611443906130fb565b98508151811061145557611455612fce565b6020026020010181905250505050611474565b6114718a613114565b99505b505061127c565b61148587896130e8565b9550601d890193505b8581101561168d576003939093018051604051627eeac760e11b81526001600160a01b03848116600483015260e89290921c60248201819052929592916000919086169062fdd58e90604401602060405180830381865afa1580156114f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151b919061302e565b9050801561167a57604051630157d2d160e31b815262ffffff8316600482015260009081906001600160a01b03881690630abe9688906024016040805180830381865afa158015611570573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115949190612ffb565b60405163bd85b03960e01b815262ffffff8716600482015291935091506000906001600160a01b0389169063bd85b03990602401602060405180830381865afa1580156115e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611609919061302e565b90506040518060a001604052808662ffffff168152602001846001600160801b03168152602001836001600160801b03168152602001858152602001828152508a8780611655906130fb565b98508151811061166757611667612fce565b6020026020010181905250505050611686565b61168388613114565b97505b505061148e565b505050918152999c999b50989950505050505050505050565b60608262ffffff168462ffffff16106116dd5762ffffff848116146116d6576116d084600161312b565b836116f7565b82846116f7565b62ffffff8416156116f3576116d060018561308d565b6000835b909450925062ffffff82161561170d5781611737565b8362ffffff168362ffffff161161172d57611728838561308d565b611737565b611737848461308d565b9150600061174b62ffffff84166003613147565b67ffffffffffffffff8111156117635761176361296f565b6040519080825280601f01601f19166020018201604052801561178d576020820181803683370190505b508051909150600090828762ffffff80891690821611845b8862ffffff16811080156117bd57508862ffffff1686105b156118ab5760405163a41a01fb60e01b8152821515600482015262ffffff841660248201526001600160a01b038d169063a41a01fb90604401602060405180830381865afa158015611813573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118379190613070565b92508161185e578962ffffff168362ffffff161180611859575062ffffff8316155b61187a565b8962ffffff168362ffffff16108061187a575062ffffff838116145b6118ab57611887866130fb565b9550828560181b179450600384019350848452806118a4906130fb565b90506117a5565b5050505060039190910282525095945050505050565b606060006118d1868686866116a6565b90506000600382516118e391906130c6565b905060008167ffffffffffffffff8111156119005761190061296f565b60405190808252806020026020018201604052801561194b57816020015b604080516060810182526000808252602080830182905292820152825260001990920191018161191e5790505b509050601d83016000805b84811015611a3b576003929092018051604051630157d2d160e31b815260e89190911c60048201819052919391925060009081906001600160a01b038e1690630abe9688906024016040805180830381865afa1580156119ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119de9190612ffb565b9150915060405180606001604052808562ffffff168152602001836001600160801b03168152602001826001600160801b0316815250868481518110611a2657611a26612fce565b60209081029190910101525050600101611956565b50919998505050505050505050565b60608060608085518751141580611a6357508451875114155b15611a8157604051634a19546360e11b815260040160405180910390fd5b865167ffffffffffffffff811115611a9b57611a9b61296f565b604051908082528060200260200182016040528015611ac4578160200160208202803683370190505b509350865167ffffffffffffffff811115611ae157611ae161296f565b604051908082528060200260200182016040528015611b0a578160200160208202803683370190505b509250865167ffffffffffffffff811115611b2757611b2761296f565b604051908082528060200260200182016040528015611b50578160200160208202803683370190505b509150865167ffffffffffffffff811115611b6d57611b6d61296f565b604051908082528060200260200182016040528015611b96578160200160208202803683370190505b5090506000896001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfd9190612faa565b905060005b8851811015611d09576000611c228a838151811061048157610481612fce565b9050600080611c328e8e856123f0565b9150915081898581518110611c4957611c49612fce565b60200260200101818152505080888581518110611c6857611c68612fce565b602002602001018181525050600080611cb787868f8981518110611c8e57611c8e612fce565b60200260200101518f8a81518110611ca857611ca8612fce565b6020026020010151888861252c565b9150915081898781518110611cce57611cce612fce565b60200260200101818152505080888781518110611ced57611ced612fce565b6020026020010181815250508560010195505050505050611c02565b505095509550955095915050565b606080825167ffffffffffffffff811115611d3457611d3461296f565b604051908082528060200260200182016040528015611d5d578160200160208202803683370190505b509150825167ffffffffffffffff811115611d7a57611d7a61296f565b604051908082528060200260200182016040528015611da3578160200160208202803683370190505b50905060005b8351811015610b8b57611dd18686611dcc87858151811061048157610481612fce565b6123f0565b848381518110611de357611de3612fce565b60200260200101848481518110611dfc57611dfc612fce565b602090810291909101019190915252600101611da9565b6060815167ffffffffffffffff811115611e2f57611e2f61296f565b604051908082528060200260200182016040528015611e58578160200160208202803683370190505b5090506000846001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ebf9190612faa565b905060005b8351811015610688576000611ee485838151811061048157610481612fce565b9050600080611ef48989856123f0565b91509150611f04828285886123df565b868581518110611f1657611f16612fce565b602002602001018181525050836001019350505050611ec4565b6060815167ffffffffffffffff811115611f4c57611f4c61296f565b604051908082528060200260200182016040528015611f75578160200160208202803683370190505b50905060005b825181101561203f57846001600160a01b031662fdd58e85611fa886858151811061048157610481612fce565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015262ffffff166024820152604401602060405180830381865afa158015611ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061201a919061302e565b82828151811061202c5761202c612fce565b6020908102919091010152600101611f7b565b509392505050565b8062ffffff8116811461206d57604051639b63641560e01b815260040160405180910390fd5b919050565b600061271071ffff00000000000000000000000000000000608084901b1604600160801b0162ffffff8416627fffff19016102728282612579565b600060806120bc8585856127e3565b901c949350505050565b6000806000866001600160a01b0316630abe96886120e388612047565b6040516001600160e01b031960e084901b16815262ffffff90911660048201526024016040805180830381865afa158015612122573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121469190612ffb565b6001600160801b031691506001600160801b0316915060006121698383876120ad565b60405163bd85b03960e01b8152600481018990529091506000906001600160a01b038a169063bd85b03990602401602060405180830381865afa1580156121b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d8919061302e565b905081156121f0576121eb878284612277565b6121f3565b60005b9998505050505050505050565b604051627eeac760e11b81526001600160a01b03838116600483015262ffffff831660248301526000919085169062fdd58e90604401602060405180830381865afa158015612253573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610288919061302e565b6000806000612286868661284e565b91509150612297868686858561286d565b9695505050505050565b600080600080866001600160a01b0316630abe96886122bf88612047565b6040516001600160e01b031960e084901b16815262ffffff90911660048201526024016040805180830381865afa1580156122fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123229190612ffb565b6001600160801b031691506001600160801b031691506000876001600160a01b031663bd85b039886040518263ffffffff1660e01b815260040161236891815260200190565b602060405180830381865afa158015612385573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a9919061302e565b905080156123cc576123bc868483612277565b6123c7878484612277565b6123d0565b6000805b90999098509650505050505050565b600061027285856109748686612072565b6000806000612400868686612200565b604051630157d2d160e31b815262ffffff8616600482015290915060009081906001600160a01b03891690630abe9688906024016040805180830381865afa158015612450573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124749190612ffb565b60405163bd85b03960e01b815262ffffff891660048201526001600160801b039283169450911691506000906001600160a01b038a169063bd85b03990602401602060405180830381865afa1580156124d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f5919061302e565b9050801561251857612508848483612277565b612513858484612277565b61251c565b6000805b909a909950975050505050505050565b600080600061253b888a612072565b9050600061254a8888846120ad565b905060006125598787856120ad565b905061256782828989612919565b94509450505050965096945050505050565b6000806000836000036125955750600160801b91506127dd9050565b508260008112156125a7579015906000035b6210000081101561279757600160801b9250846001600160801b038111156125d157911591600019045b60018216156125e25792830260801c925b800260801c60028216156125f85792830260801c925b800260801c600482161561260e5792830260801c925b800260801c60088216156126245792830260801c925b800260801c601082161561263a5792830260801c925b800260801c60208216156126505792830260801c925b800260801c60408216156126665792830260801c925b8002608090811c9082161561267d5792830260801c925b800260801c6101008216156126945792830260801c925b800260801c6102008216156126ab5792830260801c925b800260801c6104008216156126c25792830260801c925b800260801c6108008216156126d95792830260801c925b800260801c6110008216156126f05792830260801c925b800260801c6120008216156127075792830260801c925b800260801c61400082161561271e5792830260801c925b800260801c6180008216156127355792830260801c925b800260801c6201000082161561274d5792830260801c925b800260801c620200008216156127655792830260801c925b800260801c6204000082161561277d5792830260801c925b800260801c620800008216156127955792830260801c925b505b826000036127c657604051631dba598d60e11b8152600481018690526024810185905260440160405180910390fd5b816127d15782610272565b610272836000196130c6565b92915050565b6000831561281e5750808302818482816127ff576127ff6130b0565b041461281e576040516363f1e01f60e01b815260040160405180910390fd5b821561028b5760809290921b9182018281101561028b576040516363f1e01f60e01b815260040160405180910390fd5b6000806000198385098385029250828110838203039150509250929050565b60008160000361288e57838381612886576128866130b0565b049050610272565b8382106128ae576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000808585111561294e576000612930878761305d565b905061293d818688612277565b925061294a818588612277565b9150505b94509492505050565b6001600160a01b038116811461296c57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b600082601f83011261299657600080fd5b8135602067ffffffffffffffff808311156129b3576129b361296f565b8260051b604051601f19603f830116810181811084821117156129d8576129d861296f565b60405293845260208187018101949081019250878511156129f857600080fd5b6020870191505b84821015612a18578135835291830191908301906129ff565b979650505050505050565b60008060008060808587031215612a3957600080fd5b8435612a4481612957565b9350602085013567ffffffffffffffff80821115612a6157600080fd5b612a6d88838901612985565b94506040870135915080821115612a8357600080fd5b612a8f88838901612985565b93506060870135915080821115612aa557600080fd5b50612ab287828801612985565b91505092959194509250565b60008151808452602080850194506020840160005b83811015612aef57815187529582019590820190600101612ad3565b509495945050505050565b60208152600061028b6020830184612abe565b600080600060608486031215612b2257600080fd5b8335612b2d81612957565b9250602084013567ffffffffffffffff80821115612b4a57600080fd5b612b5687838801612985565b93506040860135915080821115612b6c57600080fd5b50612b7986828701612985565b9150509250925092565b60008060008060808587031215612b9957600080fd5b8435612ba481612957565b93506020850135612bb481612957565b9250604085013567ffffffffffffffff80821115612a8357600080fd5b606081526000612be46060830186612abe565b8281036020840152612bf68186612abe565b905082810360408401526122978185612abe565b604081526000612c1d6040830185612abe565b82810360208401526102728185612abe565b62ffffff8116811461296c57600080fd5b600080600080600060a08688031215612c5857600080fd5b8535612c6381612957565b94506020860135612c7381612957565b93506040860135612c8381612c2f565b92506060860135612c9381612c2f565b91506080860135612ca381612c2f565b809150509295509295909350565b6000604080830162ffffff8087168552602060406020870152828751808552606094506060880191506020890160005b82811015612d31578151805187168552858101516001600160801b039081168787015289820151168986015287810151888601526080908101519085015260a09093019290840190600101612ce1565b50919a9950505050505050505050565b60008060008060808587031215612d5757600080fd5b8435612d6281612957565b93506020850135612d7281612c2f565b92506040850135612d8281612c2f565b91506060850135612d9281612c2f565b939692955090935050565b60006020808352835180602085015260005b81811015612dcb57858101830151858201604001528201612daf565b506000604082860101526040601f19601f8301168501019250505092915050565b602080825282518282018190526000919060409081850190868401855b82811015612e4a578151805162ffffff168552868101516001600160801b039081168887015290860151168585015260609093019290850190600101612e09565b5091979650505050505050565b600080600080600060a08688031215612e6f57600080fd5b8535612e7a81612957565b94506020860135612e8a81612957565b9350604086013567ffffffffffffffff80821115612ea757600080fd5b612eb389838a01612985565b94506060880135915080821115612ec957600080fd5b612ed589838a01612985565b93506080880135915080821115612eeb57600080fd5b50612ef888828901612985565b9150509295509295909350565b608081526000612f186080830187612abe565b8281036020840152612f2a8187612abe565b90508281036040840152612f3e8186612abe565b90508281036060840152612a188185612abe565b600080600060608486031215612f6757600080fd5b8335612f7281612957565b92506020840135612f8281612957565b9150604084013567ffffffffffffffff811115612f9e57600080fd5b612b7986828701612985565b600060208284031215612fbc57600080fd5b815161ffff8116811461028b57600080fd5b634e487b7160e01b600052603260045260246000fd5b80516001600160801b038116811461206d57600080fd5b6000806040838503121561300e57600080fd5b61301783612fe4565b915061302560208401612fe4565b90509250929050565b60006020828403121561304057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156127dd576127dd613047565b60006020828403121561308257600080fd5b815161028b81612c2f565b62ffffff8281168282160390808211156130a9576130a9613047565b5092915050565b634e487b7160e01b600052601260045260246000fd5b6000826130e357634e487b7160e01b600052601260045260246000fd5b500490565b808201808211156127dd576127dd613047565b60006001820161310d5761310d613047565b5060010190565b60008161312357613123613047565b506000190190565b62ffffff8181168382160190808211156130a9576130a9613047565b80820281158282048414176127dd576127dd61304756fea2646970667358221220cbd6f6222f12810979380617fc27e86289107996161f84bdf7eaaf37b3e7282a64736f6c63430008160033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063868d56db1161008c578063b7b5c78811610066578063b7b5c78814610208578063bfeb53e61461022b578063c192492c1461023e578063d4ac4cb21461025157600080fd5b8063868d56db146101a7578063a61a2790146101c8578063a8dad9d5146101e857600080fd5b80635749657a116100c85780635749657a1461014d5780635919fd721461016e578063710d9f5a1461018157806381fdfecc1461019457600080fd5b806313eba6f3146100ef57806319b1b94a146101185780633c6edb451461012b575b600080fd5b6101026100fd366004612a23565b610264565b60405161010f9190612afa565b60405180910390f35b610102610126366004612b0d565b61027b565b61013e610139366004612b83565b610292565b60405161010f93929190612bd1565b61016061015b366004612b0d565b6102b3565b60405161010f929190612c0a565b61016061017c366004612b0d565b6102cd565b61010261018f366004612b0d565b6102db565b6101026101a2366004612a23565b6102e8565b6101ba6101b5366004612c40565b6102f6565b60405161010f929190612cb1565b6101db6101d6366004612d41565b610315565b60405161010f9190612d9d565b6101fb6101f6366004612d41565b610323565b60405161010f9190612dec565b61021b610216366004612e57565b610331565b60405161010f9493929190612f05565b610160610239366004612f52565b610357565b61010261024c366004612f52565b610365565b61010261025f366004612f52565b610372565b60606102728585858561037f565b95945050505050565b6060610288848484610526565b90505b9392505050565b60608060606102a387878787610691565b9250925092509450945094915050565b6060806102c1858585610a43565b91509150935093915050565b6060806102c1858585610b94565b6060610288848484610e1d565b606061027285858585610f6d565b6000606061030787878787876110cf565b915091509550959350505050565b6060610272858585856116a6565b6060610272858585856118c1565b6060806060806103448989898989611a4a565b929c919b50995090975095505050505050565b6060806102c1858585611d17565b6060610288848484611e13565b6060610288848484611f30565b60608251845114158061039457508151845114155b156103b257604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff8111156103cc576103cc61296f565b6040519080825280602002602001820160405280156103f5578160200160208202803683370190505b5090506000856001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190612faa565b905060005b855181101561051c57600061048e87838151811061048157610481612fce565b6020026020010151612047565b9050600061049c8285612072565b905060006104dd8885815181106104b5576104b5612fce565b60200260200101518886815181106104cf576104cf612fce565b6020026020010151846120ad565b90506104f08a8462ffffff1683856120c6565b86858151811061050257610502612fce565b602002602001018181525050836001019350505050610461565b5050949350505050565b6060815183511461054a57604051634a19546360e11b815260040160405180910390fd5b825167ffffffffffffffff8111156105645761056461296f565b60405190808252806020026020018201604052801561058d578160200160208202803683370190505b5090506000846001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f49190612faa565b905060005b845181101561068857600061062261061c87848151811061048157610481612fce565b84612072565b90506106628787848151811061063a5761063a612fce565b602002602001015187858151811061065457610654612fce565b6020026020010151846120c6565b84838151811061067457610674612fce565b6020908102919091010152506001016105f9565b50509392505050565b606080606083518551146106b857604051634a19546360e11b815260040160405180910390fd5b845167ffffffffffffffff8111156106d2576106d261296f565b6040519080825280602002602001820160405280156106fb578160200160208202803683370190505b509250845167ffffffffffffffff8111156107185761071861296f565b604051908082528060200260200182016040528015610741578160200160208202803683370190505b509150845167ffffffffffffffff81111561075e5761075e61296f565b604051908082528060200260200182016040528015610787578160200160208202803683370190505b5090506000876001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ee9190612faa565b905060005b8651811015610a3757600061081388838151811061048157610481612fce565b905060006108228b8b84612200565b604051630157d2d160e31b815262ffffff8416600482015290915060009081906001600160a01b038e1690630abe9688906024016040805180830381865afa158015610872573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108969190612ffb565b60405163bd85b03960e01b815262ffffff871660048201526001600160801b039283169450911691506000906001600160a01b038f169063bd85b03990602401602060405180830381865afa1580156108f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610917919061302e565b9050600080821561093d5761092d868685612277565b610938878686612277565b610941565b6000805b9150915060008d898151811061095957610959612fce565b60200260200101519050600061097984846109748c8f612072565b6120ad565b9050600082821161098b5760006109a1565b6109a1898361099a868261305d565b9190612277565b9050808f8c815181106109b6576109b6612fce565b602090810291909101015285156109e2576109d2818988612277565b6109dd828989612277565b6109e6565b6000805b8f8d815181106109f8576109f8612fce565b602002602001018f8e81518110610a1157610a11612fce565b60200260200101828152508281525050508a6001019a50505050505050505050506107f3565b50509450945094915050565b6060808251845114610a6857604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff811115610a8257610a8261296f565b604051908082528060200260200182016040528015610aab578160200160208202803683370190505b509150835167ffffffffffffffff811115610ac857610ac861296f565b604051908082528060200260200182016040528015610af1578160200160208202803683370190505b50905060005b8451811015610b8b57600080610b4088888581518110610b1957610b19612fce565b6020026020010151888681518110610b3357610b33612fce565b60200260200101516122a1565b9150915081858481518110610b5757610b57612fce565b60200260200101818152505080848481518110610b7657610b76612fce565b60209081029190910101525050600101610af7565b50935093915050565b6060808251845114610bb957604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff811115610bd357610bd361296f565b604051908082528060200260200182016040528015610bfc578160200160208202803683370190505b509150835167ffffffffffffffff811115610c1957610c1961296f565b604051908082528060200260200182016040528015610c42578160200160208202803683370190505b5090506000856001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca99190612faa565b905060005b8551811015610e13576000610cd161061c88848151811061048157610481612fce565b9050600080896001600160a01b0316630abe9688610cfa8b878151811061048157610481612fce565b6040516001600160e01b031960e084901b16815262ffffff90911660048201526024016040805180830381865afa158015610d39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5d9190612ffb565b6001600160801b031691506001600160801b031691506000610d808383866120ad565b90506000898681518110610d9657610d96612fce565b6020026020010151905081600014610dc357610db3818584612277565b610dbe828585612277565b610dc7565b6000805b8a8881518110610dd957610dd9612fce565b602002602001018a8981518110610df257610df2612fce565b60200260200101828152508281525050508560010195505050505050610cae565b5050935093915050565b60608151835114610e4157604051634a19546360e11b815260040160405180910390fd5b825167ffffffffffffffff811115610e5b57610e5b61296f565b604051908082528060200260200182016040528015610e84578160200160208202803683370190505b5090506000846001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ec7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eeb9190612faa565b905060005b8451811015610688576000610f1086838151811061048157610481612fce565b9050600080610f31898462ffffff16898781518110610b3357610b33612fce565b91509150610f41828285886123df565b868581518110610f5357610f53612fce565b602002602001018181525050836001019350505050610ef0565b606082518451141580610f8257508151845114155b15610fa057604051634a19546360e11b815260040160405180910390fd5b835167ffffffffffffffff811115610fba57610fba61296f565b604051908082528060200260200182016040528015610fe3578160200160208202803683370190505b5090506000856001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611026573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104a9190612faa565b905060005b855181101561051c576110aa85828151811061106d5761106d612fce565b602002602001015185838151811061108757611087612fce565b60200260200101516110a489858151811061048157610481612fce565b856123df565b8382815181106110bc576110bc612fce565b602090810291909101015260010161104f565b600060608462ffffff1660000361114557866001600160a01b031663dbe65edc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561111e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111429190613070565b94505b600062ffffff8516156111805761117b8862ffffff8616156111715761116c60018961308d565b611173565b875b6000886116a6565b611190565b6040805160008152602081019091525b9050600062ffffff8516156111b3576111ae898862ffffff886116a6565b6111c3565b6040805160008152602081019091525b90506000600383516111d591906130c6565b90506000600383516111e791906130c6565b905060006111f582846130e8565b905060008167ffffffffffffffff8111156112125761121261296f565b60405190808252806020026020018201604052801561126b57816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282526000199092019101816112305790505b5090506003840286016020018d8d60005b8781101561147b576002199093018051604051627eeac760e11b81526001600160a01b03848116600483015260e89290921c60248201819052929592916000919086169062fdd58e90604401602060405180830381865afa1580156112e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611309919061302e565b9050801561146857604051630157d2d160e31b815262ffffff8316600482015260009081906001600160a01b03881690630abe9688906024016040805180830381865afa15801561135e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113829190612ffb565b60405163bd85b03960e01b815262ffffff8716600482015291935091506000906001600160a01b0389169063bd85b03990602401602060405180830381865afa1580156113d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f7919061302e565b90506040518060a001604052808662ffffff168152602001846001600160801b03168152602001836001600160801b03168152602001858152602001828152508a8780611443906130fb565b98508151811061145557611455612fce565b6020026020010181905250505050611474565b6114718a613114565b99505b505061127c565b61148587896130e8565b9550601d890193505b8581101561168d576003939093018051604051627eeac760e11b81526001600160a01b03848116600483015260e89290921c60248201819052929592916000919086169062fdd58e90604401602060405180830381865afa1580156114f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151b919061302e565b9050801561167a57604051630157d2d160e31b815262ffffff8316600482015260009081906001600160a01b03881690630abe9688906024016040805180830381865afa158015611570573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115949190612ffb565b60405163bd85b03960e01b815262ffffff8716600482015291935091506000906001600160a01b0389169063bd85b03990602401602060405180830381865afa1580156115e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611609919061302e565b90506040518060a001604052808662ffffff168152602001846001600160801b03168152602001836001600160801b03168152602001858152602001828152508a8780611655906130fb565b98508151811061166757611667612fce565b6020026020010181905250505050611686565b61168388613114565b97505b505061148e565b505050918152999c999b50989950505050505050505050565b60608262ffffff168462ffffff16106116dd5762ffffff848116146116d6576116d084600161312b565b836116f7565b82846116f7565b62ffffff8416156116f3576116d060018561308d565b6000835b909450925062ffffff82161561170d5781611737565b8362ffffff168362ffffff161161172d57611728838561308d565b611737565b611737848461308d565b9150600061174b62ffffff84166003613147565b67ffffffffffffffff8111156117635761176361296f565b6040519080825280601f01601f19166020018201604052801561178d576020820181803683370190505b508051909150600090828762ffffff80891690821611845b8862ffffff16811080156117bd57508862ffffff1686105b156118ab5760405163a41a01fb60e01b8152821515600482015262ffffff841660248201526001600160a01b038d169063a41a01fb90604401602060405180830381865afa158015611813573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118379190613070565b92508161185e578962ffffff168362ffffff161180611859575062ffffff8316155b61187a565b8962ffffff168362ffffff16108061187a575062ffffff838116145b6118ab57611887866130fb565b9550828560181b179450600384019350848452806118a4906130fb565b90506117a5565b5050505060039190910282525095945050505050565b606060006118d1868686866116a6565b90506000600382516118e391906130c6565b905060008167ffffffffffffffff8111156119005761190061296f565b60405190808252806020026020018201604052801561194b57816020015b604080516060810182526000808252602080830182905292820152825260001990920191018161191e5790505b509050601d83016000805b84811015611a3b576003929092018051604051630157d2d160e31b815260e89190911c60048201819052919391925060009081906001600160a01b038e1690630abe9688906024016040805180830381865afa1580156119ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119de9190612ffb565b9150915060405180606001604052808562ffffff168152602001836001600160801b03168152602001826001600160801b0316815250868481518110611a2657611a26612fce565b60209081029190910101525050600101611956565b50919998505050505050505050565b60608060608085518751141580611a6357508451875114155b15611a8157604051634a19546360e11b815260040160405180910390fd5b865167ffffffffffffffff811115611a9b57611a9b61296f565b604051908082528060200260200182016040528015611ac4578160200160208202803683370190505b509350865167ffffffffffffffff811115611ae157611ae161296f565b604051908082528060200260200182016040528015611b0a578160200160208202803683370190505b509250865167ffffffffffffffff811115611b2757611b2761296f565b604051908082528060200260200182016040528015611b50578160200160208202803683370190505b509150865167ffffffffffffffff811115611b6d57611b6d61296f565b604051908082528060200260200182016040528015611b96578160200160208202803683370190505b5090506000896001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfd9190612faa565b905060005b8851811015611d09576000611c228a838151811061048157610481612fce565b9050600080611c328e8e856123f0565b9150915081898581518110611c4957611c49612fce565b60200260200101818152505080888581518110611c6857611c68612fce565b602002602001018181525050600080611cb787868f8981518110611c8e57611c8e612fce565b60200260200101518f8a81518110611ca857611ca8612fce565b6020026020010151888861252c565b9150915081898781518110611cce57611cce612fce565b60200260200101818152505080888781518110611ced57611ced612fce565b6020026020010181815250508560010195505050505050611c02565b505095509550955095915050565b606080825167ffffffffffffffff811115611d3457611d3461296f565b604051908082528060200260200182016040528015611d5d578160200160208202803683370190505b509150825167ffffffffffffffff811115611d7a57611d7a61296f565b604051908082528060200260200182016040528015611da3578160200160208202803683370190505b50905060005b8351811015610b8b57611dd18686611dcc87858151811061048157610481612fce565b6123f0565b848381518110611de357611de3612fce565b60200260200101848481518110611dfc57611dfc612fce565b602090810291909101019190915252600101611da9565b6060815167ffffffffffffffff811115611e2f57611e2f61296f565b604051908082528060200260200182016040528015611e58578160200160208202803683370190505b5090506000846001600160a01b03166317f11ecc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ebf9190612faa565b905060005b8351811015610688576000611ee485838151811061048157610481612fce565b9050600080611ef48989856123f0565b91509150611f04828285886123df565b868581518110611f1657611f16612fce565b602002602001018181525050836001019350505050611ec4565b6060815167ffffffffffffffff811115611f4c57611f4c61296f565b604051908082528060200260200182016040528015611f75578160200160208202803683370190505b50905060005b825181101561203f57846001600160a01b031662fdd58e85611fa886858151811061048157610481612fce565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015262ffffff166024820152604401602060405180830381865afa158015611ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061201a919061302e565b82828151811061202c5761202c612fce565b6020908102919091010152600101611f7b565b509392505050565b8062ffffff8116811461206d57604051639b63641560e01b815260040160405180910390fd5b919050565b600061271071ffff00000000000000000000000000000000608084901b1604600160801b0162ffffff8416627fffff19016102728282612579565b600060806120bc8585856127e3565b901c949350505050565b6000806000866001600160a01b0316630abe96886120e388612047565b6040516001600160e01b031960e084901b16815262ffffff90911660048201526024016040805180830381865afa158015612122573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121469190612ffb565b6001600160801b031691506001600160801b0316915060006121698383876120ad565b60405163bd85b03960e01b8152600481018990529091506000906001600160a01b038a169063bd85b03990602401602060405180830381865afa1580156121b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d8919061302e565b905081156121f0576121eb878284612277565b6121f3565b60005b9998505050505050505050565b604051627eeac760e11b81526001600160a01b03838116600483015262ffffff831660248301526000919085169062fdd58e90604401602060405180830381865afa158015612253573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610288919061302e565b6000806000612286868661284e565b91509150612297868686858561286d565b9695505050505050565b600080600080866001600160a01b0316630abe96886122bf88612047565b6040516001600160e01b031960e084901b16815262ffffff90911660048201526024016040805180830381865afa1580156122fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123229190612ffb565b6001600160801b031691506001600160801b031691506000876001600160a01b031663bd85b039886040518263ffffffff1660e01b815260040161236891815260200190565b602060405180830381865afa158015612385573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a9919061302e565b905080156123cc576123bc868483612277565b6123c7878484612277565b6123d0565b6000805b90999098509650505050505050565b600061027285856109748686612072565b6000806000612400868686612200565b604051630157d2d160e31b815262ffffff8616600482015290915060009081906001600160a01b03891690630abe9688906024016040805180830381865afa158015612450573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124749190612ffb565b60405163bd85b03960e01b815262ffffff891660048201526001600160801b039283169450911691506000906001600160a01b038a169063bd85b03990602401602060405180830381865afa1580156124d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f5919061302e565b9050801561251857612508848483612277565b612513858484612277565b61251c565b6000805b909a909950975050505050505050565b600080600061253b888a612072565b9050600061254a8888846120ad565b905060006125598787856120ad565b905061256782828989612919565b94509450505050965096945050505050565b6000806000836000036125955750600160801b91506127dd9050565b508260008112156125a7579015906000035b6210000081101561279757600160801b9250846001600160801b038111156125d157911591600019045b60018216156125e25792830260801c925b800260801c60028216156125f85792830260801c925b800260801c600482161561260e5792830260801c925b800260801c60088216156126245792830260801c925b800260801c601082161561263a5792830260801c925b800260801c60208216156126505792830260801c925b800260801c60408216156126665792830260801c925b8002608090811c9082161561267d5792830260801c925b800260801c6101008216156126945792830260801c925b800260801c6102008216156126ab5792830260801c925b800260801c6104008216156126c25792830260801c925b800260801c6108008216156126d95792830260801c925b800260801c6110008216156126f05792830260801c925b800260801c6120008216156127075792830260801c925b800260801c61400082161561271e5792830260801c925b800260801c6180008216156127355792830260801c925b800260801c6201000082161561274d5792830260801c925b800260801c620200008216156127655792830260801c925b800260801c6204000082161561277d5792830260801c925b800260801c620800008216156127955792830260801c925b505b826000036127c657604051631dba598d60e11b8152600481018690526024810185905260440160405180910390fd5b816127d15782610272565b610272836000196130c6565b92915050565b6000831561281e5750808302818482816127ff576127ff6130b0565b041461281e576040516363f1e01f60e01b815260040160405180910390fd5b821561028b5760809290921b9182018281101561028b576040516363f1e01f60e01b815260040160405180910390fd5b6000806000198385098385029250828110838203039150509250929050565b60008160000361288e57838381612886576128866130b0565b049050610272565b8382106128ae576040516313eae71560e01b815260040160405180910390fd5b600084868809600186198101871660008190038190049091018683119095039490940294038390049390931760029290940460038102831880820284030280820284030280820284030280820284030280820284030290810290920390910292909202949350505050565b6000808585111561294e576000612930878761305d565b905061293d818688612277565b925061294a818588612277565b9150505b94509492505050565b6001600160a01b038116811461296c57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b600082601f83011261299657600080fd5b8135602067ffffffffffffffff808311156129b3576129b361296f565b8260051b604051601f19603f830116810181811084821117156129d8576129d861296f565b60405293845260208187018101949081019250878511156129f857600080fd5b6020870191505b84821015612a18578135835291830191908301906129ff565b979650505050505050565b60008060008060808587031215612a3957600080fd5b8435612a4481612957565b9350602085013567ffffffffffffffff80821115612a6157600080fd5b612a6d88838901612985565b94506040870135915080821115612a8357600080fd5b612a8f88838901612985565b93506060870135915080821115612aa557600080fd5b50612ab287828801612985565b91505092959194509250565b60008151808452602080850194506020840160005b83811015612aef57815187529582019590820190600101612ad3565b509495945050505050565b60208152600061028b6020830184612abe565b600080600060608486031215612b2257600080fd5b8335612b2d81612957565b9250602084013567ffffffffffffffff80821115612b4a57600080fd5b612b5687838801612985565b93506040860135915080821115612b6c57600080fd5b50612b7986828701612985565b9150509250925092565b60008060008060808587031215612b9957600080fd5b8435612ba481612957565b93506020850135612bb481612957565b9250604085013567ffffffffffffffff80821115612a8357600080fd5b606081526000612be46060830186612abe565b8281036020840152612bf68186612abe565b905082810360408401526122978185612abe565b604081526000612c1d6040830185612abe565b82810360208401526102728185612abe565b62ffffff8116811461296c57600080fd5b600080600080600060a08688031215612c5857600080fd5b8535612c6381612957565b94506020860135612c7381612957565b93506040860135612c8381612c2f565b92506060860135612c9381612c2f565b91506080860135612ca381612c2f565b809150509295509295909350565b6000604080830162ffffff8087168552602060406020870152828751808552606094506060880191506020890160005b82811015612d31578151805187168552858101516001600160801b039081168787015289820151168986015287810151888601526080908101519085015260a09093019290840190600101612ce1565b50919a9950505050505050505050565b60008060008060808587031215612d5757600080fd5b8435612d6281612957565b93506020850135612d7281612c2f565b92506040850135612d8281612c2f565b91506060850135612d9281612c2f565b939692955090935050565b60006020808352835180602085015260005b81811015612dcb57858101830151858201604001528201612daf565b506000604082860101526040601f19601f8301168501019250505092915050565b602080825282518282018190526000919060409081850190868401855b82811015612e4a578151805162ffffff168552868101516001600160801b039081168887015290860151168585015260609093019290850190600101612e09565b5091979650505050505050565b600080600080600060a08688031215612e6f57600080fd5b8535612e7a81612957565b94506020860135612e8a81612957565b9350604086013567ffffffffffffffff80821115612ea757600080fd5b612eb389838a01612985565b94506060880135915080821115612ec957600080fd5b612ed589838a01612985565b93506080880135915080821115612eeb57600080fd5b50612ef888828901612985565b9150509295509295909350565b608081526000612f186080830187612abe565b8281036020840152612f2a8187612abe565b90508281036040840152612f3e8186612abe565b90508281036060840152612a188185612abe565b600080600060608486031215612f6757600080fd5b8335612f7281612957565b92506020840135612f8281612957565b9150604084013567ffffffffffffffff811115612f9e57600080fd5b612b7986828701612985565b600060208284031215612fbc57600080fd5b815161ffff8116811461028b57600080fd5b634e487b7160e01b600052603260045260246000fd5b80516001600160801b038116811461206d57600080fd5b6000806040838503121561300e57600080fd5b61301783612fe4565b915061302560208401612fe4565b90509250929050565b60006020828403121561304057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156127dd576127dd613047565b60006020828403121561308257600080fd5b815161028b81612c2f565b62ffffff8281168282160390808211156130a9576130a9613047565b5092915050565b634e487b7160e01b600052601260045260246000fd5b6000826130e357634e487b7160e01b600052601260045260246000fd5b500490565b808201808211156127dd576127dd613047565b60006001820161310d5761310d613047565b5060010190565b60008161312357613123613047565b506000190190565b62ffffff8181168382160190808211156130a9576130a9613047565b80820281158282048414176127dd576127dd61304756fea2646970667358221220cbd6f6222f12810979380617fc27e86289107996161f84bdf7eaaf37b3e7282a64736f6c63430008160033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.