Feature Tip: Add private address tag to any address under My Name Tag !
ERC-721
Overview
Max Total Supply
204 MUFFIN-POS
Holders
124
Market
Volume (24H)
N/A
Min Price (24H)
N/A
Max Price (24H)
N/A
Other Info
Token Contract
Balance
1 MUFFIN-POSLoading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
Manager
Compiler Version
v0.8.10+commit.fc410830
Optimization Enabled:
Yes with 4500 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.10; import "../interfaces/manager/IManager.sol"; import "./base/ManagerBase.sol"; import "./base/PositionManager.sol"; import "./base/SwapManager.sol"; import "./base/Multicall.sol"; import "./base/SelfPermit.sol"; contract Manager is IManager, ManagerBase, SwapManager, PositionManager, Multicall, SelfPermit { constructor(address _hub, address _WETH9) ManagerBase(_hub, _WETH9) {} }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "../common/IMulticall.sol"; import "./IManagerBase.sol"; import "./ISwapManager.sol"; import "./IPositionManager.sol"; import "./ISelfPermit.sol"; interface IManager is IManagerBase, ISwapManager, IPositionManager, IMulticall, ISelfPermit {}
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.10; import "../../interfaces/common/IWETH.sol"; import "../../interfaces/hub/IMuffinHub.sol"; import "../../interfaces/manager/IManagerBase.sol"; import "../../libraries/utils/SafeTransferLib.sol"; abstract contract ManagerBase is IManagerBase { address public immutable WETH9; address public immutable hub; constructor(address _hub, address _WETH9) { hub = _hub; WETH9 = _WETH9; } modifier fromHub() { require(msg.sender == hub); _; } /// @dev Transform an user address into account id function getAccRefId(address user) internal pure returns (uint256 accRefId) { accRefId = uint160(user); require(accRefId != 0, "ZERO_ACC_REF_ID"); } function payHub( address token, address payer, uint256 amount ) internal { if (token == WETH9 && address(this).balance >= amount) { // pay with WETH9 IWETH(WETH9).deposit{value: amount}(); // wrap only what is needed to pay IWETH(WETH9).transfer(hub, amount); } else { // pull payment SafeTransferLib.safeTransferFrom(token, payer, hub, amount); } } /*=============================================================== * ACCOUNTS *==============================================================*/ /// @dev Called by the hub contract function muffinDepositCallback( address token, uint256 amount, bytes calldata data ) external fromHub { if (amount > 0) payHub(token, abi.decode(data, (address)), amount); } /// @notice Deposit tokens into hub's internal account /// @dev DO NOT deposit rebasing tokens or multiple-address tokens as it will cause loss of funds /// @param recipient Recipient of the token deposit /// @param token Token address /// @param amount Amount to deposit function deposit( address recipient, address token, uint256 amount ) public payable { IMuffinHub(hub).deposit(address(this), getAccRefId(recipient), token, amount, abi.encode(msg.sender)); } /// @notice Withdraw tokens from hub's internal account to recipient /// @param recipient Recipient of the withdrawn token /// @param token Token address /// @param amount Amount to withdraw function withdraw( address recipient, address token, uint256 amount ) public payable { IMuffinHub(hub).withdraw(recipient, getAccRefId(msg.sender), token, amount); } /// @notice Deposit tokens into hub's internal account managed by other address /// @dev DO NOT deposit rebasing tokens or multiple-address tokens as it will cause loss of funds /// @param recipient Recipient of the token deposit /// @param token Token address /// @param amount Amount to deposit function depositToExternal( address recipient, uint256 recipientAccRefId, address token, uint256 amount ) external payable { IMuffinHub(hub).deposit(recipient, recipientAccRefId, token, amount, abi.encode(msg.sender)); } /*=============================================================== * ETH TRANSFER (FOR MULTICALL) *==============================================================*/ /// @notice Unwraps the contract's WETH balance and sends it to recipient as ETH. /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH from users. /// @dev This function should be an intermediate function of an atomic transaction. Do not leave WETH inside this /// contract accross transactions. function unwrapWETH(uint256 amountMinimum, address recipient) external payable { uint256 balanceWETH = IWETH(WETH9).balanceOf(address(this)); require(balanceWETH >= amountMinimum, "Insufficient WETH"); if (balanceWETH > 0) { IWETH(WETH9).withdraw(balanceWETH); SafeTransferLib.safeTransferETH(recipient, balanceWETH); } } /// @notice Refunds any ETH balance held by this contract to the `msg.sender` /// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps /// that use ether for the input amount /// @dev This function should be an intermediate function of an atomic transaction. Do not leave ETH inside this /// contract accross transactions. function refundETH() external payable { if (address(this).balance > 0) SafeTransferLib.safeTransferETH(msg.sender, address(this).balance); } receive() external payable { require(msg.sender == WETH9); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.10; import "../../interfaces/hub/IMuffinHub.sol"; import "../../interfaces/hub/positions/IMuffinHubPositions.sol"; import "../../interfaces/manager/IPositionManager.sol"; import "../../libraries/math/PoolMath.sol"; import "../../libraries/math/TickMath.sol"; import "../../libraries/math/UnsafeMath.sol"; import "../../libraries/Pools.sol"; import "../../libraries/Positions.sol"; import "./ManagerBase.sol"; import "./ERC721Extended.sol"; abstract contract PositionManager is IPositionManager, ManagerBase, ERC721Extended { struct PositionInfo { address owner; uint40 pairId; uint8 tierId; int24 tickLower; int24 tickUpper; } /// @notice Mapping of token id to position managed by this contract mapping(uint256 => PositionInfo) public positionsByTokenId; struct Pair { address token0; address token1; } /// @dev Next pair id. skips 0 uint40 internal nextPairId = 1; /// @notice Mapping of pair id to its underlying tokens mapping(uint40 => Pair) public pairs; /// @notice Mapping of pool id to pair id mapping(bytes32 => uint40) public pairIdsByPoolId; constructor() ERC721Extended("Muffin Position", "MUFFIN-POS") {} modifier checkApproved(uint256 tokenId) { _checkApproved(tokenId); _; } function _checkApproved(uint256 tokenId) internal view { require(_isApprovedOrOwner(msg.sender, tokenId), "NOT_APPROVED"); } function _getPoolId(address token0, address token1) internal pure returns (bytes32) { return keccak256(abi.encode(token0, token1)); } /// @dev Cache the underlying tokens of a pool and return an id of the cache function _cacheTokenPair(address token0, address token1) internal returns (uint40 pairId) { bytes32 poolId = _getPoolId(token0, token1); pairId = pairIdsByPoolId[poolId]; if (pairId == 0) { pairIdsByPoolId[poolId] = (pairId = nextPairId++); pairs[pairId] = Pair(token0, token1); } } /*=============================================================== * CREATE POOL / TIER *==============================================================*/ /// @notice Create a pool for token0 and token1 if it hasn't been created /// @dev DO NOT create pool with rebasing tokens or multiple-address tokens as it will cause loss of funds /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param sqrtGamma Sqrt of (1 - percentage swap fee of the 1st tier) /// @param sqrtPrice Sqrt price of token0 denominated in token1 function createPool( address token0, address token1, uint24 sqrtGamma, uint128 sqrtPrice, bool useAccount ) external payable { IMuffinHub _hub = IMuffinHub(hub); // check tick spacing. zero means the pool is not created (uint8 tickSpacing, ) = _hub.getPoolParameters(_getPoolId(token0, token1)); if (tickSpacing == 0) { _depositForTierCreation(token0, token1, sqrtPrice, useAccount); _hub.createPool(token0, token1, sqrtGamma, sqrtPrice, getAccRefId(msg.sender)); } _cacheTokenPair(token0, token1); } /// @notice Add a tier to a pool /// @dev This function is subject to sandwitch attack which costs more tokens to add a tier, but the extra cost /// should be small in common token pairs. Also, users can multicall with "mint" to do slippage check. /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param sqrtGamma Sqrt of (1 - percentage swap fee of the 1st tier) /// @param expectedTierId Expected id of the new tier. Revert if unmatched. Set to type(uint8).max for skipping the check. function addTier( address token0, address token1, uint24 sqrtGamma, bool useAccount, uint8 expectedTierId ) external payable { IMuffinHub _hub = IMuffinHub(hub); // get first tier's sqrtPrice. revert if pool is not created. uint128 sqrtPrice = _hub.getTier(_getPoolId(token0, token1), 0).sqrtPrice; _depositForTierCreation(token0, token1, sqrtPrice, useAccount); uint8 tierId = _hub.addTier(token0, token1, sqrtGamma, getAccRefId(msg.sender)); require(tierId == expectedTierId || expectedTierId == type(uint8).max); _cacheTokenPair(token0, token1); } /// @dev Deposit tokens required to create a tier function _depositForTierCreation( address token0, address token1, uint128 sqrtPrice, bool useAccount ) internal { unchecked { uint256 amount0 = UnsafeMath.ceilDiv(uint256(Pools.BASE_LIQUIDITY_D8) << (72 + 8), sqrtPrice); uint256 amount1 = UnsafeMath.ceilDiv(uint256(Pools.BASE_LIQUIDITY_D8) * sqrtPrice, 1 << (72 - 8)); if (useAccount) { bytes32 accHash = keccak256(abi.encode(address(this), getAccRefId(msg.sender))); uint256 amt0Acc = _getAccountBalance(token0, accHash); uint256 amt1Acc = _getAccountBalance(token1, accHash); if (amount0 > amt0Acc) deposit(msg.sender, token0, amount0 - amt0Acc); if (amount1 > amt1Acc) deposit(msg.sender, token1, amount1 - amt1Acc); } else { deposit(msg.sender, token0, amount0); deposit(msg.sender, token1, amount1); } } } function _getAccountBalance(address token, bytes32 accHash) internal view returns (uint256) { return IMuffinHub(hub).accounts(token, accHash); } /*=============================================================== * ADD LIQUIDITY *==============================================================*/ /// @dev Called by hub contract function muffinMintCallback( address token0, address token1, uint256 amount0, uint256 amount1, bytes calldata data ) external fromHub { address payer = abi.decode(data, (address)); if (amount0 > 0) payHub(token0, payer, amount0); if (amount1 > 0) payHub(token1, payer, amount1); } /** * @notice Mint a position NFT * @param params MintParams struct * @return tokenId Id of the NFT * @return liquidityD8 Amount of liquidity added (divided by 2^8) * @return amount0 Token0 amount paid * @return amount1 Token1 amount paid */ function mint(MintParams calldata params) external payable returns ( uint256 tokenId, uint96 liquidityD8, uint256 amount0, uint256 amount1 ) { tokenId = _mintNext(params.recipient); PositionInfo memory info = PositionInfo({ owner: params.recipient, pairId: _cacheTokenPair(params.token0, params.token1), tierId: params.tierId, tickLower: params.tickLower, tickUpper: params.tickUpper }); positionsByTokenId[tokenId] = info; (liquidityD8, amount0, amount1) = _addLiquidity( info, Pair(params.token0, params.token1), tokenId, params.amount0Desired, params.amount1Desired, params.amount0Min, params.amount1Min, params.useAccount ); } /** * @notice Add liquidity to an existing position * @param params AddLiquidityParams struct * @return liquidityD8 Amount of liquidity added (divided by 2^8) * @return amount0 Token0 amount paid * @return amount1 Token1 amount paid */ function addLiquidity(AddLiquidityParams calldata params) external payable checkApproved(params.tokenId) returns ( uint96 liquidityD8, uint256 amount0, uint256 amount1 ) { PositionInfo memory info = positionsByTokenId[params.tokenId]; (liquidityD8, amount0, amount1) = _addLiquidity( info, pairs[info.pairId], params.tokenId, params.amount0Desired, params.amount1Desired, params.amount0Min, params.amount1Min, params.useAccount ); } function _addLiquidity( PositionInfo memory info, Pair memory pair, uint256 tokenId, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, bool useAccount ) internal returns ( uint96 liquidityD8, uint256 amount0, uint256 amount1 ) { liquidityD8 = PoolMath.calcLiquidityForAmts( IMuffinHub(hub).getTier(_getPoolId(pair.token0, pair.token1), info.tierId).sqrtPrice, TickMath.tickToSqrtPrice(info.tickLower), TickMath.tickToSqrtPrice(info.tickUpper), amount0Desired, amount1Desired ); (amount0, amount1) = IMuffinHubPositions(hub).mint( IMuffinHubPositionsActions.MintParams({ token0: pair.token0, token1: pair.token1, tierId: info.tierId, tickLower: info.tickLower, tickUpper: info.tickUpper, liquidityD8: liquidityD8, recipient: address(this), positionRefId: tokenId, senderAccRefId: useAccount ? getAccRefId(msg.sender) : 0, data: abi.encode(msg.sender) }) ); require(amount0 >= amount0Min && amount1 >= amount1Min, "Price slippage"); } /*=============================================================== * REMOVE LIQUIDITY *==============================================================*/ /** * @notice Remove liquidity from a position * @param params RemoveLiquidityParams struct * @return amount0 Token0 amount from the removed liquidity * @return amount1 Token1 amount from the removed liquidity * @return feeAmount0 Token0 fee collected from the position * @return feeAmount1 Token1 fee collected from the position */ function removeLiquidity(RemoveLiquidityParams calldata params) external payable checkApproved(params.tokenId) returns ( uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1 ) { PositionInfo storage info = positionsByTokenId[params.tokenId]; Pair memory pair = pairs[info.pairId]; IMuffinHubPositionsActions.BurnParams memory burnParams = IMuffinHubPositionsActions.BurnParams({ token0: pair.token0, token1: pair.token1, tierId: info.tierId, tickLower: info.tickLower, tickUpper: info.tickUpper, liquidityD8: params.liquidityD8, positionRefId: params.tokenId, accRefId: getAccRefId(info.owner), collectAllFees: params.collectAllFees }); (amount0, amount1, feeAmount0, feeAmount1) = params.settled ? IMuffinHubPositions(hub).collectSettled(burnParams) : IMuffinHubPositions(hub).burn(burnParams); require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, "Price slippage"); if (params.withdrawTo != address(0)) { uint256 sumAmt0 = amount0 + feeAmount0; uint256 sumAmt1 = amount1 + feeAmount1; if (sumAmt0 > 0) withdraw(params.withdrawTo, pair.token0, sumAmt0); if (sumAmt1 > 0) withdraw(params.withdrawTo, pair.token1, sumAmt1); } } /*=============================================================== * LIMIT ORDER *==============================================================*/ /// @notice Set position's limit order type /// @param tokenId Id of the position NFT. Or set to zero to indicate the latest NFT id in this contract /// (useful for chaining this function after `mint` in a multicall) /// @param limitOrderType Direction of limit order (0: N/A, 1: zero->one, 2: one->zero) function setLimitOrderType(uint256 tokenId, uint8 limitOrderType) external payable { // zero is the magic number to indicate the latest token id if (tokenId == 0) tokenId = latestTokenId(); _checkApproved(tokenId); PositionInfo storage info = positionsByTokenId[tokenId]; Pair storage pair = pairs[info.pairId]; IMuffinHubPositions(hub).setLimitOrderType( pair.token0, pair.token1, info.tierId, info.tickLower, info.tickUpper, tokenId, limitOrderType ); } /*=============================================================== * BURN NFT *==============================================================*/ /// @notice Burn NFTs of empty positions /// @param tokenIds Array of NFT id function burn(uint256[] calldata tokenIds) external payable { for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; // check existance + approval _checkApproved(tokenId); // check if position is empty PositionInfo storage info = positionsByTokenId[tokenId]; Pair storage pair = pairs[info.pairId]; Positions.Position memory position = IMuffinHub(hub).getPosition( _getPoolId(pair.token0, pair.token1), address(this), tokenId, info.tierId, info.tickLower, info.tickUpper ); require(position.liquidityD8 == 0, "NOT_EMPTY"); _burn(tokenId); delete positionsByTokenId[tokenId]; } } /*=============================================================== * VIEW FUNCTIONS *==============================================================*/ /// @notice Get the position info of an NFT /// @param tokenId Id of the NFT function getPosition(uint256 tokenId) external view returns ( address owner, address token0, address token1, uint8 tierId, int24 tickLower, int24 tickUpper, Positions.Position memory position ) { PositionInfo storage info = positionsByTokenId[tokenId]; (owner, tierId, tickLower, tickUpper) = (info.owner, info.tierId, info.tickLower, info.tickUpper); require(info.owner != address(0), "NOT_EXISTS"); Pair storage pair = pairs[info.pairId]; (token0, token1) = (pair.token0, pair.token1); position = IMuffinHub(hub).getPosition(_getPoolId(token0, token1), address(this), tokenId, tierId, tickLower, tickUpper); } /*=============================================================== * OVERRIDE FUNCTIONS IN ERC721 *==============================================================*/ /// @dev override `_getOwner` in ERC721.sol function _getOwner(uint256 tokenId) internal view override returns (address owner) { owner = positionsByTokenId[tokenId].owner; } /// @dev override `_setOwner` in ERC721.sol function _setOwner(uint256 tokenId, address owner) internal override { positionsByTokenId[tokenId].owner = owner; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.10; import "../../interfaces/hub/IMuffinHub.sol"; import "../../interfaces/manager/ISwapManager.sol"; import "../../libraries/math/Math.sol"; import "./ManagerBase.sol"; abstract contract SwapManager is ISwapManager, ManagerBase { using Math for uint256; error DeadlinePassed(); modifier checkDeadline(uint256 deadline) { _checkDeadline(deadline); _; } /// @dev Reverts if the transaction deadline has passed function _checkDeadline(uint256 deadline) internal view { if (block.timestamp > deadline) revert DeadlinePassed(); } /// @dev Called by the hub contract function muffinSwapCallback( address tokenIn, address, // tokenOut, uint256 amountIn, uint256, // amountOut, bytes calldata data ) external fromHub { if (amountIn > 0) payHub(tokenIn, abi.decode(data, (address)), amountIn); } /** * @notice Swap `amountIn` of one token for as much as possible of another token * @param tokenIn Address of input token * @param tokenOut Address of output token * @param tierChoices Bitmap to select which tiers are allowed to swap (e.g. 0xFFFF to allow all possible tiers) * @param amountIn Desired input amount * @param amountOutMinimum Minimum output amount * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountOut Output amount of the swap */ function exactInSingle( address tokenIn, address tokenOut, uint256 tierChoices, uint256 amountIn, uint256 amountOutMinimum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable checkDeadline(deadline) returns (uint256 amountOut) { (, amountOut) = IMuffinHub(hub).swap( tokenIn, tokenOut, tierChoices, amountIn.toInt256(), toAccount ? address(this) : recipient, toAccount ? getAccRefId(recipient) : 0, fromAccount ? getAccRefId(msg.sender) : 0, abi.encode(msg.sender) ); require(amountOut >= amountOutMinimum, "TOO_LITTLE_RECEIVED"); } /** * @notice Swap `amountIn` of one token for as much as possible of another along the specified path * @param path Multi-hop path * @param amountIn Desired input amount * @param amountOutMinimum Minimum output amount * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountOut Output amount of the swap */ function exactIn( bytes calldata path, uint256 amountIn, uint256 amountOutMinimum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable checkDeadline(deadline) returns (uint256 amountOut) { (, amountOut) = IMuffinHub(hub).swapMultiHop( IMuffinHubActions.SwapMultiHopParams({ path: path, amountDesired: amountIn.toInt256(), recipient: toAccount ? address(this) : recipient, recipientAccRefId: toAccount ? getAccRefId(recipient) : 0, senderAccRefId: fromAccount ? getAccRefId(msg.sender) : 0, data: abi.encode(msg.sender) }) ); require(amountOut >= amountOutMinimum, "TOO_LITTLE_RECEIVED"); } /** * @notice Swap as little as possible of one token for `amountOut` of another token * @param tokenIn Address of input token * @param tokenOut Address of output token * @param tierChoices Bitmap to select which tiers are allowed to swap (e.g. 0xFFFF to allow all possible tiers) * @param amountOut Desired output amount * @param amountInMaximum Maximum input amount to pay * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountIn Input amount of the swap */ function exactOutSingle( address tokenIn, address tokenOut, uint256 tierChoices, uint256 amountOut, uint256 amountInMaximum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable checkDeadline(deadline) returns (uint256 amountIn) { (amountIn, ) = IMuffinHub(hub).swap( tokenIn, tokenOut, tierChoices, -amountOut.toInt256(), toAccount ? address(this) : recipient, toAccount ? getAccRefId(recipient) : 0, fromAccount ? getAccRefId(msg.sender) : 0, abi.encode(msg.sender) ); require(amountIn <= amountInMaximum, "TOO_MUCH_REQUESTED"); } /** * @notice Swap as little as possible of one token for `amountOut` of another along the specified path * @param path Address of output token * @param amountOut Desired output amount * @param amountInMaximum Maximum input amount to pay * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountIn Input amount of the swap */ function exactOut( bytes calldata path, uint256 amountOut, uint256 amountInMaximum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable checkDeadline(deadline) returns (uint256 amountIn) { (amountIn, ) = IMuffinHub(hub).swapMultiHop( IMuffinHubActions.SwapMultiHopParams({ path: path, amountDesired: -amountOut.toInt256(), recipient: toAccount ? address(this) : recipient, recipientAccRefId: toAccount ? getAccRefId(recipient) : 0, senderAccRefId: fromAccount ? getAccRefId(msg.sender) : 0, data: abi.encode(msg.sender) }) ); require(amountIn <= amountInMaximum, "TOO_MUCH_REQUESTED"); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.10; import "../../interfaces/common/IMulticall.sol"; /// @title Multicall /// @notice Enables calling multiple methods in a single call to the contract abstract contract Multicall is IMulticall { /// @inheritdoc IMulticall function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) { results = new bytes[](data.length); unchecked { for (uint256 i = 0; i < data.length; i++) { (bool success, bytes memory result) = address(this).delegatecall(data[i]); if (!success) { if (result.length == 0) revert(); assembly { revert(add(32, result), mload(result)) } } results[i] = result; } } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.10; import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; import "../../interfaces/common/IERC20PermitAllowed.sol"; import "../../interfaces/manager/ISelfPermit.sol"; abstract contract SelfPermit is ISelfPermit { /// @notice Permits this contract to spend a given token from `msg.sender` /// @dev The `owner` is always msg.sender and the `spender` is always address(this). /// @param token The address of the token spent /// @param value The amount that can be spent of token /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermit( address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable { IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s); } /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter /// @dev The `owner` is always msg.sender and the `spender` is always address(this) /// @param token The address of the token spent /// @param nonce The current nonce of the owner /// @param expiry The timestamp at which the permit is no longer valid /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermitAllowed( address token, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) external payable { IERC20PermitAllowed(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; /// @title Multicall interface /// @notice Enables calling multiple methods in a single call to the contract interface IMulticall { /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed /// @dev The `msg.value` should not be trusted for any method callable from multicall. /// @param data The encoded function data for each of the calls to make to this contract /// @return results The results from each of the calls passed in via data function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface IManagerBase { function WETH9() external view returns (address); function hub() external view returns (address); function muffinDepositCallback( address token, uint256 amount, bytes calldata data ) external; /// @notice Deposit tokens into hub's internal account /// @dev DO NOT deposit rebasing tokens or multiple-address tokens as it will cause loss of funds /// @param recipient Recipient of the token deposit /// @param token Token address /// @param amount Amount to deposit function deposit( address recipient, address token, uint256 amount ) external payable; /// @notice Withdraw tokens from hub's internal account to recipient /// @param recipient Recipient of the withdrawn token /// @param token Token address /// @param amount Amount to withdraw function withdraw( address recipient, address token, uint256 amount ) external payable; /// @notice Deposit tokens into hub's internal account managed by other address /// @dev DO NOT deposit rebasing tokens or multiple-address tokens as it will cause loss of funds /// @param recipient Recipient of the token deposit /// @param token Token address /// @param amount Amount to deposit function depositToExternal( address recipient, uint256 recipientAccRefId, address token, uint256 amount ) external payable; /// @notice Unwraps the contract's WETH balance and sends it to recipient as ETH. /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH from users. /// @dev This function should be an intermediate function of an atomic transaction. Do not leave WETH inside this /// contract accross transactions. function unwrapWETH(uint256 amountMinimum, address recipient) external payable; /// @notice Refunds any ETH balance held by this contract to the `msg.sender` /// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps /// that use ether for the input amount /// @dev This function should be an intermediate function of an atomic transaction. Do not leave ETH inside this /// contract accross transactions. function refundETH() external payable; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "./IManagerBase.sol"; interface ISwapManager is IManagerBase { function muffinSwapCallback( address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut, bytes calldata data ) external; /** * @notice Swap `amountIn` of one token for as much as possible of another token * @param tokenIn Address of input token * @param tokenOut Address of output token * @param tierChoices Bitmap to select which tiers are allowed to swap (e.g. 0xFFFF to allow all possible tiers) * @param amountIn Desired input amount * @param amountOutMinimum Minimum output amount * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountOut Output amount of the swap */ function exactInSingle( address tokenIn, address tokenOut, uint256 tierChoices, uint256 amountIn, uint256 amountOutMinimum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable returns (uint256 amountOut); /** * @notice Swap `amountIn` of one token for as much as possible of another along the specified path * @param path Multi-hop path * @param amountIn Desired input amount * @param amountOutMinimum Minimum output amount * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountOut Output amount of the swap */ function exactIn( bytes calldata path, uint256 amountIn, uint256 amountOutMinimum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable returns (uint256 amountOut); /** * @notice Swap as little as possible of one token for `amountOut` of another token * @param tokenIn Address of input token * @param tokenOut Address of output token * @param tierChoices Bitmap to select which tiers are allowed to swap (e.g. 0xFFFF to allow all possible tiers) * @param amountOut Desired output amount * @param amountInMaximum Maximum input amount to pay * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountIn Input amount of the swap */ function exactOutSingle( address tokenIn, address tokenOut, uint256 tierChoices, uint256 amountOut, uint256 amountInMaximum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable returns (uint256 amountIn); /** * @notice Swap as little as possible of one token for `amountOut` of another along the specified path * @param path Address of output token * @param amountOut Desired output amount * @param amountInMaximum Maximum input amount to pay * @param recipient Address of the recipient of the output token * @param fromAccount True for using sender's internal account to pay * @param toAccount True for storing output tokens in recipient's internal account * @param deadline Transaction reverts if it's processed after deadline * @return amountIn Input amount of the swap */ function exactOut( bytes calldata path, uint256 amountOut, uint256 amountInMaximum, address recipient, bool fromAccount, bool toAccount, uint256 deadline ) external payable returns (uint256 amountIn); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "../../libraries/Positions.sol"; import "./IManagerBase.sol"; import "./IERC721Extended.sol"; interface IPositionManager is IERC721Extended, IManagerBase { /// @notice Mapping of token id to position managed by this contract function positionsByTokenId(uint256 tokenId) external view returns ( address owner, uint40 pairId, uint8 tierId, int24 tickLower, int24 tickUpper ); /// @notice Mapping of pair id to its underlying tokens function pairs(uint40 pairId) external view returns (address token0, address token1); /// @notice Mapping of pool id to pair id function pairIdsByPoolId(bytes32 poolId) external view returns (uint40 pairId); /// @notice Create a pool for token0 and token1 if it hasn't been created /// @dev DO NOT create pool with rebasing tokens or multiple-address tokens as it will cause loss of funds /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param sqrtGamma Sqrt of (1 - percentage swap fee of the 1st tier) /// @param sqrtPrice Sqrt price of token0 denominated in token1 function createPool( address token0, address token1, uint24 sqrtGamma, uint128 sqrtPrice, bool useAccount ) external payable; /// @notice Add a tier to a pool /// @dev This function is subject to sandwitch attack which costs more tokens to add a tier, but the extra cost /// should be small in common token pairs. Also, users can multicall with "mint" to do slippage check. /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param sqrtGamma Sqrt of (1 - percentage swap fee of the 1st tier) /// @param expectedTierId Expected id of the new tier. Revert if unmatched. Set to type(uint8).max for skipping the check. function addTier( address token0, address token1, uint24 sqrtGamma, bool useAccount, uint8 expectedTierId ) external payable; /// @dev Called by hub contract function muffinMintCallback( address token0, address token1, uint256 amount0, uint256 amount1, bytes calldata data ) external; /** * @notice Parameters for the mint function * @param token0 Address of token0 of the pool * @param token1 Address of token1 of the pool * @param tierId Position's tier index * @param tickLower Position's lower tick boundary * @param tickUpper Position's upper tick boundary * @param amount0Desired Desired token0 amount to add to the pool * @param amount1Desired Desired token1 amount to add to the pool * @param amount0Min Minimum token0 amount * @param amount1Min Minimum token1 amount * @param recipient Recipient of the position token * @param useAccount Use sender's internal account to pay */ struct MintParams { address token0; address token1; uint8 tierId; int24 tickLower; int24 tickUpper; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; bool useAccount; } /** * @notice Mint a position NFT * @param params MintParams struct * @return tokenId Id of the NFT * @return liquidityD8 Amount of liquidity added (divided by 2^8) * @return amount0 Token0 amount paid * @return amount1 Token1 amount paid */ function mint(MintParams calldata params) external payable returns ( uint256 tokenId, uint96 liquidityD8, uint256 amount0, uint256 amount1 ); /** * @notice Parameters for the addLiquidity function * @param tokenId Id of the position NFT * @param amount0Desired Desired token0 amount to add to the pool * @param amount1Desired Desired token1 amount to add to the pool * @param amount0Min Minimum token0 amount * @param amount1Min Minimum token1 amount * @param useAccount Use sender's internal account to pay */ struct AddLiquidityParams { uint256 tokenId; uint256 amount0Desired; uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; bool useAccount; } /** * @notice Add liquidity to an existing position * @param params AddLiquidityParams struct * @return liquidityD8 Amount of liquidity added (divided by 2^8) * @return amount0 Token0 amount paid * @return amount1 Token1 amount paid */ function addLiquidity(AddLiquidityParams calldata params) external payable returns ( uint96 liquidityD8, uint256 amount0, uint256 amount1 ); /** * @notice Parameters for the removeLiquidity function * @param tokenId Id of the position NFT * @param liquidityD8 Amount of liquidity to remove (divided by 2^8) * @param amount0Min Minimum token0 amount received from the removed liquidity * @param amount1Min Minimum token1 amount received from the removed liquidity * @param withdrawTo Recipient of the withdrawn tokens. Set to zero for no withdrawal * @param collectAllFees True to collect all remaining accrued fees in the position * @param settled True if the position is settled */ struct RemoveLiquidityParams { uint256 tokenId; uint96 liquidityD8; uint256 amount0Min; uint256 amount1Min; address withdrawTo; bool collectAllFees; bool settled; } /** * @notice Remove liquidity from a position * @param params RemoveLiquidityParams struct * @return amount0 Token0 amount from the removed liquidity * @return amount1 Token1 amount from the removed liquidity * @return feeAmount0 Token0 fee collected from the position * @return feeAmount1 Token1 fee collected from the position */ function removeLiquidity(RemoveLiquidityParams calldata params) external payable returns ( uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1 ); /// @notice Set position's limit order type /// @param tokenId Id of the position NFT. Or set to zero to indicate the latest NFT id in this contract /// (useful for chaining this function after `mint` in a multicall) /// @param limitOrderType Direction of limit order (0: N/A, 1: zero->one, 2: one->zero) function setLimitOrderType(uint256 tokenId, uint8 limitOrderType) external payable; /// @notice Burn NFTs of empty positions /// @param tokenIds Array of NFT id function burn(uint256[] calldata tokenIds) external payable; /// @notice Get the position info of an NFT /// @param tokenId Id of the NFT function getPosition(uint256 tokenId) external view returns ( address owner, address token0, address token1, uint8 tierId, int24 tickLower, int24 tickUpper, Positions.Position memory position ); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface ISelfPermit { /// @notice Permits this contract to spend a given token from `msg.sender` /// @dev The `owner` is always msg.sender and the `spender` is always address(this). /// @param token The address of the token spent /// @param value The amount that can be spent of token /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermit( address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable; /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter /// @dev The `owner` is always msg.sender and the `spender` is always address(this) /// @param token The address of the token spent /// @param nonce The current nonce of the owner /// @param expiry The timestamp at which the permit is no longer valid /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function selfPermitAllowed( address token, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) external payable; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "./math/Math.sol"; library Positions { struct Position { uint96 liquidityD8; uint80 feeGrowthInside0Last; // UQ16.64 uint80 feeGrowthInside1Last; // UQ16.64 uint8 limitOrderType; uint32 settlementSnapshotId; } // Limit order types: uint8 internal constant NOT_LIMIT_ORDER = 0; uint8 internal constant ZERO_FOR_ONE = 1; uint8 internal constant ONE_FOR_ZERO = 2; /** * @param positions Mapping of positions * @param owner Position owner's address * @param refId Arbitrary identifier set by the position owner * @param tierId Index of the tier which the position is in * @param tickLower Lower tick boundary of the position * @param tickUpper Upper tick boundary of the position * @return position The position object */ function get( mapping(bytes32 => Position) storage positions, address owner, uint256 refId, uint8 tierId, int24 tickLower, int24 tickUpper ) internal view returns (Position storage position) { position = positions[keccak256(abi.encodePacked(owner, tierId, tickLower, tickUpper, refId))]; } /** * @notice Update position's liquidity and accrue fees * @dev When adding liquidity, feeGrowthInside{0,1} are updated so as to accrue fees without the need to transfer * them to owner's account. When removing partial liquidity, feeGrowthInside{0,1} are unchanged and partial fees are * transferred to owner's account proportionally to amount of liquidity removed. * * @param liquidityDeltaD8 Amount of liquidity change in the position, scaled down 2^8 * @param feeGrowthInside0 Pool's current accumulated fee0 per unit of liquidity inside the position's price range * @param feeGrowthInside1 Pool's current accumulated fee1 per unit of liquidity inside the position's price range * @param collectAllFees True to collect the position's all accrued fees * @return feeAmtOut0 Amount of fee0 to transfer to owner account (≤ 2^(128+80)) * @return feeAmtOut1 Amount of fee1 to transfer to owner account (≤ 2^(128+80)) */ function update( Position storage self, int96 liquidityDeltaD8, uint80 feeGrowthInside0, uint80 feeGrowthInside1, bool collectAllFees ) internal returns (uint256 feeAmtOut0, uint256 feeAmtOut1) { unchecked { uint96 liquidityD8 = self.liquidityD8; uint96 liquidityD8New = Math.addInt96(liquidityD8, liquidityDeltaD8); uint80 feeGrowthDelta0 = feeGrowthInside0 - self.feeGrowthInside0Last; uint80 feeGrowthDelta1 = feeGrowthInside1 - self.feeGrowthInside1Last; self.liquidityD8 = liquidityD8New; if (collectAllFees) { feeAmtOut0 = (uint256(liquidityD8) * feeGrowthDelta0) >> 56; feeAmtOut1 = (uint256(liquidityD8) * feeGrowthDelta1) >> 56; self.feeGrowthInside0Last = feeGrowthInside0; self.feeGrowthInside1Last = feeGrowthInside1; // } else if (liquidityDeltaD8 > 0) { self.feeGrowthInside0Last = feeGrowthInside0 - uint80((uint256(liquidityD8) * feeGrowthDelta0) / liquidityD8New); self.feeGrowthInside1Last = feeGrowthInside1 - uint80((uint256(liquidityD8) * feeGrowthDelta1) / liquidityD8New); // } else if (liquidityDeltaD8 < 0) { feeAmtOut0 = (uint256(uint96(-liquidityDeltaD8)) * feeGrowthDelta0) >> 56; feeAmtOut1 = (uint256(uint96(-liquidityDeltaD8)) * feeGrowthDelta1) >> 56; } } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; interface IERC721Extended is IERC165, IERC721, IERC721Metadata { function tokenDescriptor() external view returns (address); function tokenDescriptorSetter() external view returns (address); function totalSupply() external view returns (uint256); function latestTokenId() external view returns (uint256); function nonces(uint256 tokenId) external view returns (uint256 nonce); function PERMIT_TYPEHASH() external view returns (bytes32); function DOMAIN_SEPARATOR() external view returns (bytes32); function permit( address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; library Math { /// @dev Compute z = x + y, where z must be non-negative and fit in a 96-bit unsigned integer function addInt96(uint96 x, int96 y) internal pure returns (uint96 z) { unchecked { uint256 s = x + uint256(int256(y)); // overflow is fine here assert(s <= type(uint96).max); z = uint96(s); } } /// @dev Compute z = x + y, where z must be non-negative and fit in a 128-bit unsigned integer function addInt128(uint128 x, int128 y) internal pure returns (uint128 z) { unchecked { uint256 s = x + uint256(int256(y)); // overflow is fine here assert(s <= type(uint128).max); z = uint128(s); } } function max(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x > y ? x : y; } /// @dev Subtract an amount from x until the amount reaches y or all x is subtracted (i.e. the result reches zero). /// Return the subtraction result and the remaining amount to subtract (if there's any) function subUntilZero(uint256 x, uint256 y) internal pure returns (uint256 z, uint256 r) { unchecked { if (x >= y) z = x - y; else r = y - x; } } // ----- cast ----- function toUint128(uint256 x) internal pure returns (uint128 z) { assert(x <= type(uint128).max); z = uint128(x); } function toUint96(uint256 x) internal pure returns (uint96 z) { assert(x <= type(uint96).max); z = uint96(x); } function toInt256(uint256 x) internal pure returns (int256 z) { assert(x <= uint256(type(int256).max)); z = int256(x); } function toInt96(uint96 x) internal pure returns (int96 z) { assert(x <= uint96(type(int96).max)); z = int96(x); } // ----- checked arithmetic ----- // (these functions are for using checked arithmetic in an unchecked scope) function add(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x + y; } function add(int256 x, int256 y) internal pure returns (int256 z) { z = x + y; } function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x - y; } function sub(int256 x, int256 y) internal pure returns (int256 z) { z = x - y; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface IWETH { function deposit() external payable; function withdraw(uint256) external; function balanceOf(address account) external view returns (uint256); function transfer(address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "./IMuffinHubBase.sol"; import "./IMuffinHubEvents.sol"; import "./IMuffinHubActions.sol"; import "./IMuffinHubView.sol"; interface IMuffinHub is IMuffinHubBase, IMuffinHubEvents, IMuffinHubActions, IMuffinHubView {}
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @dev Adapted from Rari's Solmate https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol /// Edited from using error message to custom error for lower bytecode size. /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. library SafeTransferLib { error FailedTransferETH(); error FailedTransfer(); error FailedTransferFrom(); error FailedApprove(); /*/////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool callStatus; assembly { // Transfer the ETH and store if it succeeded or not. callStatus := call(gas(), to, amount, 0, 0, 0, 0) } if (!callStatus) revert FailedTransferETH(); } /*/////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( address token, address from, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector. mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument. mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 100 because the calldata length is 4 + 32 * 3. callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0) } if (!didLastOptionalReturnCallSucceed(callStatus)) revert FailedTransferFrom(); } function safeTransfer( address token, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector. mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) } if (!didLastOptionalReturnCallSucceed(callStatus)) revert FailedTransfer(); } function safeApprove( address token, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector. mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) } if (!didLastOptionalReturnCallSucceed(callStatus)) revert FailedApprove(); } /*/////////////////////////////////////////////////////////////// INTERNAL HELPER LOGIC //////////////////////////////////////////////////////////////*/ function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) { assembly { // If the call reverted: if iszero(callStatus) { // Copy the revert message into memory. returndatacopy(0, 0, returndatasize()) // Revert with the same message. revert(0, returndatasize()) } switch returndatasize() case 32 { // Copy the return data into memory. returndatacopy(0, 0, returndatasize()) // Set success to whether it returned true. success := iszero(iszero(mload(0))) } case 0 { // There was no return data. success := 1 } default { // It returned some malformed input. success := 0 } } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface IMuffinHubBase { /// @notice Get the contract governance address function governance() external view returns (address); /// @notice Get token balance of a user's internal account /// @param token Token address /// @param accHash keccek256 hash of (owner, accRefId), where accRefId is an arbitrary reference id from account owner /// @return balance Token balance in the account function accounts(address token, bytes32 accHash) external view returns (uint256 balance); /// @notice Get token's reentrancy lock and accrued protocol fees /// @param token Token address /// @return locked 1 if token is locked, otherwise unlocked /// @return protocolFeeAmt Amount of token accrued as protocol fee function tokens(address token) external view returns (uint8 locked, uint248 protocolFeeAmt); /// @notice Get the addresses of the underlying tokens of a pool /// @param poolId Pool id, i.e. keccek256 hash of (token0, token1) /// @return token0 Address of the pool's token0 /// @return token1 Address of the pool's token1 function underlyings(bytes32 poolId) external view returns (address token0, address token1); /// @notice Maximum number of tiers each pool can technically have. This number might vary in different networks. function maxNumOfTiers() external pure returns (uint256); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface IMuffinHubEvents { /// @notice Emitted when user deposits tokens to an account event Deposit( address indexed recipient, uint256 indexed recipientAccRefId, address indexed token, uint256 amount, address sender ); /// @notice Emitted when user withdraws tokens from an account event Withdraw( address indexed sender, uint256 indexed senderAccRefId, address indexed token, uint256 amount, address recipient ); /// @notice Emitted when a pool is created event PoolCreated(address indexed token0, address indexed token1, bytes32 indexed poolId); /// @notice Emitted when a new tier is added, or when tier's parameters are updated event UpdateTier( bytes32 indexed poolId, uint8 indexed tierId, uint24 indexed sqrtGamma, uint128 sqrtPrice, uint8 limitOrderTickSpacingMultiplier ); /// @notice Emitted when a pool's tick spacing or protocol fee is updated event UpdatePool(bytes32 indexed poolId, uint8 tickSpacing, uint8 protocolFee); /// @notice Emitted when protocol fee is collected event CollectProtocol(address indexed recipient, address indexed token, uint256 amount); /// @notice Emitted when governance address is updated event GovernanceUpdated(address indexed governance); /// @notice Emitted when default parameters are updated event UpdateDefaultParameters(uint8 tickSpacing, uint8 protocolFee); /// @notice Emitted when liquidity is minted for a given position event Mint( bytes32 indexed poolId, address indexed owner, uint256 indexed positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, address sender, uint256 senderAccRefId, uint96 liquidityD8, uint256 amount0, uint256 amount1 ); /// @notice Emitted when a position's liquidity is removed and collected /// @param amount0 Token0 amount from the burned liquidity /// @param amount1 Token1 amount from the burned liquidity /// @param feeAmount0 Token0 fee collected from the position /// @param feeAmount0 Token1 fee collected from the position event Burn( bytes32 indexed poolId, address indexed owner, uint256 indexed positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, uint256 ownerAccRefId, uint96 liquidityD8, uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1 ); /// @notice Emitted when limit order settlement occurs during a swap /// @dev when tickEnd < tickStart, it means the tier crossed from a higher tick to a lower tick, and the settled /// limit orders were selling token1 for token0, vice versa. event Settle( bytes32 indexed poolId, uint8 indexed tierId, int24 indexed tickEnd, int24 tickStart, uint96 liquidityD8 ); /// @notice Emitted when a settled position's liquidity is collected event CollectSettled( bytes32 indexed poolId, address indexed owner, uint256 indexed positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, uint256 ownerAccRefId, uint96 liquidityD8, uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1 ); /// @notice Emitted when a position's limit order type is updated event SetLimitOrderType( bytes32 indexed poolId, address indexed owner, uint256 indexed positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, uint8 limitOrderType ); /// @notice Emitted for any swap happened in any pool /// @param amountInDistribution Percentages of input token amount routed to each tier. Each value occupies FLOOR(256/MAX_TIERS) /// bits and is a binary fixed-point with 1 integer bit and FLOOR(256/MAX_TIERS)-1 fraction bits. /// @param amountOutDistribution Percentages of output token amount routed to each tier. Same format as "amountInDistribution". /// @param tierData Array of tier's liquidity (0-127th bits) and sqrt price (128-255th bits) after the swap event Swap( bytes32 indexed poolId, address indexed sender, address indexed recipient, uint256 senderAccRefId, uint256 recipientAccRefId, int256 amount0, int256 amount1, uint256 amountInDistribution, uint256 amountOutDistribution, uint256[] tierData ); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface IMuffinHubActions { /// @notice Deposit token into recipient's account /// @dev DO NOT deposit rebasing tokens or multiple-address tokens as it will cause loss of funds. /// DO NOT withdraw the token you deposit or swap the token out from the contract during the callback. /// @param recipient Recipient's address /// @param recipientAccRefId Recipient's account id /// @param token Address of the token to deposit /// @param amount Token amount to deposit /// @param data Arbitrary data that is passed to callback function function deposit( address recipient, uint256 recipientAccRefId, address token, uint256 amount, bytes calldata data ) external; /// @notice Withdraw token from sender's account and send to recipient's address /// @param recipient Recipient's address /// @param senderAccRefId Id of sender's account, i.e. the account to withdraw token from /// @param token Address of the token to withdraw /// @param amount Token amount to withdraw function withdraw( address recipient, uint256 senderAccRefId, address token, uint256 amount ) external; /// @notice Create pool /// @dev DO NOT create pool with rebasing tokens or multiple-address tokens as it will cause loss of funds /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param sqrtGamma Sqrt (1 - percentage swap fee of the tier) (precision: 1e5) /// @param sqrtPrice Sqrt price of token0 denominated in token1 (UQ56.72) /// @param senderAccRefId Sender's account id, for paying the base liquidity /// @return poolId Pool id function createPool( address token0, address token1, uint24 sqrtGamma, uint128 sqrtPrice, uint256 senderAccRefId ) external returns (bytes32 poolId); /// @notice Add a new tier to a pool. Called by governanace only. /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param sqrtGamma Sqrt (1 - percentage swap fee) (precision: 1e5) /// @param senderAccRefId Sender's account id, for paying the base liquidity /// @return tierId Id of the new tier function addTier( address token0, address token1, uint24 sqrtGamma, uint256 senderAccRefId ) external returns (uint8 tierId); /// @notice Swap one token for another /// @param tokenIn Input token address /// @param tokenOut Output token address /// @param tierChoices Bitmap to select which tiers are allowed to swap /// @param amountDesired Desired swap amount (positive: input, negative: output) /// @param recipient Recipient's address /// @param recipientAccRefId Recipient's account id /// @param senderAccRefId Sender's account id /// @param data Arbitrary data that is passed to callback function /// @return amountIn Input token amount /// @return amountOut Output token amount function swap( address tokenIn, address tokenOut, uint256 tierChoices, int256 amountDesired, address recipient, uint256 recipientAccRefId, uint256 senderAccRefId, bytes calldata data ) external returns (uint256 amountIn, uint256 amountOut); /// @notice Parameters for the multi-hop swap function /// @param path Multi-hop path. encodePacked(address tokenA, uint16 tierChoices, address tokenB, uint16 tierChoices ...) /// @param amountDesired Desired swap amount (positive: input, negative: output) /// @param recipient Recipient's address /// @param recipientAccRefId Recipient's account id /// @param senderAccRefId Sender's account id /// @param data Arbitrary data that is passed to callback function struct SwapMultiHopParams { bytes path; int256 amountDesired; address recipient; uint256 recipientAccRefId; uint256 senderAccRefId; bytes data; } /// @notice Swap one token for another along the specified path /// @param params SwapMultiHopParams struct /// @return amountIn Input token amount /// @return amountOut Output token amount function swapMultiHop(SwapMultiHopParams calldata params) external returns (uint256 amountIn, uint256 amountOut); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "../../libraries/Tiers.sol"; import "../../libraries/Ticks.sol"; import "../../libraries/Positions.sol"; interface IMuffinHubView { /// @notice Return whether the given fee rate is allowed in the given pool /// @param poolId Pool id /// @param sqrtGamma Fee rate, expressed in sqrt(1 - %fee) (precision: 1e5) /// @return allowed True if the % fee is allowed function isSqrtGammaAllowed(bytes32 poolId, uint24 sqrtGamma) external view returns (bool allowed); /// @notice Return pool's default tick spacing and protocol fee /// @return tickSpacing Default tick spacing applied to new pools. Note that there is also pool-specific default /// tick spacing which overrides the global default if set. /// @return protocolFee Default protocol fee applied to new pools function getDefaultParameters() external view returns (uint8 tickSpacing, uint8 protocolFee); /// @notice Return the pool's tick spacing and protocol fee /// @return tickSpacing Pool's tick spacing /// @return protocolFee Pool's protocol fee function getPoolParameters(bytes32 poolId) external view returns (uint8 tickSpacing, uint8 protocolFee); /// @notice Return a tier state function getTier(bytes32 poolId, uint8 tierId) external view returns (Tiers.Tier memory tier); /// @notice Return the number of existing tiers in the given pool function getTiersCount(bytes32 poolId) external view returns (uint256 count); /// @notice Return a tick state function getTick( bytes32 poolId, uint8 tierId, int24 tick ) external view returns (Ticks.Tick memory tickObj); /// @notice Return a position state. /// @param poolId Pool id /// @param owner Address of the position owner /// @param positionRefId Reference id for the position set by the owner /// @param tierId Tier index /// @param tickLower Lower tick boundary of the position /// @param tickUpper Upper tick boundary of the position /// @param position Position struct function getPosition( bytes32 poolId, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper ) external view returns (Positions.Position memory position); /// @notice Return the value of a slot in MuffinHub contract function getStorageAt(bytes32 slot) external view returns (bytes32 word); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; library Tiers { struct Tier { uint128 liquidity; uint128 sqrtPrice; // UQ56.72 uint24 sqrtGamma; // 5 decimal places int24 tick; int24 nextTickBelow; // the next lower tick to cross (note that it can be equal to `tier.tick`) int24 nextTickAbove; // the next upper tick to cross uint80 feeGrowthGlobal0; // UQ16.64 uint80 feeGrowthGlobal1; // UQ16.64 } /// @dev Update tier's next tick if the given tick is more adjacent to the current tick function updateNextTick(Tier storage self, int24 tickNew) internal { if (tickNew <= self.tick) { if (tickNew > self.nextTickBelow) self.nextTickBelow = tickNew; } else { if (tickNew < self.nextTickAbove) self.nextTickAbove = tickNew; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; library Ticks { /** * @param liquidityLowerD8 Liquidity from positions with lower tick boundary at this tick * @param liquidityUpperD8 Liquidity from positions with upper tick boundary at this tick * @param nextBelow Next initialized tick below this tick * @param nextAbove Next initialized tick above this tick * @param needSettle0 True if needed to settle positions with lower tick boundary at this tick (i.e. 1 -> 0 limit orders) * @param needSettle1 True if needed to settle positions with upper tick boundary at this tick (i.e. 0 -> 1 limit orders) * @param feeGrowthOutside0 Fee0 growth per unit liquidity from this tick to the end in a direction away from the tier's current tick (UQ16.64) * @param feeGrowthOutside1 Fee1 growth per unit liquidity from this tick to the end in a direction away from the tier's current tick (UQ16.64) */ struct Tick { uint96 liquidityLowerD8; uint96 liquidityUpperD8; int24 nextBelow; int24 nextAbove; bool needSettle0; bool needSettle1; uint80 feeGrowthOutside0; uint80 feeGrowthOutside1; } /// @dev Flip the direction of "outside". Called when the tick is being crossed. function flip( Tick storage self, uint80 feeGrowthGlobal0, uint80 feeGrowthGlobal1 ) internal { unchecked { self.feeGrowthOutside0 = feeGrowthGlobal0 - self.feeGrowthOutside0; self.feeGrowthOutside1 = feeGrowthGlobal1 - self.feeGrowthOutside1; } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "../IMuffinHubBase.sol"; import "../IMuffinHubEvents.sol"; import "./IMuffinHubPositionsActions.sol"; import "./IMuffinHubPositionsView.sol"; interface IMuffinHubPositions is IMuffinHubBase, IMuffinHubEvents, IMuffinHubPositionsActions, IMuffinHubPositionsView {}
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "./Math.sol"; import "./UnsafeMath.sol"; import "./FullMath.sol"; library PoolMath { using Math for uint256; uint256 private constant Q72 = 0x1000000000000000000; uint256 private constant Q184 = 0x10000000000000000000000000000000000000000000000; // ----- sqrt price <> token amounts ----- /// @dev Calculate amount0 delta when price moves from sqrtP0 to sqrtP1. /// i.e. Δx = L (√P0 - √P1) / (√P0 √P1) /// /// @dev Rounding rules: /// if sqrtP0 > sqrtP1 (price goes down): => amt0 is input => round away from zero /// if sqrtP0 < sqrtP1 (price goes up): => amt0 is output => round towards zero function calcAmt0FromSqrtP( uint128 sqrtP0, uint128 sqrtP1, uint128 liquidity ) internal pure returns (int256 amt0) { unchecked { bool priceUp = sqrtP1 > sqrtP0; if (priceUp) (sqrtP0, sqrtP1) = (sqrtP1, sqrtP0); uint256 num = uint256(liquidity) * (sqrtP0 - sqrtP1); uint256 denom = uint256(sqrtP0) * sqrtP1; amt0 = Math.toInt256( num < Q184 ? (priceUp ? (num << 72) / denom : UnsafeMath.ceilDiv(num << 72, denom)) : (priceUp ? FullMath.mulDiv(num, Q72, denom) : FullMath.mulDivRoundingUp(num, Q72, denom)) ); if (priceUp) amt0 *= -1; } } /// @dev Calculate amount1 delta when price moves from sqrtP0 to sqrtP1. /// i.e. Δy = L (√P0 - √P1) /// /// @dev Rounding rules: /// if sqrtP0 > sqrtP1 (price goes down): => amt1 is output => round towards zero /// if sqrtP0 < sqrtP1 (price goes up): => amt1 is input => round away from zero function calcAmt1FromSqrtP( uint128 sqrtP0, uint128 sqrtP1, uint128 liquidity ) internal pure returns (int256 amt1) { unchecked { bool priceDown = sqrtP1 < sqrtP0; if (priceDown) (sqrtP0, sqrtP1) = (sqrtP1, sqrtP0); uint256 num = uint256(liquidity) * (sqrtP1 - sqrtP0); amt1 = (priceDown ? num >> 72 : UnsafeMath.ceilDiv(num, Q72)).toInt256(); if (priceDown) amt1 *= -1; } } /// @dev Calculate the new sqrt price after an amount0 delta. /// i.e. √P1 = L √P0 / (L + Δx * √P0) if no overflow /// = L / (L/√P0 + Δx) otherwise /// /// @dev Rounding rules: /// if amt0 in: price goes down => sqrtP1 rounded up for less price change for less amt1 out /// if amt0 out: price goes up => sqrtP1 rounded up for more price change for more amt1 in /// therefore: sqrtP1 always rounded up function calcSqrtPFromAmt0( uint128 sqrtP0, uint128 liquidity, int256 amt0 ) internal pure returns (uint128 sqrtP1) { unchecked { if (amt0 == 0) return sqrtP0; uint256 absAmt0 = uint256(amt0 < 0 ? -amt0 : amt0); uint256 product = absAmt0 * sqrtP0; uint256 liquidityX72 = uint256(liquidity) << 72; uint256 denom; if (amt0 > 0) { if ((product / absAmt0 == sqrtP0) && ((denom = liquidityX72 + product) >= liquidityX72)) { // if product and denom don't overflow: uint256 num = uint256(liquidity) * sqrtP0; sqrtP1 = num < Q184 ? uint128(UnsafeMath.ceilDiv(num << 72, denom)) // denom > 0 : uint128(FullMath.mulDivRoundingUp(num, Q72, denom)); } else { // if either one overflows: sqrtP1 = uint128(UnsafeMath.ceilDiv(liquidityX72, (liquidityX72 / sqrtP0).add(absAmt0))); // absAmt0 > 0 } } else { // ensure product doesn't overflow and denom doesn't underflow require(product / absAmt0 == sqrtP0); require((denom = liquidityX72 - product) <= liquidityX72); require(denom != 0); uint256 num = uint256(liquidity) * sqrtP0; sqrtP1 = num < Q184 ? UnsafeMath.ceilDiv(num << 72, denom).toUint128() : FullMath.mulDivRoundingUp(num, Q72, denom).toUint128(); } } } /// @dev Calculate the new sqrt price after an amount1 delta. /// i.e. √P1 = √P0 + (Δy / L) /// /// @dev Rounding rules: /// if amt1 in: price goes up => sqrtP1 rounded down for less price delta for less amt0 out /// if amt1 out: price goes down => sqrtP1 rounded down for more price delta for more amt0 in /// therefore: sqrtP1 always rounded down function calcSqrtPFromAmt1( uint128 sqrtP0, uint128 liquidity, int256 amt1 ) internal pure returns (uint128 sqrtP1) { unchecked { if (amt1 < 0) { // price moves down require(liquidity != 0); uint256 absAmt1 = uint256(-amt1); uint256 absAmt1DivL = absAmt1 < Q184 ? UnsafeMath.ceilDiv(absAmt1 * Q72, liquidity) : FullMath.mulDivRoundingUp(absAmt1, Q72, liquidity); sqrtP1 = uint256(sqrtP0).sub(absAmt1DivL).toUint128(); } else { // price moves up uint256 amt1DivL = uint256(amt1) < Q184 ? (uint256(amt1) * Q72) / liquidity : FullMath.mulDiv(uint256(amt1), Q72, liquidity); sqrtP1 = uint256(sqrtP0).add(amt1DivL).toUint128(); } } } // ----- liquidity <> token amounts ----- /// @dev Calculate the amount{0,1} needed for the given liquidity change function calcAmtsForLiquidity( uint128 sqrtP, uint128 sqrtPLower, uint128 sqrtPUpper, int96 liquidityDeltaD8 ) internal pure returns (uint256 amt0, uint256 amt1) { // we assume {sqrtP, sqrtPLower, sqrtPUpper} ≠ 0 and sqrtPLower < sqrtPUpper unchecked { // find the sqrt price at which liquidity is add/removed sqrtP = (sqrtP < sqrtPLower) ? sqrtPLower : (sqrtP > sqrtPUpper) ? sqrtPUpper : sqrtP; // calc amt{0,1} for the change of liquidity uint128 absL = uint128(uint96(liquidityDeltaD8 >= 0 ? liquidityDeltaD8 : -liquidityDeltaD8)) << 8; if (liquidityDeltaD8 >= 0) { // round up amt0 = uint256(calcAmt0FromSqrtP(sqrtPUpper, sqrtP, absL)); amt1 = uint256(calcAmt1FromSqrtP(sqrtPLower, sqrtP, absL)); } else { // round down amt0 = uint256(-calcAmt0FromSqrtP(sqrtP, sqrtPUpper, absL)); amt1 = uint256(-calcAmt1FromSqrtP(sqrtP, sqrtPLower, absL)); } } } /// @dev Calculate the max liquidity received if adding given token amounts to the tier. function calcLiquidityForAmts( uint128 sqrtP, uint128 sqrtPLower, uint128 sqrtPUpper, uint256 amt0, uint256 amt1 ) internal pure returns (uint96 liquidityD8) { // we assume {sqrtP, sqrtPLower, sqrtPUpper} ≠ 0 and sqrtPLower < sqrtPUpper unchecked { uint256 liquidity; if (sqrtP <= sqrtPLower) { // L = Δx (√P0 √P1) / (√P0 - √P1) liquidity = FullMath.mulDiv(amt0, uint256(sqrtPLower) * sqrtPUpper, (sqrtPUpper - sqrtPLower) * Q72); } else if (sqrtP >= sqrtPUpper) { // L = Δy / (√P0 - √P1) liquidity = FullMath.mulDiv(amt1, Q72, sqrtPUpper - sqrtPLower); } else { uint256 liquidity0 = FullMath.mulDiv(amt0, uint256(sqrtP) * sqrtPUpper, (sqrtPUpper - sqrtP) * Q72); uint256 liquidity1 = FullMath.mulDiv(amt1, Q72, sqrtP - sqrtPLower); liquidity = (liquidity0 < liquidity1 ? liquidity0 : liquidity1); } liquidityD8 = (liquidity >> 8).toUint96(); } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; library TickMath { uint256 private constant Q56 = 0x100000000000000; uint256 private constant Q128 = 0x100000000000000000000000000000000; /// @dev Minimum tick supported in this protocol int24 internal constant MIN_TICK = -776363; /// @dev Maximum tick supported in this protocol int24 internal constant MAX_TICK = 776363; /// @dev Minimum sqrt price, i.e. tickToSqrtPrice(MIN_TICK) uint128 internal constant MIN_SQRT_P = 65539; /// @dev Maximum sqrt price, i.e. tickToSqrtPrice(MAX_TICK) uint128 internal constant MAX_SQRT_P = 340271175397327323250730767849398346765; /** * @dev Find sqrtP = u^tick, where u = sqrt(1.0001) * * Let b_i = the i-th bit of x and b_i ∈ {0, 1} * Then x = (b0 * 2^0) + (b1 * 2^1) + (b2 * 2^2) + ... * Thus, r = u^x * = u^(b0 * 2^0) * u^(b1 * 2^1) * u^(b2 * 2^2) * ... * = k0^b0 * k1^b1 * k2^b2 * ... (where k_i = u^(2^i)) * We pre-compute k_i since u is a known constant. In practice, we use u = 1/sqrt(1.0001) to * prevent overflow during the computation, then inverse the result at the end. */ function tickToSqrtPrice(int24 tick) internal pure returns (uint128 sqrtP) { unchecked { require(MIN_TICK <= tick && tick <= MAX_TICK); uint256 x = uint256(uint24(tick < 0 ? -tick : tick)); // abs(tick) uint256 r = Q128; // UQ128.128 if (x & 0x1 > 0) r = (r * 0xFFFCB933BD6FAD37AA2D162D1A594001) >> 128; if (x & 0x2 > 0) r = (r * 0xFFF97272373D413259A46990580E213A) >> 128; if (x & 0x4 > 0) r = (r * 0xFFF2E50F5F656932EF12357CF3C7FDCC) >> 128; if (x & 0x8 > 0) r = (r * 0xFFE5CACA7E10E4E61C3624EAA0941CD0) >> 128; if (x & 0x10 > 0) r = (r * 0xFFCB9843D60F6159C9DB58835C926644) >> 128; if (x & 0x20 > 0) r = (r * 0xFF973B41FA98C081472E6896DFB254C0) >> 128; if (x & 0x40 > 0) r = (r * 0xFF2EA16466C96A3843EC78B326B52861) >> 128; if (x & 0x80 > 0) r = (r * 0xFE5DEE046A99A2A811C461F1969C3053) >> 128; if (x & 0x100 > 0) r = (r * 0xFCBE86C7900A88AEDCFFC83B479AA3A4) >> 128; if (x & 0x200 > 0) r = (r * 0xF987A7253AC413176F2B074CF7815E54) >> 128; if (x & 0x400 > 0) r = (r * 0xF3392B0822B70005940C7A398E4B70F3) >> 128; if (x & 0x800 > 0) r = (r * 0xE7159475A2C29B7443B29C7FA6E889D9) >> 128; if (x & 0x1000 > 0) r = (r * 0xD097F3BDFD2022B8845AD8F792AA5825) >> 128; if (x & 0x2000 > 0) r = (r * 0xA9F746462D870FDF8A65DC1F90E061E5) >> 128; if (x & 0x4000 > 0) r = (r * 0x70D869A156D2A1B890BB3DF62BAF32F7) >> 128; if (x & 0x8000 > 0) r = (r * 0x31BE135F97D08FD981231505542FCFA6) >> 128; if (x & 0x10000 > 0) r = (r * 0x9AA508B5B7A84E1C677DE54F3E99BC9) >> 128; if (x & 0x20000 > 0) r = (r * 0x5D6AF8DEDB81196699C329225EE604) >> 128; if (x & 0x40000 > 0) r = (r * 0x2216E584F5FA1EA926041BEDFE98) >> 128; if (x & 0x80000 > 0) r = (r * 0x48A170391F7DC42444E8FA2) >> 128; // Stop computation here since abs(tick) < 2**20 (i.e. 776363 < 1048576) // Inverse r since base = 1/sqrt(1.0001) if (tick >= 0) r = type(uint256).max / r; // Downcast to UQ56.72 and round up sqrtP = uint128((r >> 56) + (r % Q56 > 0 ? 1 : 0)); } } /// @dev Find tick = floor(log_u(sqrtP)), where u = sqrt(1.0001) function sqrtPriceToTick(uint128 sqrtP) internal pure returns (int24 tick) { unchecked { require(MIN_SQRT_P <= sqrtP && sqrtP <= MAX_SQRT_P); uint256 x = uint256(sqrtP); // Find msb of sqrtP (since sqrtP < 2^128, we start the check at 2**64) uint256 xc = x; uint256 msb; if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) { xc >>= 1; msb += 1; } // Calculate integer part of log2(x), can be negative int256 r = (int256(msb) - 72) << 64; // Q64.64 // Scale up x to make it 127-bit uint256 z = x << (127 - msb); // Do the following to find the decimal part of log2(x) (i.e. from 63th bit downwards): // 1. sqaure z // 2. if z becomes 128 bit: // 3. half z // 4. set this bit to 1 // And stop at 46th bit since we have enough decimal places to continue to next steps z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x8000000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x4000000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x2000000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x1000000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x800000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x400000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x200000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x100000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x80000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x40000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x20000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x10000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x8000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x4000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x2000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x1000000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x800000000000; } z = (z * z) >> 127; if (z >= Q128) { z >>= 1; r |= 0x400000000000; } // Change the base of log2(x) to sqrt(1.0001). (i.e. log_u(x) = log2(u) * log_u(2)) r *= 255738958999603826347141; // Add both the maximum positive and negative errors to r to see if it diverges into two different ticks. // If it does, calculate the upper tick's sqrtP and compare with the given sqrtP. int24 tickUpper = int24((r + 17996007701288367970265332090599899137) >> 128); int24 tickLower = int24( r < -230154402537746701963478439606373042805014528 ? (r - 98577143636729737466164032634120830977) >> 128 : r < -162097929153559009270803518120019400513814528 ? (r - 527810000259722480933883300202676225) >> 128 : r >> 128 ); tick = (tickUpper == tickLower || sqrtP >= tickToSqrtPrice(tickUpper)) ? tickUpper : tickLower; } } struct Cache { int24 tick; uint128 sqrtP; } /// @dev memoize last tick-to-sqrtP conversion function tickToSqrtPriceMemoized(Cache memory cache, int24 tick) internal pure returns (uint128 sqrtP) { if (tick == cache.tick) sqrtP = cache.sqrtP; else { sqrtP = tickToSqrtPrice(tick); cache.sqrtP = sqrtP; cache.tick = tick; } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; library UnsafeMath { /// @dev Division by 0 has unspecified behavior, and must be checked externally. function ceilDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { z := add(div(x, y), gt(mod(x, y), 0)) } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "./math/TickMath.sol"; import "./math/SwapMath.sol"; import "./math/UnsafeMath.sol"; import "./math/Math.sol"; import "./Tiers.sol"; import "./Ticks.sol"; import "./TickMaps.sol"; import "./Positions.sol"; import "./Settlement.sol"; library Pools { using Math for uint96; using Math for uint128; using Tiers for Tiers.Tier; using Ticks for Ticks.Tick; using TickMaps for TickMaps.TickMap; using Positions for Positions.Position; error InvalidAmount(); error InvalidTierChoices(); error InvalidTick(); error InvalidTickRangeForLimitOrder(); error NoLiquidityForLimitOrder(); error PositionAlreadySettled(); error PositionNotSettled(); uint24 internal constant MAX_SQRT_GAMMA = 100_000; uint96 internal constant BASE_LIQUIDITY_D8 = 100; // tier's base liquidity, scaled down 2^8. User pays it when adding a tier int256 internal constant SWAP_AMOUNT_TOLERANCE = 100; // tolerance between desired and actual swap amounts uint256 internal constant AMOUNT_DISTRIBUTION_BITS = 256 / MAX_TIERS; // i.e. 42 if MAX_TIERS is 6 uint256 internal constant AMOUNT_DISTRIBUTION_RESOLUTION = AMOUNT_DISTRIBUTION_BITS - 1; /// @param unlocked Reentrancy lock /// @param tickSpacing Tick spacing. Only ticks that are multiples of the tick spacing can be used /// @param protocolFee Protocol fee with base 255 (e.g. protocolFee = 51 for 20% protocol fee) /// @param tiers Array of tiers /// @param tickMaps Bitmap for each tier to store which ticks are initializated /// @param ticks Mapping of tick states of each tier /// @param settlements Mapping of settlements for token{0,1} singled-sided positions /// @param positions Mapping of position states /// @param limitOrderTickSpacingMultipliers Tick spacing of limit order for each tier, as multiples of the pool's tick spacing struct Pool { bool unlocked; uint8 tickSpacing; uint8 protocolFee; Tiers.Tier[] tiers; mapping(uint256 => TickMaps.TickMap) tickMaps; mapping(uint256 => mapping(int24 => Ticks.Tick)) ticks; mapping(uint256 => mapping(int24 => Settlement.Info[2])) settlements; mapping(bytes32 => Positions.Position) positions; uint8[MAX_TIERS] limitOrderTickSpacingMultipliers; } function lock(Pool storage pool) internal { require(pool.unlocked); pool.unlocked = false; } function unlock(Pool storage pool) internal { pool.unlocked = true; } function getPoolAndId( mapping(bytes32 => Pool) storage pools, address token0, address token1 ) internal view returns (Pool storage pool, bytes32 poolId) { poolId = keccak256(abi.encode(token0, token1)); pool = pools[poolId]; } /*=============================================================== * INITIALIZATION *==============================================================*/ function initialize( Pool storage pool, uint24 sqrtGamma, uint128 sqrtPrice, uint8 tickSpacing, uint8 protocolFee ) internal returns (uint256 amount0, uint256 amount1) { require(pool.tickSpacing == 0); // ensure not initialized require(TickMath.MIN_SQRT_P <= sqrtPrice && sqrtPrice <= TickMath.MAX_SQRT_P); require(tickSpacing > 0); pool.tickSpacing = tickSpacing; pool.protocolFee = protocolFee; (amount0, amount1) = _addTier(pool, sqrtGamma, sqrtPrice); // default enable limit order on first tier pool.limitOrderTickSpacingMultipliers[0] = 1; // BE AWARE the pool is locked. Please unlock it after token transfer is done. } function addTier(Pool storage pool, uint24 sqrtGamma) internal returns ( uint256 amount0, uint256 amount1, uint8 tierId ) { lock(pool); require((tierId = uint8(pool.tiers.length)) > 0); (amount0, amount1) = _addTier(pool, sqrtGamma, pool.tiers[0].sqrtPrice); // use 1st tier sqrt price as reference // BE AWARE the pool is locked. Please unlock it after token transfer is done. } function _addTier( Pool storage pool, uint24 sqrtGamma, uint128 sqrtPrice ) internal returns (uint256 amount0, uint256 amount1) { uint256 tierId = pool.tiers.length; require(tierId < MAX_TIERS); require(sqrtGamma <= MAX_SQRT_GAMMA); // initialize tier Tiers.Tier memory tier = Tiers.Tier({ liquidity: uint128(BASE_LIQUIDITY_D8) << 8, sqrtPrice: sqrtPrice, sqrtGamma: sqrtGamma, tick: TickMath.sqrtPriceToTick(sqrtPrice), nextTickBelow: TickMath.MIN_TICK, nextTickAbove: TickMath.MAX_TICK, feeGrowthGlobal0: 0, feeGrowthGlobal1: 0 }); if (sqrtPrice == TickMath.MAX_SQRT_P) tier.tick--; // max tick is never crossed pool.tiers.push(tier); // initialize min tick & max tick Ticks.Tick storage lower = pool.ticks[tierId][TickMath.MIN_TICK]; Ticks.Tick storage upper = pool.ticks[tierId][TickMath.MAX_TICK]; (lower.liquidityLowerD8, lower.nextBelow, lower.nextAbove) = ( BASE_LIQUIDITY_D8, TickMath.MIN_TICK, TickMath.MAX_TICK ); (upper.liquidityUpperD8, upper.nextBelow, upper.nextAbove) = ( BASE_LIQUIDITY_D8, TickMath.MIN_TICK, TickMath.MAX_TICK ); // initialize tick map pool.tickMaps[tierId].set(TickMath.MIN_TICK); pool.tickMaps[tierId].set(TickMath.MAX_TICK); // calculate tokens to take for full-range base liquidity amount0 = UnsafeMath.ceilDiv(uint256(BASE_LIQUIDITY_D8) << (72 + 8), sqrtPrice); amount1 = UnsafeMath.ceilDiv(uint256(BASE_LIQUIDITY_D8) * sqrtPrice, 1 << (72 - 8)); } /*=============================================================== * SETTINGS *==============================================================*/ function setPoolParameters( Pool storage pool, uint8 tickSpacing, uint8 protocolFee ) internal { require(pool.unlocked); require(tickSpacing > 0); pool.tickSpacing = tickSpacing; pool.protocolFee = protocolFee; } function setTierParameters( Pool storage pool, uint8 tierId, uint24 sqrtGamma, uint8 limitOrderTickSpacingMultiplier ) internal { require(pool.unlocked); require(tierId < pool.tiers.length); require(sqrtGamma <= MAX_SQRT_GAMMA); pool.tiers[tierId].sqrtGamma = sqrtGamma; pool.limitOrderTickSpacingMultipliers[tierId] = limitOrderTickSpacingMultiplier; } /*=============================================================== * SWAP *==============================================================*/ uint256 private constant Q64 = 0x10000000000000000; uint256 private constant Q128 = 0x100000000000000000000000000000000; /// @notice Emitted when limit order settlement occurs during a swap /// @dev Normally, we emit events from hub contract instead of from this pool library, but bubbling up the event /// data back to hub contract comsumes gas significantly, therefore we simply emit the "settle" event here. event Settle( bytes32 indexed poolId, uint8 indexed tierId, int24 indexed tickEnd, int24 tickStart, uint96 liquidityD8 ); struct SwapCache { bool zeroForOne; bool exactIn; uint8 protocolFee; uint256 protocolFeeAmt; uint256 tierChoices; TickMath.Cache tmCache; int256[MAX_TIERS] amounts; bytes32 poolId; } struct TierState { uint128 sqrtPTick; uint256 amountIn; uint256 amountOut; bool crossed; } /// @dev Struct returned by the "swap" function /// @param amount0 Pool's token0 balance change /// @param amount1 Pool's token1 balance change /// @param amountInDistribution Percentages of input amount routed to each tier (for logging) /// @param tierData Array of tier's liquidity and sqrt price after the swap (for logging) /// @param protocolFeeAmt Amount of input token as protocol fee struct SwapResult { int256 amount0; int256 amount1; uint256 amountInDistribution; uint256 amountOutDistribution; uint256[] tierData; uint256 protocolFeeAmt; } /// @notice Perform a swap in the pool /// @param pool Pool storage pointer /// @param isToken0 True if amtDesired refers to token0 /// @param amtDesired Desired swap amount (positive: exact input, negative: exact output) /// @param tierChoices Bitmap to allow which tiers to swap /// @param poolId Pool id, only used for emitting settle event. Can pass in zero to skip emitting event /// @return result Swap result function swap( Pool storage pool, bool isToken0, int256 amtDesired, uint256 tierChoices, bytes32 poolId // only used for `Settle` event ) internal returns (SwapResult memory result) { lock(pool); Tiers.Tier[] memory tiers; TierState[MAX_TIERS] memory states; unchecked { // truncate tierChoices uint256 tiersCount = pool.tiers.length; uint256 maxTierChoices = (1 << tiersCount) - 1; tierChoices &= maxTierChoices; if (amtDesired == 0 || amtDesired == SwapMath.REJECTED) revert InvalidAmount(); if (tierChoices == 0) revert InvalidTierChoices(); // only load tiers that are allowed by users if (tierChoices == maxTierChoices) { tiers = pool.tiers; } else { tiers = new Tiers.Tier[](tiersCount); for (uint256 i; i < tiers.length; i++) { if (tierChoices & (1 << i) != 0) tiers[i] = pool.tiers[i]; } } } SwapCache memory cache = SwapCache({ zeroForOne: isToken0 == (amtDesired > 0), exactIn: amtDesired > 0, protocolFee: pool.protocolFee, protocolFeeAmt: 0, tierChoices: tierChoices, tmCache: TickMath.Cache({tick: type(int24).max, sqrtP: 0}), amounts: _emptyInt256Array(), poolId: poolId }); int256 initialAmtDesired = amtDesired; int256 amountA; // pool's balance change of the token which "amtDesired" refers to int256 amountB; // pool's balance change of the opposite token while (true) { // calculate the swap amount for each tier cache.amounts = cache.exactIn ? SwapMath.calcTierAmtsIn(tiers, isToken0, amtDesired, cache.tierChoices) : SwapMath.calcTierAmtsOut(tiers, isToken0, amtDesired, cache.tierChoices); // compute the swap for each tier for (uint256 i; i < tiers.length; ) { (int256 amtAStep, int256 amtBStep) = _swapStep(pool, isToken0, cache, states[i], tiers[i], i); amountA += amtAStep; amountB += amtBStep; unchecked { i++; } } // check if we meet the stopping criteria amtDesired = initialAmtDesired - amountA; unchecked { if ( (cache.exactIn ? amtDesired <= SWAP_AMOUNT_TOLERANCE : amtDesired >= -SWAP_AMOUNT_TOLERANCE) || cache.tierChoices == 0 ) break; } } result.protocolFeeAmt = cache.protocolFeeAmt; unchecked { (result.amountInDistribution, result.amountOutDistribution, result.tierData) = _updateTiers( pool, states, tiers, uint256(cache.exactIn ? amountA : amountB), uint256(cache.exactIn ? -amountB : -amountA) ); } (result.amount0, result.amount1) = isToken0 ? (amountA, amountB) : (amountB, amountA); // BE AWARE the pool is locked. Please unlock it after token transfer is done. } function _swapStep( Pool storage pool, bool isToken0, SwapCache memory cache, TierState memory state, Tiers.Tier memory tier, uint256 tierId ) internal returns (int256 amtAStep, int256 amtBStep) { if (cache.amounts[tierId] == SwapMath.REJECTED) return (0, 0); // calculate sqrt price of the next tick if (state.sqrtPTick == 0) state.sqrtPTick = TickMath.tickToSqrtPriceMemoized( cache.tmCache, cache.zeroForOne ? tier.nextTickBelow : tier.nextTickAbove ); unchecked { // calculate input & output amts, new sqrt price, and fee amt for this swap step uint256 feeAmtStep; (amtAStep, amtBStep, tier.sqrtPrice, feeAmtStep) = SwapMath.computeStep( isToken0, cache.exactIn, cache.amounts[tierId], tier.sqrtPrice, state.sqrtPTick, tier.liquidity, tier.sqrtGamma ); if (amtAStep == SwapMath.REJECTED) return (0, 0); // cache input & output amounts for later event logging (locally) if (cache.exactIn) { state.amountIn += uint256(amtAStep); state.amountOut += uint256(-amtBStep); } else { state.amountIn += uint256(amtBStep); state.amountOut += uint256(-amtAStep); } // update protocol fee amt (locally) uint256 protocolFeeAmt = (feeAmtStep * cache.protocolFee) / type(uint8).max; cache.protocolFeeAmt += protocolFeeAmt; feeAmtStep -= protocolFeeAmt; // update fee growth (locally) (realistically assume feeAmtStep < 2**192) uint80 feeGrowth = uint80((feeAmtStep << 64) / tier.liquidity); if (cache.zeroForOne) { tier.feeGrowthGlobal0 += feeGrowth; } else { tier.feeGrowthGlobal1 += feeGrowth; } } // handle cross tick, which updates a tick state if (tier.sqrtPrice == state.sqrtPTick) { int24 tickCross = cache.zeroForOne ? tier.nextTickBelow : tier.nextTickAbove; // skip crossing tick if reaches the end of the supported price range if (tickCross == TickMath.MIN_TICK || tickCross == TickMath.MAX_TICK) { cache.tierChoices &= ~(1 << tierId); return (amtAStep, amtBStep); } // clear cached tick price, so as to calculate a new one in next loop state.sqrtPTick = 0; state.crossed = true; // flip the direction of tick's data (effect) Ticks.Tick storage cross = pool.ticks[tierId][tickCross]; cross.flip(tier.feeGrowthGlobal0, tier.feeGrowthGlobal1); unchecked { // update tier's liquidity and next ticks (locally) (uint128 liqLowerD8, uint128 liqUpperD8) = (cross.liquidityLowerD8, cross.liquidityUpperD8); if (cache.zeroForOne) { tier.liquidity = tier.liquidity + (liqUpperD8 << 8) - (liqLowerD8 << 8); tier.nextTickBelow = cross.nextBelow; tier.nextTickAbove = tickCross; } else { tier.liquidity = tier.liquidity + (liqLowerD8 << 8) - (liqUpperD8 << 8); tier.nextTickBelow = tickCross; tier.nextTickAbove = cross.nextAbove; } } // settle single-sided positions (i.e. filled limit orders) if neccessary if (cache.zeroForOne ? cross.needSettle0 : cross.needSettle1) { (int24 tickStart, uint96 liquidityD8Settled) = Settlement.settle( pool.settlements[tierId], pool.ticks[tierId], pool.tickMaps[tierId], tier, tickCross, cache.zeroForOne ); if (cache.poolId != 0) { emit Settle(cache.poolId, uint8(tierId), tickCross, tickStart, liquidityD8Settled); } } } } /// @dev Apply the post-swap data changes from memory to storage, also prepare data for event logging function _updateTiers( Pool storage pool, TierState[MAX_TIERS] memory states, Tiers.Tier[] memory tiers, uint256 amtIn, uint256 amtOut ) internal returns ( uint256 amtInDistribution, uint256 amtOutDistribution, uint256[] memory tierData ) { tierData = new uint256[](tiers.length); unchecked { bool amtInNoOverflow = amtIn < (1 << (256 - AMOUNT_DISTRIBUTION_RESOLUTION)); bool amtOutNoOverflow = amtOut < (1 << (256 - AMOUNT_DISTRIBUTION_RESOLUTION)); for (uint256 i; i < tiers.length; i++) { TierState memory state = states[i]; // we can safely assume tier data is unchanged when there's zero input amount and no crossing tick, // since we would have rejected the tier if such case happened. if (state.amountIn > 0 || state.crossed) { Tiers.Tier memory tier = tiers[i]; // calculate current tick: // if tier's price is equal to tick's price (let say the tick is T), the tier is expected to be in // the upper tick space [T, T+1]. Only if the tier's next upper crossing tick is T, the tier is in // the lower tick space [T-1, T]. tier.tick = TickMath.sqrtPriceToTick(tier.sqrtPrice); if (tier.tick == tier.nextTickAbove) tier.tick--; pool.tiers[i] = tier; // prepare data for logging tierData[i] = (uint256(tier.sqrtPrice) << 128) | tier.liquidity; if (amtIn > 0) { amtInDistribution |= ( amtInNoOverflow ? (state.amountIn << AMOUNT_DISTRIBUTION_RESOLUTION) / amtIn : state.amountIn / ((amtIn >> AMOUNT_DISTRIBUTION_RESOLUTION) + 1) ) << (i * AMOUNT_DISTRIBUTION_BITS); // prettier-ignore } if (amtOut > 0) { amtOutDistribution |= ( amtOutNoOverflow ? (state.amountOut << AMOUNT_DISTRIBUTION_RESOLUTION) / amtOut : state.amountOut / ((amtOut >> AMOUNT_DISTRIBUTION_RESOLUTION) + 1) ) << (i * AMOUNT_DISTRIBUTION_BITS); // prettier-ignore } } } } } function _emptyInt256Array() internal pure returns (int256[MAX_TIERS] memory) {} /*=============================================================== * UPDATE LIQUIDITY *==============================================================*/ function _checkTickInputs(int24 tickLower, int24 tickUpper) internal pure { if (tickLower >= tickUpper || TickMath.MIN_TICK > tickLower || tickUpper > TickMath.MAX_TICK) { revert InvalidTick(); } } /// @notice Update a position's liquidity /// @param owner Address of the position owner /// @param positionRefId Reference id of the position /// @param tierId Tier index of the position /// @param tickLower Lower tick boundary of the position /// @param tickUpper Upper tick boundary of the position /// @param liquidityDeltaD8 Amount of liquidity change, divided by 2^8 /// @param collectAllFees True to collect all remaining accrued fees of the position function updateLiquidity( Pool storage pool, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, int96 liquidityDeltaD8, bool collectAllFees ) internal returns ( uint256 amount0, uint256 amount1, uint256 feeAmtOut0, uint256 feeAmtOut1 ) { lock(pool); _checkTickInputs(tickLower, tickUpper); if (liquidityDeltaD8 > 0) { if (tickLower % int24(uint24(pool.tickSpacing)) != 0) revert InvalidTick(); if (tickUpper % int24(uint24(pool.tickSpacing)) != 0) revert InvalidTick(); } // -------------------- UPDATE LIQUIDITY -------------------- { // update current liquidity if in-range Tiers.Tier storage tier = pool.tiers[tierId]; if (tickLower <= tier.tick && tier.tick < tickUpper) tier.liquidity = tier.liquidity.addInt128(int128(liquidityDeltaD8) << 8); } // --------------------- UPDATE TICKS ----------------------- { bool initialized; initialized = _updateTick(pool, tierId, tickLower, liquidityDeltaD8, true); initialized = _updateTick(pool, tierId, tickUpper, liquidityDeltaD8, false) || initialized; if (initialized) { Tiers.Tier storage tier = pool.tiers[tierId]; tier.updateNextTick(tickLower); tier.updateNextTick(tickUpper); } } // -------------------- UPDATE POSITION --------------------- (feeAmtOut0, feeAmtOut1) = _updatePosition( pool, owner, positionRefId, tierId, tickLower, tickUpper, liquidityDeltaD8, collectAllFees ); // -------------------- CLEAN UP TICKS ---------------------- if (liquidityDeltaD8 < 0) { bool deleted; deleted = _deleteEmptyTick(pool, tierId, tickLower); deleted = _deleteEmptyTick(pool, tierId, tickUpper) || deleted; // reset tier's next ticks if any ticks deleted if (deleted) { Tiers.Tier storage tier = pool.tiers[tierId]; int24 below = TickMaps.nextBelow(pool.tickMaps[tierId], tier.tick + 1); int24 above = pool.ticks[tierId][below].nextAbove; tier.nextTickBelow = below; tier.nextTickAbove = above; } } // -------------------- TOKEN AMOUNTS ----------------------- // calculate input and output amount for the liquidity change if (liquidityDeltaD8 != 0) (amount0, amount1) = PoolMath.calcAmtsForLiquidity( pool.tiers[tierId].sqrtPrice, TickMath.tickToSqrtPrice(tickLower), TickMath.tickToSqrtPrice(tickUpper), liquidityDeltaD8 ); // BE AWARE the pool is locked. Please unlock it after token transfer is done. } /*=============================================================== * TICKS (UPDATE LIQUIDITY) *==============================================================*/ function _updateTick( Pool storage pool, uint8 tierId, int24 tick, int96 liquidityDeltaD8, bool isLower ) internal returns (bool initialized) { mapping(int24 => Ticks.Tick) storage ticks = pool.ticks[tierId]; Ticks.Tick storage obj = ticks[tick]; if (obj.liquidityLowerD8 == 0 && obj.liquidityUpperD8 == 0) { // initialize tick if adding liquidity to empty tick if (liquidityDeltaD8 > 0) { TickMaps.TickMap storage tickMap = pool.tickMaps[tierId]; int24 below = tickMap.nextBelow(tick); int24 above = ticks[below].nextAbove; obj.nextBelow = below; obj.nextAbove = above; ticks[below].nextAbove = tick; ticks[above].nextBelow = tick; tickMap.set(tick); initialized = true; } // assume past fees and reward were generated _below_ the current tick Tiers.Tier storage tier = pool.tiers[tierId]; if (tick <= tier.tick) { obj.feeGrowthOutside0 = tier.feeGrowthGlobal0; obj.feeGrowthOutside1 = tier.feeGrowthGlobal1; } } // update liquidity if (isLower) { obj.liquidityLowerD8 = obj.liquidityLowerD8.addInt96(liquidityDeltaD8); } else { obj.liquidityUpperD8 = obj.liquidityUpperD8.addInt96(liquidityDeltaD8); } } function _deleteEmptyTick( Pool storage pool, uint8 tierId, int24 tick ) internal returns (bool deleted) { mapping(int24 => Ticks.Tick) storage ticks = pool.ticks[tierId]; Ticks.Tick storage obj = ticks[tick]; if (obj.liquidityLowerD8 == 0 && obj.liquidityUpperD8 == 0) { assert(tick != TickMath.MIN_TICK && tick != TickMath.MAX_TICK); int24 below = obj.nextBelow; int24 above = obj.nextAbove; ticks[below].nextAbove = above; ticks[above].nextBelow = below; delete ticks[tick]; pool.tickMaps[tierId].unset(tick); deleted = true; } } /*=============================================================== * POSITION (UPDATE LIQUIDITY) *==============================================================*/ function _getFeeGrowthInside( Pool storage pool, uint8 tierId, int24 tickLower, int24 tickUpper ) internal view returns (uint80 feeGrowthInside0, uint80 feeGrowthInside1) { Ticks.Tick storage upper = pool.ticks[tierId][tickUpper]; Ticks.Tick storage lower = pool.ticks[tierId][tickLower]; Tiers.Tier storage tier = pool.tiers[tierId]; int24 tickCurrent = tier.tick; unchecked { if (tickCurrent < tickLower) { // current price below range feeGrowthInside0 = lower.feeGrowthOutside0 - upper.feeGrowthOutside0; feeGrowthInside1 = lower.feeGrowthOutside1 - upper.feeGrowthOutside1; } else if (tickCurrent >= tickUpper) { // current price above range feeGrowthInside0 = upper.feeGrowthOutside0 - lower.feeGrowthOutside0; feeGrowthInside1 = upper.feeGrowthOutside1 - lower.feeGrowthOutside1; } else { // current price in range feeGrowthInside0 = tier.feeGrowthGlobal0 - upper.feeGrowthOutside0 - lower.feeGrowthOutside0; feeGrowthInside1 = tier.feeGrowthGlobal1 - upper.feeGrowthOutside1 - lower.feeGrowthOutside1; } } } function _updatePosition( Pool storage pool, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, int96 liquidityDeltaD8, bool collectAllFees ) internal returns (uint256 feeAmtOut0, uint256 feeAmtOut1) { Positions.Position storage position = Positions.get( pool.positions, owner, positionRefId, tierId, tickLower, tickUpper ); { // update position liquidity and accrue fees (uint80 feeGrowth0, uint80 feeGrowth1) = _getFeeGrowthInside(pool, tierId, tickLower, tickUpper); (feeAmtOut0, feeAmtOut1) = position.update(liquidityDeltaD8, feeGrowth0, feeGrowth1, collectAllFees); } // update settlement if position is an unsettled limit order if (position.limitOrderType != Positions.NOT_LIMIT_ORDER) { // passing a zero default tick spacing to here since the settlement state must be already initialized as // this position has been a limit order uint32 nextSnapshotId = Settlement.update( pool.settlements[tierId], pool.ticks[tierId], tickLower, tickUpper, position.limitOrderType, liquidityDeltaD8, 0 ); // not allowed to update if already settled if (position.settlementSnapshotId != nextSnapshotId) revert PositionAlreadySettled(); // reset position to normal if it is emptied if (position.liquidityD8 == 0) { position.limitOrderType = Positions.NOT_LIMIT_ORDER; position.settlementSnapshotId = 0; } } } /*=============================================================== * LIMIT ORDER *==============================================================*/ /// @notice Set (or unset) position to (or from) a limit order /// @dev It first unsets position from being a limit order (if it is), then set position to a new limit order type function setLimitOrderType( Pool storage pool, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, uint8 limitOrderType ) internal { require(pool.unlocked); require(limitOrderType <= Positions.ONE_FOR_ZERO); _checkTickInputs(tickLower, tickUpper); Positions.Position storage position = Positions.get( pool.positions, owner, positionRefId, tierId, tickLower, tickUpper ); uint16 defaultTickSpacing = uint16(pool.tickSpacing) * pool.limitOrderTickSpacingMultipliers[tierId]; // unset position to normal type if (position.limitOrderType != Positions.NOT_LIMIT_ORDER) { (uint32 nextSnapshotId, ) = Settlement.update( pool.settlements[tierId], pool.ticks[tierId], tickLower, tickUpper, position.limitOrderType, position.liquidityD8, false, defaultTickSpacing ); // not allowed to update if already settled if (position.settlementSnapshotId != nextSnapshotId) revert PositionAlreadySettled(); // unset to normal position.limitOrderType = Positions.NOT_LIMIT_ORDER; position.settlementSnapshotId = 0; } // set position to limit order if (limitOrderType != Positions.NOT_LIMIT_ORDER) { if (position.liquidityD8 == 0) revert NoLiquidityForLimitOrder(); (uint32 nextSnapshotId, uint16 tickSpacing) = Settlement.update( pool.settlements[tierId], pool.ticks[tierId], tickLower, tickUpper, limitOrderType, position.liquidityD8, true, defaultTickSpacing ); // ensure position has a correct tick range for limit order if (uint24(tickUpper - tickLower) != tickSpacing) revert InvalidTickRangeForLimitOrder(); // set to limit order position.limitOrderType = limitOrderType; position.settlementSnapshotId = nextSnapshotId; } } /// @notice Collect tokens from a settled position. Reset to normal position if all tokens are collected /// @dev We only need to update position state. No need to remove any active liquidity from tier or update upper or /// lower tick states as these have already been done when settling these positions during a swap function collectSettled( Pool storage pool, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper, uint96 liquidityD8, bool collectAllFees ) internal returns ( uint256 amount0, uint256 amount1, uint256 feeAmtOut0, uint256 feeAmtOut1 ) { lock(pool); _checkTickInputs(tickLower, tickUpper); Positions.Position storage position = Positions.get( pool.positions, owner, positionRefId, tierId, tickLower, tickUpper ); { // ensure it's a settled limit order, and get data snapshot (bool settled, Settlement.Snapshot memory snapshot) = Settlement.getSnapshot( pool.settlements[tierId], position, tickLower, tickUpper ); if (!settled) revert PositionNotSettled(); // update position using snapshotted data (feeAmtOut0, feeAmtOut1) = position.update( -liquidityD8.toInt96(), snapshot.feeGrowthInside0, snapshot.feeGrowthInside1, collectAllFees ); } // calculate output amounts using the price where settlement was done uint128 sqrtPriceLower = TickMath.tickToSqrtPrice(tickLower); uint128 sqrtPriceUpper = TickMath.tickToSqrtPrice(tickUpper); (amount0, amount1) = PoolMath.calcAmtsForLiquidity( position.limitOrderType == Positions.ZERO_FOR_ONE ? sqrtPriceUpper : sqrtPriceLower, sqrtPriceLower, sqrtPriceUpper, -liquidityD8.toInt96() ); // reset position to normal if it is emptied if (position.liquidityD8 == 0) { position.limitOrderType = Positions.NOT_LIMIT_ORDER; position.settlementSnapshotId = 0; } // BE AWARE the pool is locked. Please unlock it after token transfer is done. } /*=============================================================== * VIEW FUNCTIONS *==============================================================*/ function getPositionFeeGrowthInside( Pool storage pool, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper ) internal view returns (uint80 feeGrowthInside0, uint80 feeGrowthInside1) { if (owner != address(0)) { (bool settled, Settlement.Snapshot memory snapshot) = Settlement.getSnapshot( pool.settlements[tierId], Positions.get(pool.positions, owner, positionRefId, tierId, tickLower, tickUpper), tickLower, tickUpper ); if (settled) return (snapshot.feeGrowthInside0, snapshot.feeGrowthInside1); } return _getFeeGrowthInside(pool, tierId, tickLower, tickUpper); } /// @dev Convert fixed-sized array to dynamic-sized function getLimitOrderTickSpacingMultipliers(Pool storage pool) internal view returns (uint8[] memory multipliers) { uint8[MAX_TIERS] memory ms = pool.limitOrderTickSpacingMultipliers; multipliers = new uint8[](pool.tiers.length); unchecked { for (uint256 i; i < multipliers.length; i++) multipliers[i] = ms[i]; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.10; import "./ERC721.sol"; import "../../interfaces/common/IERC1271.sol"; import "../../interfaces/common/IERC721Descriptor.sol"; import "../../interfaces/manager/IERC721Extended.sol"; abstract contract ERC721Extended is IERC721Extended, ERC721 { address public tokenDescriptor; address public tokenDescriptorSetter; bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)"); bytes32 private immutable nameHash; mapping(uint256 => uint256) public nonces; uint128 internal minted; uint128 internal burned; constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) { nameHash = keccak256(bytes(name_)); tokenDescriptorSetter = msg.sender; } /*===================================================================== * TOKEN URI *====================================================================*/ function tokenURI(uint256 tokenId) public view override(IERC721Metadata, ERC721) returns (string memory) { require(_exists(tokenId), "token not exist"); return tokenDescriptor != address(0) ? IERC721Descriptor(tokenDescriptor).tokenURI(address(this), tokenId) : ""; } function setTokenDescriptor(address descriptor) external { require(msg.sender == tokenDescriptorSetter); tokenDescriptor = descriptor; } function setTokenDescriptorSetter(address setter) external { require(msg.sender == tokenDescriptorSetter); tokenDescriptorSetter = setter; } /*===================================================================== * PERMIT *====================================================================*/ function DOMAIN_SEPARATOR() public view returns (bytes32 domainSeperator) { domainSeperator = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), nameHash, keccak256("1"), block.chainid, address(this) ) ); } function permit( address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable { require(deadline >= block.timestamp, "Permit Expired"); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, nonces[tokenId]++, deadline)) ) ); address owner = ownerOf(tokenId); if (Address.isContract(owner)) { require(IERC1271(owner).isValidSignature(digest, abi.encodePacked(r, s, v)) == 0x1626ba7e, "Unauthorized"); } else { address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress != address(0), "Invalid signature"); require(recoveredAddress == owner, "Unauthorized"); } _approve(spender, tokenId); } /*===================================================================== * TOKEN ID & TOTAL SUPPLY *====================================================================*/ function totalSupply() public view virtual returns (uint256) { return minted - burned; } function _mintNext(address to) internal virtual returns (uint256 tokenId) { tokenId = minted + 1; // skip zero token id _mint(to, tokenId); } function latestTokenId() public view virtual returns (uint256 tokenId) { return minted; } function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual override { super._beforeTokenTransfer(from, to, tokenId); if (from == address(0)) minted++; if (to == address(0)) burned++; } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface IMuffinHubPositionsActions { /// @notice Parameters for the mint function /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param tierId Position's tier index /// @param tickLower Position's lower tick boundary /// @param tickUpper Position's upper tick boundary /// @param liquidityD8 Amount of liquidity to mint, divided by 2^8 /// @param recipient Recipient's address /// @param positionRefId Arbitrary reference id for the position /// @param senderAccRefId Sender's account id /// @param data Arbitrary data that is passed to callback function struct MintParams { address token0; address token1; uint8 tierId; int24 tickLower; int24 tickUpper; uint96 liquidityD8; address recipient; uint256 positionRefId; uint256 senderAccRefId; bytes data; } /// @notice Mint liquidity to a position /// @param params MintParams struct /// @return amount0 Token0 amount to pay by the sender /// @return amount1 Token1 amount to pay by the sender function mint(MintParams calldata params) external returns (uint256 amount0, uint256 amount1); /// @notice Parameters for the burn function /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param tierId Tier index of the position /// @param tickLower Lower tick boundary of the position /// @param tickUpper Upper tick boundary of the position /// @param liquidityD8 Amount of liquidity to burn, divided by 2^8 /// @param positionRefId Arbitrary reference id for the position /// @param accRefId Position owner's account id for receiving tokens /// @param collectAllFees True to collect all accrued fees of the position struct BurnParams { address token0; address token1; uint8 tierId; int24 tickLower; int24 tickUpper; uint96 liquidityD8; uint256 positionRefId; uint256 accRefId; bool collectAllFees; } /// @notice Remove liquidity from a position /// @dev When removing partial liquidity and params.collectAllFees is set to false, partial fees /// are sent to position owner's account proportionally to the amount of liquidity removed. /// @param params BurnParams struct /// @return amount0 Amount of token0 sent to the position owner account /// @return amount1 Amount of token1 sent to the position owner account /// @return feeAmount0 Amount of token0 fee sent to the position owner account /// @return feeAmount1 Amount of token1 fee sent to the position owner account function burn(BurnParams calldata params) external returns ( uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1 ); /// @notice Collect underlying tokens from a settled position /// @param params BurnParams struct /// @return amount0 Amount of token0 sent to the position owner account /// @return amount1 Amount of token1 sent to the position owner account /// @return feeAmount0 Amount of token0 fee sent to the position owner account /// @return feeAmount1 Amount of token1 fee sent to the position owner account function collectSettled(BurnParams calldata params) external returns ( uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1 ); /// @notice Set a position's type, e.g. set to limit order /// @param token0 Address of token0 of the pool /// @param token1 Address of token1 of the pool /// @param tierId Tier index of the position /// @param tickLower Lower tick boundary of the position /// @param tickUpper Upper tick boundary of the position /// @param positionRefId Arbitrary reference id for the position /// @param limitOrderType Direction of limit order (0: N/A; 1: zero for one; 2: one for zero) function setLimitOrderType( address token0, address token1, uint8 tierId, int24 tickLower, int24 tickUpper, uint256 positionRefId, uint8 limitOrderType ) external; /*=============================================================== * GOVERNANCE *==============================================================*/ /// @notice Update the governance address function setGovernance(address _governance) external; /// @notice Update pool's default tick spacing and protocol fee /// @param protocolFee Numerator of the % protocol fee (denominator is 255) function setDefaultParameters(uint8 tickSpacing, uint8 protocolFee) external; /// @notice Update pool's tick spacing and protocol fee /// @dev If setting a new tick spacing, the already initialized ticks that are not multiples of the new tick spacing /// will become unable to be added liquidity. To prevent this UX issue, the new tick spacing should better be a /// divisor of the old tick spacing. function setPoolParameters( bytes32 poolId, uint8 tickSpacing, uint8 protocolFee ) external; /// @notice Update a tier's swap fee and its tick spacing multiplier for limt orders function setTierParameters( bytes32 poolId, uint8 tierId, uint24 sqrtGamma, uint8 limitOrderTickSpacingMultiplier ) external; /// @notice Update the whitelist of swap fees which LPs can choose to create a pool function setDefaultAllowedSqrtGammas(uint24[] calldata sqrtGammas) external; /// @notice Update the pool-specific whitelist of swap fees function setPoolAllowedSqrtGammas(bytes32 poolId, uint24[] calldata sqrtGammas) external; /// @notice Update the pool-specific default tick spacing /// @param tickSpacing Tick spacing. Set to zero to unset the default. function setPoolDefaultTickSpacing(bytes32 poolId, uint8 tickSpacing) external; /// @notice Collect the protocol fee accrued function collectProtocolFee(address token, address recipient) external returns (uint256 amount); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; import "../../../libraries/Settlement.sol"; import "../../../libraries/Tiers.sol"; interface IMuffinHubPositionsView { /// @notice Return pool's default allowed fee rates /// @return sqrtGammas List of fee rate, expressed in sqrt(1 - %fee) (precision: 1e5) function getDefaultAllowedSqrtGammas() external view returns (uint24[] memory sqrtGammas); /// @notice Return the pool's allowed fee rates /// @param poolId Pool id /// @return sqrtGammas List of fee rate, expressed in sqrt(1 - %fee) (precision: 1e5) function getPoolAllowedSqrtGammas(bytes32 poolId) external view returns (uint24[] memory sqrtGammas); /// @notice Return the pool's default tick spacing. If set, it overrides the global default tick spacing. /// @param poolId Pool id /// @return tickSpacing Tick spacing. Zero means it is not set. function getPoolDefaultTickSpacing(bytes32 poolId) external view returns (uint8 tickSpacing); /// @notice Return the states of all the tiers in the given pool function getAllTiers(bytes32 poolId) external view returns (Tiers.Tier[] memory tiers); /// @notice Return the current fee-per-liquidity accumulator in the position's range. /// If the position was a limit order and already settled, return the values at when the position was settled. /// @return feeGrowthInside0 Accumulated token0 fee per liquidity since the creation of the pool /// @return feeGrowthInside1 Accumulated token1 fee per liquidity since the creation of the pool function getPositionFeeGrowthInside( bytes32 poolId, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper ) external view returns (uint80 feeGrowthInside0, uint80 feeGrowthInside1); /// @notice Return the state of a settlement /// @param poolId Pool id /// @param tierId Tier Index /// @param tick Tick number at which the settlement occurs /// @param zeroForOne Direction of the limit orders that the settlement handles /// @return liquidityD8 Amount of liquidity pending to settle /// @return tickSpacing Width of the limit orders which the settlement will settle /// @return nextSnapshotId Next data snapshot id that will be used function getSettlement( bytes32 poolId, uint8 tierId, int24 tick, bool zeroForOne ) external view returns ( uint96 liquidityD8, uint16 tickSpacing, uint32 nextSnapshotId ); /// @notice Return a data snapshot of a settlement /// @param poolId Pool id /// @param tierId Tier Index /// @param tick Tick number at which the settlement occurs /// @param zeroForOne Direction of the limit orders that the settlement handles /// @param snapshotId Snapshot id of your desired snapshot of this settlement function getSettlementSnapshot( bytes32 poolId, uint8 tierId, int24 tick, bool zeroForOne, uint32 snapshotId ) external view returns (Settlement.Snapshot memory snapshot); /// @notice Return the tick spacing multipliers for limit orders in the given pool's tiers, /// i.e. the list of required width of the limit range orders on each tier, /// e.g. 1 means "pool.tickSpacing * 1", 0 means disabled. function getLimitOrderTickSpacingMultipliers(bytes32 poolId) external view returns (uint8[] memory tickSpacingMultipliers); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "./math/TickMath.sol"; import "./Tiers.sol"; import "./Ticks.sol"; import "./TickMaps.sol"; import "./Positions.sol"; library Settlement { using TickMaps for TickMaps.TickMap; /** * @notice Data for settling single-sided positions (i.e. filled limit orders) * @param liquidityD8 Amount of liquidity to remove * @param tickSpacing Tick spacing of the limit orders * @param nextSnapshotId Next data snapshot id * @param snapshots Array of data snapshots */ struct Info { uint96 liquidityD8; uint16 tickSpacing; uint32 nextSnapshotId; mapping(uint32 => Snapshot) snapshots; } /// @notice Data snapshot when settling the positions struct Snapshot { uint80 feeGrowthInside0; uint80 feeGrowthInside1; } /** * @notice Update the amount of liquidity pending to be settled on a tick, given the lower and upper tick * boundaries of a limit-order position. * @param settlements Mapping of settlements of each tick * @param ticks Mapping of ticks of the tier which the position is in * @param tickLower Lower tick boundary of the position * @param tickUpper Upper tick boundary of the position * @param limitOrderType Direction of the limit order (i.e. token0 or token1) * @param liquidityDeltaD8 Change of the amount of liquidity to be settled * @param isAdd True if the liquidity change is additive. False otherwise. * @param defaultTickSpacing Default tick spacing of limit orders. Only needed when initializing * @return nextSnapshotId Settlement's next snapshot id * @return tickSpacing Tick spacing of the limit orders pending to be settled */ function update( mapping(int24 => Info[2]) storage settlements, mapping(int24 => Ticks.Tick) storage ticks, int24 tickLower, int24 tickUpper, uint8 limitOrderType, uint96 liquidityDeltaD8, bool isAdd, uint16 defaultTickSpacing ) internal returns (uint32 nextSnapshotId, uint16 tickSpacing) { assert(limitOrderType == Positions.ZERO_FOR_ONE || limitOrderType == Positions.ONE_FOR_ZERO); Info storage settlement = limitOrderType == Positions.ZERO_FOR_ONE ? settlements[tickUpper][1] : settlements[tickLower][0]; // update the amount of liquidity to settle settlement.liquidityD8 = isAdd ? settlement.liquidityD8 + liquidityDeltaD8 : settlement.liquidityD8 - liquidityDeltaD8; // initialize settlement if it's the first limit order at this tick nextSnapshotId = settlement.nextSnapshotId; if (settlement.tickSpacing == 0) { settlement.tickSpacing = defaultTickSpacing; settlement.snapshots[nextSnapshotId] = Snapshot(0, 1); // pre-fill to reduce SSTORE gas during swap } // if no liqudity to settle, clear tick spacing so as to set a latest one next time bool isEmpty = settlement.liquidityD8 == 0; if (isEmpty) settlement.tickSpacing = 0; // update "needSettle" flag in tick state if (limitOrderType == Positions.ZERO_FOR_ONE) { ticks[tickUpper].needSettle1 = !isEmpty; } else { ticks[tickLower].needSettle0 = !isEmpty; } // return data for validating position's settling status tickSpacing = settlement.tickSpacing; } /// @dev Bridging function to sidestep "stack too deep" problem function update( mapping(int24 => Info[2]) storage settlements, mapping(int24 => Ticks.Tick) storage ticks, int24 tickLower, int24 tickUpper, uint8 limitOrderType, int96 liquidityDeltaD8, uint16 defaultTickSpacing ) internal returns (uint32 nextSnapshotId) { bool isAdd = liquidityDeltaD8 > 0; unchecked { (nextSnapshotId, ) = update( settlements, ticks, tickLower, tickUpper, limitOrderType, uint96(isAdd ? liquidityDeltaD8 : -liquidityDeltaD8), isAdd, defaultTickSpacing ); } } /** * @notice Settle single-sided positions, i.e. filled limit orders, that ends at the tick `tickEnd`. * @dev Called during a swap right after tickEnd is crossed. It updates settlement and tick, and possibly tickmap. * @param settlements Mapping of settlements of each tick * @param ticks Mapping of ticks of a tier * @param tickMap Tick bitmap of a tier * @param tier Latest tier data (in memory) currently used in the swap * @param tickEnd Ending tick of the limit orders, i.e. the tick just being crossed in the swap * @param token0In The direction of the ongoing swap * @return tickStart Starting tick of the limit orders, i.e. the other tick besides "tickEnd" that forms the positions * @return liquidityD8 Amount of liquidity settled */ function settle( mapping(int24 => Info[2]) storage settlements, mapping(int24 => Ticks.Tick) storage ticks, TickMaps.TickMap storage tickMap, Tiers.Tier memory tier, int24 tickEnd, bool token0In ) internal returns (int24 tickStart, uint96 liquidityD8) { Info storage settlement; // we assume settlement is intialized Ticks.Tick storage start; Ticks.Tick storage end = ticks[tickEnd]; unchecked { if (token0In) { settlement = settlements[tickEnd][0]; tickStart = tickEnd + int16(settlement.tickSpacing); start = ticks[tickStart]; // remove liquidity changes on ticks (effect) liquidityD8 = settlement.liquidityD8; start.liquidityUpperD8 -= liquidityD8; end.liquidityLowerD8 -= liquidityD8; end.needSettle0 = false; } else { settlement = settlements[tickEnd][1]; tickStart = tickEnd - int16(settlement.tickSpacing); start = ticks[tickStart]; // remove liquidity changes on ticks (effect) liquidityD8 = settlement.liquidityD8; start.liquidityLowerD8 -= liquidityD8; end.liquidityUpperD8 -= liquidityD8; end.needSettle1 = false; } // play extra safe to ensure settlement is initialized assert(tickStart != tickEnd); // snapshot data inside the tick range (effect) settlement.snapshots[settlement.nextSnapshotId] = Snapshot( end.feeGrowthOutside0 - start.feeGrowthOutside0, end.feeGrowthOutside1 - start.feeGrowthOutside1 ); } // reset settlement state since it's finished (effect) settlement.nextSnapshotId++; settlement.tickSpacing = 0; settlement.liquidityD8 = 0; // delete the starting tick if empty (effect) if (start.liquidityLowerD8 == 0 && start.liquidityUpperD8 == 0) { assert(tickStart != TickMath.MIN_TICK && tickStart != TickMath.MAX_TICK); int24 below = start.nextBelow; int24 above = start.nextAbove; ticks[below].nextAbove = above; ticks[above].nextBelow = below; delete ticks[tickStart]; tickMap.unset(tickStart); } // delete the ending tick if empty (effect), and update tier's next ticks (locally) if (end.liquidityLowerD8 == 0 && end.liquidityUpperD8 == 0) { assert(tickEnd != TickMath.MIN_TICK && tickEnd != TickMath.MAX_TICK); int24 below = end.nextBelow; int24 above = end.nextAbove; ticks[below].nextAbove = above; ticks[above].nextBelow = below; delete ticks[tickEnd]; tickMap.unset(tickEnd); // since the tier just crossed tickEnd, we can safely set tier's next ticks in this way tier.nextTickBelow = below; tier.nextTickAbove = above; } } /** * @notice Get data snapshot if the position is a settled limit order * @param settlements Mapping of settlements of each tick * @param position Position state * @param tickLower Position's lower tick boundary * @param tickUpper Position's upper tick boundary * @return settled True if position is settled * @return snapshot Data snapshot if position is settled */ function getSnapshot( mapping(int24 => Info[2]) storage settlements, Positions.Position storage position, int24 tickLower, int24 tickUpper ) internal view returns (bool settled, Snapshot memory snapshot) { if (position.limitOrderType == Positions.ZERO_FOR_ONE || position.limitOrderType == Positions.ONE_FOR_ZERO) { Info storage settlement = position.limitOrderType == Positions.ZERO_FOR_ONE ? settlements[tickUpper][1] : settlements[tickLower][0]; if (position.settlementSnapshotId < settlement.nextSnapshotId) { settled = true; snapshot = settlement.snapshots[position.settlementSnapshotId]; } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "./math/TickMath.sol"; library TickMaps { struct TickMap { uint256 blockMap; // stores which blocks are initialized mapping(uint256 => uint256) blocks; // stores which words are initialized mapping(uint256 => uint256) words; // stores which ticks are initialized } /// @dev Compress and convert tick into an unsigned integer, then compute the indices of the block and word that the /// compressed tick uses. Assume tick >= TickMath.MIN_TICK function _indices(int24 tick) internal pure returns ( uint256 blockIdx, uint256 wordIdx, uint256 compressed ) { unchecked { compressed = uint256(int256((tick - TickMath.MIN_TICK))); blockIdx = compressed >> 16; wordIdx = compressed >> 8; assert(blockIdx < 256); } } /// @dev Convert the unsigned integer back to a tick. Assume "compressed" is a valid value, computed by _indices function. function _decompress(uint256 compressed) internal pure returns (int24 tick) { unchecked { tick = int24(int256(compressed) + TickMath.MIN_TICK); } } function set(TickMap storage self, int24 tick) internal { (uint256 blockIdx, uint256 wordIdx, uint256 compressed) = _indices(tick); self.words[wordIdx] |= 1 << (compressed & 0xFF); self.blocks[blockIdx] |= 1 << (wordIdx & 0xFF); self.blockMap |= 1 << blockIdx; } function unset(TickMap storage self, int24 tick) internal { (uint256 blockIdx, uint256 wordIdx, uint256 compressed) = _indices(tick); self.words[wordIdx] &= ~(1 << (compressed & 0xFF)); if (self.words[wordIdx] == 0) { self.blocks[blockIdx] &= ~(1 << (wordIdx & 0xFF)); if (self.blocks[blockIdx] == 0) { self.blockMap &= ~(1 << blockIdx); } } } /// @dev Find the next initialized tick below the given tick. Assume tick >= TickMath.MIN_TICK // How to find the next initialized bit below the i-th bit inside a word (e.g. i = 8)? // 1) Mask _off_ the word from the 8th bit to the 255th bit (zero-indexed) // 2) Find the most significant bit of the masked word // 8th bit // ↓ // word: 0001 1101 0010 1100 // mask: 0000 0000 1111 1111 i.e. (1 << i) - 1 // masked: 0000 0000 0010 1100 // ↑ // msb(masked) = 5 function nextBelow(TickMap storage self, int24 tick) internal view returns (int24 tickBelow) { unchecked { (uint256 blockIdx, uint256 wordIdx, uint256 compressed) = _indices(tick); uint256 word = self.words[wordIdx] & ((1 << (compressed & 0xFF)) - 1); if (word == 0) { uint256 block_ = self.blocks[blockIdx] & ((1 << (wordIdx & 0xFF)) - 1); if (block_ == 0) { uint256 blockMap = self.blockMap & ((1 << blockIdx) - 1); assert(blockMap != 0); blockIdx = _msb(blockMap); block_ = self.blocks[blockIdx]; } wordIdx = (blockIdx << 8) | _msb(block_); word = self.words[wordIdx]; } tickBelow = _decompress((wordIdx << 8) | _msb(word)); } } /// @notice Returns the index of the most significant bit of the number, where the least significant bit is at index 0 /// and the most significant bit is at index 255 /// @dev The function satisfies the property: x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) /// @param x the value for which to compute the most significant bit, must be greater than 0 /// @return r the index of the most significant bit function _msb(uint256 x) internal pure returns (uint8 r) { unchecked { assert(x > 0); if (x >= 0x100000000000000000000000000000000) { x >>= 128; r += 128; } if (x >= 0x10000000000000000) { x >>= 64; r += 64; } if (x >= 0x100000000) { x >>= 32; r += 32; } if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 0x4) { x >>= 2; r += 2; } if (x >= 0x2) r += 1; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev https://github.com/Uniswap/uniswap-v3-core/blob/v1.0.0/contracts/libraries/FullMath.sol * Added `unchecked` and changed line 76 for being compatible in solidity 0.8 */ // solhint-disable max-line-length /// @title Contains 512-bit math functions /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision /// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits library FullMath { /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv function mulDiv( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = a * b // Compute the product mod 2**256 and mod 2**256 - 1 // then 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 uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(a, b, not(0)) prod0 := mul(a, b) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { require(denominator > 0); assembly { result := div(prod0, denominator) } return result; } // Make sure the result is less than 2**256. // Also prevents denominator == 0 require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0] // Compute remainder using mulmod uint256 remainder; assembly { remainder := mulmod(a, b, denominator) } // Subtract 256 bit number from 512 bit number assembly { prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator // Compute largest power of two divisor of denominator. // Always >= 1. // [*] The next line is edited to be compatible with solidity 0.8 // ref: https://ethereum.stackexchange.com/a/96646 // original: uint256 twos = -denominator & denominator; uint256 twos = denominator & (~denominator + 1); // Divide denominator by power of two assembly { denominator := div(denominator, twos) } // Divide [prod1 prod0] by the factors of two assembly { prod0 := div(prod0, twos) } // Shift in bits from prod1 into prod0. For this we need // to flip `twos` such that it is 2**256 / twos. // If twos is zero, then it becomes one assembly { twos := add(div(sub(0, twos), twos), 1) } prod0 |= prod1 * twos; // 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 // correct for four bits. That is, denominator * inv = 1 mod 2**4 uint256 inv = (3 * denominator) ^ 2; // Now use 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. inv *= 2 - denominator * inv; // inverse mod 2**8 inv *= 2 - denominator * inv; // inverse mod 2**16 inv *= 2 - denominator * inv; // inverse mod 2**32 inv *= 2 - denominator * inv; // inverse mod 2**64 inv *= 2 - denominator * inv; // inverse mod 2**128 inv *= 2 - denominator * inv; // 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 precoditions 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 * inv; return result; } } /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result function mulDivRoundingUp( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { result = mulDiv(a, b, denominator); if (mulmod(a, b, denominator) > 0) { result++; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "./FullMath.sol"; import "./PoolMath.sol"; import "./UnsafeMath.sol"; import "./Math.sol"; import "../Tiers.sol"; /// @dev Technically maximum number of fee tiers per pool. /// @dev Declared at file level so other libraries/contracts can use it to define fixed-size array. uint256 constant MAX_TIERS = 6; library SwapMath { using Math for uint256; using Math for int256; int256 internal constant REJECTED = type(int256).max; // represents the tier is rejected for the swap int256 private constant MAX_UINT_DIV_1E10 = 0x6DF37F675EF6EADF5AB9A2072D44268D97DF837E6748956E5C6C2117; uint256 private constant Q72 = 0x1000000000000000000; /// @notice Given a set of tiers and the desired input amount, calculate the optimized input amount for each tier /// @param tiers List of tiers /// @param isToken0 True if "amount" refers to token0 /// @param amount Desired input amount of the swap (must be positive) /// @param tierChoices Bitmap to allow which tiers to swap /// @return amts Optimized input amounts for tiers function calcTierAmtsIn( Tiers.Tier[] memory tiers, bool isToken0, int256 amount, uint256 tierChoices ) internal pure returns (int256[MAX_TIERS] memory amts) { assert(amount > 0); uint256[MAX_TIERS] memory lsg; // array of liquidity divided by sqrt gamma (UQ128) uint256[MAX_TIERS] memory res; // array of token reserve divided by gamma (UQ200) uint256 num; // numerator of sqrt lambda (sum of UQ128) uint256 denom; // denominator of sqrt lambda (sum of UQ200 + amount) unchecked { for (uint256 i; i < tiers.length; i++) { // reject unselected tiers if (tierChoices & (1 << i) == 0) { amts[i] = REJECTED; continue; } // calculate numerator and denominator of sqrt lamdba (lagrange multiplier) Tiers.Tier memory t = tiers[i]; uint256 liquidity = uint256(t.liquidity); uint24 sqrtGamma = t.sqrtGamma; num += (lsg[i] = UnsafeMath.ceilDiv(liquidity * 1e5, sqrtGamma)); denom += (res[i] = isToken0 ? UnsafeMath.ceilDiv(liquidity * Q72 * 1e10, uint256(t.sqrtPrice) * sqrtGamma * sqrtGamma) : UnsafeMath.ceilDiv(liquidity * t.sqrtPrice, (Q72 * sqrtGamma * sqrtGamma) / 1e10)); } } denom += uint256(amount); unchecked { // calculate input amts, then reject the tiers with negative input amts. // repeat until all input amts are non-negative uint256 product = denom * num; bool wontOverflow = (product / denom == num) && (product <= uint256(type(int256).max)); for (uint256 i; i < tiers.length; ) { if (amts[i] != REJECTED) { if ( (amts[i] = ( wontOverflow ? int256((denom * lsg[i]) / num) : FullMath.mulDiv(denom, lsg[i], num).toInt256() ).sub(int256(res[i]))) < 0 ) { amts[i] = REJECTED; num -= lsg[i]; denom -= res[i]; i = 0; continue; } } i++; } } } /// @notice Given a set of tiers and the desired output amount, calculate the optimized output amount for each tier /// @param tiers List of tiers /// @param isToken0 True if "amount" refers to token0 /// @param amount Desired output amount of the swap (must be negative) /// @param tierChoices Bitmap to allow which tiers to swap /// @return amts Optimized output amounts for tiers function calcTierAmtsOut( Tiers.Tier[] memory tiers, bool isToken0, int256 amount, uint256 tierChoices ) internal pure returns (int256[MAX_TIERS] memory amts) { assert(amount < 0); uint256[MAX_TIERS] memory lsg; // array of liquidity divided by sqrt fee (UQ128) uint256[MAX_TIERS] memory res; // array of token reserve (UQ200) uint256 num; // numerator of sqrt lambda (sum of UQ128) int256 denom; // denominator of sqrt lambda (sum of UQ200 - amount) unchecked { for (uint256 i; i < tiers.length; i++) { // reject unselected tiers if (tierChoices & (1 << i) == 0) { amts[i] = REJECTED; continue; } // calculate numerator and denominator of sqrt lamdba (lagrange multiplier) Tiers.Tier memory t = tiers[i]; uint256 liquidity = uint256(t.liquidity); num += (lsg[i] = (liquidity * 1e5) / t.sqrtGamma); denom += int256(res[i] = isToken0 ? (liquidity << 72) / t.sqrtPrice : (liquidity * t.sqrtPrice) >> 72); } } denom += amount; unchecked { // calculate output amts, then reject the tiers with positive output amts. // repeat until all output amts are non-positive for (uint256 i; i < tiers.length; ) { if (amts[i] != REJECTED) { if ((amts[i] = _ceilMulDiv(denom, lsg[i], num).sub(int256(res[i]))) > 0) { amts[i] = REJECTED; num -= lsg[i]; denom -= int256(res[i]); i = 0; continue; } } i++; } } } function _ceilMulDiv( int256 x, uint256 y, uint256 denom ) internal pure returns (int256 z) { unchecked { z = x < 0 ? -FullMath.mulDiv(uint256(-x), y, denom).toInt256() : FullMath.mulDivRoundingUp(uint256(x), y, denom).toInt256(); } } /// @dev Calculate a single swap step. We process the swap as much as possible until the tier's price hits the next tick. /// @param isToken0 True if "amount" refers to token0 /// @param exactIn True if the swap is specified with an input token amount (instead of an output) /// @param amount The swap amount (positive: token in; negative token out) /// @param sqrtP The sqrt price currently /// @param sqrtPTick The sqrt price of the next crossing tick /// @param liquidity The current liqudity amount /// @param sqrtGamma The sqrt of (1 - percentage swap fee) (precision: 1e5) /// @return amtA The delta of the pool's tokenA balance (tokenA means token0 if `isToken0` is true, vice versa) /// @return amtB The delta of the pool's tokenB balance (tokenB means the opposite token of tokenA) /// @return sqrtPNew The new sqrt price after the swap /// @return feeAmt The fee amount charged for this swap function computeStep( bool isToken0, bool exactIn, int256 amount, uint128 sqrtP, uint128 sqrtPTick, uint128 liquidity, uint24 sqrtGamma ) internal pure returns ( int256 amtA, int256 amtB, uint128 sqrtPNew, uint256 feeAmt ) { unchecked { amtA = amount; int256 amtInExclFee; // i.e. input amt excluding fee // calculate amt needed to reach to the tick int256 amtTick = isToken0 ? PoolMath.calcAmt0FromSqrtP(sqrtP, sqrtPTick, liquidity) : PoolMath.calcAmt1FromSqrtP(sqrtP, sqrtPTick, liquidity); // calculate percentage fee (precision: 1e10) uint256 gamma = uint256(sqrtGamma) * sqrtGamma; if (exactIn) { // amtA: the input amt (positive) // amtB: the output amt (negative) // calculate input amt excluding fee amtInExclFee = amtA < MAX_UINT_DIV_1E10 ? int256((uint256(amtA) * gamma) / 1e10) : int256((uint256(amtA) / 1e10) * gamma); // check if crossing tick if (amtInExclFee < amtTick) { // no cross tick: calculate new sqrt price after swap sqrtPNew = isToken0 ? PoolMath.calcSqrtPFromAmt0(sqrtP, liquidity, amtInExclFee) : PoolMath.calcSqrtPFromAmt1(sqrtP, liquidity, amtInExclFee); } else { // cross tick: replace new sqrt price and input amt sqrtPNew = sqrtPTick; amtInExclFee = amtTick; // re-calculate input amt _including_ fee amtA = ( amtInExclFee < MAX_UINT_DIV_1E10 ? UnsafeMath.ceilDiv(uint256(amtInExclFee) * 1e10, gamma) : UnsafeMath.ceilDiv(uint256(amtInExclFee), gamma) * 1e10 ).toInt256(); } // calculate output amt amtB = isToken0 ? PoolMath.calcAmt1FromSqrtP(sqrtP, sqrtPNew, liquidity) : PoolMath.calcAmt0FromSqrtP(sqrtP, sqrtPNew, liquidity); // calculate fee amt feeAmt = uint256(amtA - amtInExclFee); } else { // amtA: the output amt (negative) // amtB: the input amt (positive) // check if crossing tick if (amtA > amtTick) { // no cross tick: calculate new sqrt price after swap sqrtPNew = isToken0 ? PoolMath.calcSqrtPFromAmt0(sqrtP, liquidity, amtA) : PoolMath.calcSqrtPFromAmt1(sqrtP, liquidity, amtA); } else { // cross tick: replace new sqrt price and output amt sqrtPNew = sqrtPTick; amtA = amtTick; } // calculate input amt excluding fee amtInExclFee = isToken0 ? PoolMath.calcAmt1FromSqrtP(sqrtP, sqrtPNew, liquidity) : PoolMath.calcAmt0FromSqrtP(sqrtP, sqrtPNew, liquidity); // calculate input amt amtB = ( amtInExclFee < MAX_UINT_DIV_1E10 ? UnsafeMath.ceilDiv(uint256(amtInExclFee) * 1e10, gamma) : UnsafeMath.ceilDiv(uint256(amtInExclFee), gamma) * 1e10 ).toInt256(); // calculate fee amt feeAmt = uint256(amtB - amtInExclFee); } // reject tier if zero input amt and not crossing tick if (amtInExclFee == 0 && sqrtPNew != sqrtPTick) { amtA = REJECTED; amtB = 0; sqrtPNew = sqrtP; feeAmt = 0; } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; /** * Forked from OpenZeppelin 4.3.1's ERC721 contract. * Removed the `_owners` mapping and added virtual getter and setter functions for token owners. */ /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension, but not including the Enumerable extension, which is available separately as * {ERC721Enumerable}. */ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { using Address for address; using Strings for uint256; // Token name string private _name; // Token symbol string private _symbol; // // Mapping from token ID to owner address // mapping(uint256 => address) private _owners; // Mapping owner address to token count mapping(address => uint256) private _balances; // Mapping from token ID to approved address mapping(uint256 => address) private _tokenApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual override returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _balances[owner]; } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { address owner = _getOwner(tokenId); require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, can be overriden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ""; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual override { address owner = ERC721.ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require( _msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual override returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { require(operator != _msgSender(), "ERC721: approve to caller"); _operatorApprovals[_msgSender()][operator] = approved; emit ApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom( address from, address to, uint256 tokenId ) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId ) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory _data ) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * `_data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer( address from, address to, uint256 tokenId, bytes memory _data ) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _getOwner(tokenId) != address(0); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ERC721.ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); } /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: * * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint( address to, uint256 tokenId, bytes memory _data ) internal virtual { _mint(to, tokenId); require( _checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer" ); } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId); _balances[to] += 1; _setOwner(tokenId, to); emit Transfer(address(0), to, tokenId); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal virtual { address owner = ERC721.ownerOf(tokenId); _beforeTokenTransfer(owner, address(0), tokenId); // Clear approvals _approve(address(0), tokenId); _balances[owner] -= 1; _setOwner(tokenId, address(0)); emit Transfer(owner, address(0), tokenId); } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer( address from, address to, uint256 tokenId ) internal virtual { require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); require(to != address(0), "ERC721: transfer to the zero address"); _beforeTokenTransfer(from, to, tokenId); // Clear approvals from the previous owner _approve(address(0), tokenId); _balances[from] -= 1; _balances[to] += 1; _setOwner(tokenId, to); emit Transfer(from, to, tokenId); } /** * @dev Approve `to` to operate on `tokenId` * * Emits a {Approval} event. */ function _approve(address to, uint256 tokenId) internal virtual { _tokenApprovals[tokenId] = to; emit Approval(ERC721.ownerOf(tokenId), to, tokenId); } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory _data ) private returns (bool) { if (to.isContract()) { try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert("ERC721: transfer to non ERC721Receiver implementer"); } else { assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual {} function _getOwner(uint256 tokenId) internal view virtual returns (address owner); function _setOwner(uint256 tokenId, address owner) internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.0; /// @title Interface for verifying contract-based account signatures /// @notice Interface that verifies provided signature for the data /// @dev Interface defined by EIP-1271 interface IERC1271 { /// @notice Returns whether the provided signature is valid for the provided data /// @dev MUST return the bytes4 magic value 0x1626ba7e when function passes. /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5). /// MUST allow external calls. /// @param hash Hash of the data to be signed /// @param signature Signature byte array associated with _data /// @return magicValue The bytes4 magic value 0x1626ba7e function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; interface IERC721Descriptor { function tokenURI(address token, uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Interface for permit /// @notice Interface used by DAI/CHAI for permit interface IERC20PermitAllowed { /// @notice Approve the spender to spend some tokens via the holder signature /// @dev This is the permit interface used by DAI and CHAI /// @param holder Address of the token holder, the token owner /// @param spender Address of the token spender /// @param nonce Holder's nonce, increases at each call to permit /// @param expiry Timestamp at which the permit is no longer valid /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0 /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` function permit( address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s ) external; }
{ "optimizer": { "enabled": true, "runs": 4500 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_hub","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeadlinePassed","type":"error"},{"inputs":[],"name":"FailedTransferETH","type":"error"},{"inputs":[],"name":"FailedTransferFrom","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"domainSeperator","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount0Desired","type":"uint256"},{"internalType":"uint256","name":"amount1Desired","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"bool","name":"useAccount","type":"bool"}],"internalType":"struct IPositionManager.AddLiquidityParams","name":"params","type":"tuple"}],"name":"addLiquidity","outputs":[{"internalType":"uint96","name":"liquidityD8","type":"uint96"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"sqrtGamma","type":"uint24"},{"internalType":"bool","name":"useAccount","type":"bool"},{"internalType":"uint8","name":"expectedTierId","type":"uint8"}],"name":"addTier","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"burn","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"sqrtGamma","type":"uint24"},{"internalType":"uint128","name":"sqrtPrice","type":"uint128"},{"internalType":"bool","name":"useAccount","type":"bool"}],"name":"createPool","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"recipientAccRefId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToExternal","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"fromAccount","type":"bool"},{"internalType":"bool","name":"toAccount","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"exactIn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"tierChoices","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"fromAccount","type":"bool"},{"internalType":"bool","name":"toAccount","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"exactInSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"fromAccount","type":"bool"},{"internalType":"bool","name":"toAccount","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"exactOut","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"tierChoices","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"fromAccount","type":"bool"},{"internalType":"bool","name":"toAccount","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"exactOutSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getPosition","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint8","name":"tierId","type":"uint8"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"components":[{"internalType":"uint96","name":"liquidityD8","type":"uint96"},{"internalType":"uint80","name":"feeGrowthInside0Last","type":"uint80"},{"internalType":"uint80","name":"feeGrowthInside1Last","type":"uint80"},{"internalType":"uint8","name":"limitOrderType","type":"uint8"},{"internalType":"uint32","name":"settlementSnapshotId","type":"uint32"}],"internalType":"struct Positions.Position","name":"position","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hub","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestTokenId","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint8","name":"tierId","type":"uint8"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint256","name":"amount0Desired","type":"uint256"},{"internalType":"uint256","name":"amount1Desired","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"useAccount","type":"bool"}],"internalType":"struct IPositionManager.MintParams","name":"params","type":"tuple"}],"name":"mint","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint96","name":"liquidityD8","type":"uint96"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"muffinDepositCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"muffinMintCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"muffinSwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"pairIdsByPoolId","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint40","name":"","type":"uint40"}],"name":"pairs","outputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"positionsByTokenId","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint40","name":"pairId","type":"uint40"},{"internalType":"uint8","name":"tierId","type":"uint8"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refundETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint96","name":"liquidityD8","type":"uint96"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"address","name":"withdrawTo","type":"address"},{"internalType":"bool","name":"collectAllFees","type":"bool"},{"internalType":"bool","name":"settled","type":"bool"}],"internalType":"struct IPositionManager.RemoveLiquidityParams","name":"params","type":"tuple"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"feeAmount0","type":"uint256"},{"internalType":"uint256","name":"feeAmount1","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowed","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint8","name":"limitOrderType","type":"uint8"}],"name":"setLimitOrderType","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"descriptor","type":"address"}],"name":"setTokenDescriptor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"setter","type":"address"}],"name":"setTokenDescriptorSetter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenDescriptor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenDescriptorSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60e0604052600a805464ffffffffff191660011790553480156200002257600080fd5b5060405162006313380380620063138339810160408190526200004591620001bb565b604080518082018252600f81526e26bab33334b7102837b9b4ba34b7b760891b60208083019182528351808501909452600a8452694d554646494e2d504f5360b01b908401526001600160a01b0380861660a0528416608052815191929183918391620000b591600091620000f8565b508051620000cb906001906020840190620000f8565b5050825160209093019290922060c0525050600680546001600160a01b0319163317905550620002309050565b8280546200010690620001f3565b90600052602060002090601f0160209004810192826200012a576000855562000175565b82601f106200014557805160ff191683800117855562000175565b8280016001018555821562000175579182015b828111156200017557825182559160200191906001019062000158565b506200018392915062000187565b5090565b5b8082111562000183576000815560010162000188565b80516001600160a01b0381168114620001b657600080fd5b919050565b60008060408385031215620001cf57600080fd5b620001da836200019e565b9150620001ea602084016200019e565b90509250929050565b600181811c908216806200020857607f821691505b602082108114156200022a57634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c051615fe86200032b600039600081816105930152611cab01526000818161061e0152818161106a015281816112360152818161139d015281816114ea015281816117d50152818161188201528181611a3c01528181611b4401528181612308015281816124f60152818161269901528181612ae10152818161302d0152818161305e0152818161339d015281816134b90152818161358501528181613ec501528181613f6c015281816140200152818161412b0152614514015260008181610348015281816106a5015281816131210152818161321b01528181613de001528181613e260152613ef40152615fe86000f3fe6080604052600436106103385760003560e01c806370a08231116101b0578063b88d4fde116100ec578063e16d9ce511610095578063f0db800c1161006f578063f0db800c14610b97578063f1371dd514610bf8578063f3995c6714610c18578063f5a9817514610c2b57600080fd5b8063e16d9ce514610a75578063e985e9c514610a88578063eb02c30114610ad157600080fd5b8063ce3fa4dd116100c6578063ce3fa4dd14610a12578063d9899ff014610a4f578063d9caed1214610a6257600080fd5b8063b88d4fde146108ff578063c2814b0c1461091f578063c87b56dd146109f257600080fd5b806395d89b4111610159578063ac9650d811610133578063ac9650d81461088c578063ae4bf64b146108ac578063b6dc7ea3146108cc578063b80f55c9146108ec57600080fd5b806395d89b4114610844578063a22cb46514610859578063a73970cf1461087957600080fd5b80638137cdbf1161018a5780638137cdbf146108005780638340f549146108135780638c0e83491461082657600080fd5b806370a082311461078d5780637ac2ff7b146107ad5780637c2d10fd146107c057600080fd5b806330adf81f1161027f5780634aa4a4fc116102285780635a9d7a68116102025780635a9d7a681461071a5780635dd582041461073a5780636352211e1461074d578063641229d91461076d57600080fd5b80634aa4a4fc146106935780634f594557146106c75780635325261c146106e757600080fd5b80633d705707116102595780633d7057071461064057806342842e0e146106605780634659a4941461068057600080fd5b806330adf81f146105235780633644e51514610557578063365a86fc1461060c57600080fd5b806318160ddd116102e15780632809a659116102bb5780632809a659146104b35780632e85d3ae146104c65780632f8bc1ec146104d957600080fd5b806318160ddd1461046b57806323b872dd146104805780632513f7f2146104a057600080fd5b8063095ea7b311610312578063095ea7b31461040857806312210e8a14610428578063141a468c1461043057600080fd5b806301ffc9a71461037957806306fdde03146103ae578063081812fc146103d057600080fd5b3661037457336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461037257600080fd5b005b600080fd5b34801561038557600080fd5b50610399610394366004614eb8565b610c3e565b60405190151581526020015b60405180910390f35b3480156103ba57600080fd5b506103c3610d23565b6040516103a59190614f2d565b3480156103dc57600080fd5b506103f06103eb366004614f40565b610db5565b6040516001600160a01b0390911681526020016103a5565b34801561041457600080fd5b50610372610423366004614f6e565b610e60565b610372610f92565b34801561043c57600080fd5b5061045d61044b366004614f40565b60076020526000908152604090205481565b6040519081526020016103a5565b34801561047757600080fd5b5061045d610fa4565b34801561048c57600080fd5b5061037261049b366004614f9a565b610fe1565b6103726104ae36600461500f565b611068565b61045d6104c1366004615080565b611228565b61045d6104d4366004615080565b61138f565b3480156104e557600080fd5b5061050d6104f4366004614f40565b600c6020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016103a5565b34801561052f57600080fd5b5061045d7f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad81565b34801561056357600080fd5b5061045d604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b34801561061857600080fd5b506103f07f000000000000000000000000000000000000000000000000000000000000000081565b34801561064c57600080fd5b5061037261065b366004615161565b6114df565b34801561066c57600080fd5b5061037261067b366004614f9a565b61154f565b61037261068e3660046151ec565b61156a565b34801561069f57600080fd5b506103f07f000000000000000000000000000000000000000000000000000000000000000081565b3480156106d357600080fd5b506103726106e2366004615248565b611618565b6106fa6106f5366004615265565b61165e565b6040805194855260208501939093529183015260608201526080016103a5565b34801561072657600080fd5b506005546103f0906001600160a01b031681565b610372610748366004615277565b6119f7565b34801561075957600080fd5b506103f0610768366004614f40565b611aae565b34801561077957600080fd5b506103726107883660046152bf565b611b39565b34801561079957600080fd5b5061045d6107a8366004615248565b611b90565b6103726107bb3660046151ec565b611c2a565b6107d36107ce36600461531b565b61208e565b604080519485526bffffffffffffffffffffffff90931660208501529183015260608201526080016103a5565b61037261080e36600461532e565b612306565b610372610821366004614f9a565b6124f4565b34801561083257600080fd5b506008546001600160801b031661045d565b34801561085057600080fd5b506103c3612599565b34801561086557600080fd5b50610372610874366004615391565b6125a8565b61045d6108873660046153ca565b61268b565b61089f61089a3660046154a8565b612853565b6040516103a591906154ea565b3480156108b857600080fd5b506006546103f0906001600160a01b031681565b3480156108d857600080fd5b506103726108e7366004615248565b612971565b6103726108fa3660046154a8565b6129b7565b34801561090b57600080fd5b5061037261091a3660046155d9565b612be1565b34801561092b57600080fd5b506109ae61093a366004614f40565b6009602052600090815260409020546001600160a01b0381169064ffffffffff740100000000000000000000000000000000000000008204169060ff79010000000000000000000000000000000000000000000000000082041690600160d01b8104600290810b91600160e81b9004900b85565b604080516001600160a01b03909616865264ffffffffff909416602086015260ff90921692840192909252600291820b6060840152900b608082015260a0016103a5565b3480156109fe57600080fd5b506103c3610a0d366004614f40565b612c69565b610a25610a20366004615688565b612d85565b604080516bffffffffffffffffffffffff90941684526020840192909252908201526060016103a5565b610372610a5d36600461569a565b612f36565b610372610a70366004614f9a565b61305c565b610372610a833660046156bf565b6130f0565b348015610a9457600080fd5b50610399610aa33660046156e4565b6001600160a01b03918216600090815260046020908152604080832093909416825291909152205460ff1690565b348015610add57600080fd5b50610af1610aec366004614f40565b613289565b604080516001600160a01b039889168152968816602080890191909152979095168686015260ff938416606080880191909152600293840b6080808901919091529290930b60a087015280516bffffffffffffffffffffffff1660c08701529586015169ffffffffffffffffffff90811660e087015293860151909316610100850152840151166101208301529091015163ffffffff16610140820152610160016103a5565b348015610ba357600080fd5b50610bd8610bb2366004615712565b600b60205260009081526040902080546001909101546001600160a01b03918216911682565b604080516001600160a01b039384168152929091166020830152016103a5565b348015610c0457600080fd5b50610372610c13366004615161565b6134ae565b610372610c263660046151ec565b613507565b61045d610c393660046153ca565b613577565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd000000000000000000000000000000000000000000000000000000001480610cd157507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610d1d57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b606060008054610d3290615739565b80601f0160208091040260200160405190810160405280929190818152602001828054610d5e90615739565b8015610dab5780601f10610d8057610100808354040283529160200191610dab565b820191906000526020600020905b815481529060010190602001808311610d8e57829003601f168201915b5050505050905090565b6000818152600960205260408120546001600160a01b0316610e445760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201527f697374656e7420746f6b656e000000000000000000000000000000000000000060648201526084015b60405180910390fd5b506000908152600360205260409020546001600160a01b031690565b6000610e6b82611aae565b9050806001600160a01b0316836001600160a01b03161415610ef55760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610e3b565b336001600160a01b0382161480610f115750610f118133610aa3565b610f835760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610e3b565b610f8d8383613729565b505050565b4715610fa257610fa233476137a4565b565b600854600090610fd3906001600160801b03700100000000000000000000000000000000820481169116615784565b6001600160801b0316905090565b610feb33826137e9565b61105d5760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f60448201527f776e6572206e6f7220617070726f7665640000000000000000000000000000006064820152608401610e3b565b610f8d8383836138f1565b7f000000000000000000000000000000000000000000000000000000000000000060006001600160a01b03821663aa5976c16110d78989604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b6040518263ffffffff1660e01b81526004016110f591815260200190565b6040805180830381865afa158015611111573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113591906157ac565b50905060ff81166112145761114c87878686613ae7565b816001600160a01b031663775dfc828888888861116833613be0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b03958616600482015294909316602485015262ffffff90911660448401526001600160801b03166064830152608482015260a4016020604051808303816000875af11580156111ee573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121291906157db565b505b61121e8787613c3c565b5050505050505050565b60008161123481613d6b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632ec31fbc8c8c8c61126f8d613da8565b611278906157f4565b89611283578b611285565b305b8a61129157600061129a565b61129a8d613be0565b8c6112a65760006112af565b6112af33613be0565b60408051336020820152016040516020818303038152906040526040518963ffffffff1660e01b81526004016112ec98979695949392919061582d565b60408051808303816000875af115801561130a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132e9190615889565b509150868211156113815760405162461bcd60e51b815260206004820152601260248201527f544f4f5f4d5543485f52455155455354454400000000000000000000000000006044820152606401610e3b565b509998505050505050505050565b60008161139b81613d6b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632ec31fbc8c8c8c6113d68d613da8565b896113e1578b6113e3565b305b8a6113ef5760006113f8565b6113f88d613be0565b8c61140457600061140d565b61140d33613be0565b60408051336020820152016040516020818303038152906040526040518963ffffffff1660e01b815260040161144a98979695949392919061582d565b60408051808303816000875af1158015611468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148c9190615889565b925050868210156113815760405162461bcd60e51b815260206004820152601360248201527f544f4f5f4c4954544c455f5245434549564544000000000000000000000000006044820152606401610e3b565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461151457600080fd5b600061152282840184615248565b9050841561153557611535878287613dde565b831561154657611546868286613dde565b50505050505050565b610f8d83838360405180602001604052806000815250612be1565b6040517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290526001600160a01b03871690638fcbaf0c90610104015b600060405180830381600087803b1580156115f857600080fd5b505af115801561160c573d6000803e3d6000fd5b50505050505050505050565b6006546001600160a01b0316331461162f57600080fd5b6006805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6000808080843561166e81613f91565b85356000908152600960209081526040808320805464ffffffffff740100000000000000000000000000000000000000008204168552600b84528285208351808501855281546001600160a01b039081168252600190920154821681870190815285516101208101875282518416815290519092168287015260ff79010000000000000000000000000000000000000000000000000084041682860152600160d01b8304600290810b6060840152600160e81b90930490920b608082015291949093909260a0830191611745918d01908d016158c7565b6bffffffffffffffffffffffff1681528a3560208201528454604090910190611776906001600160a01b0316613be0565b815260200161178b60c08c0160a08d016158e4565b1515905290506117a160e08a0160c08b016158e4565b611852576040517fdec980ec0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063dec980ec9061180a908490600401615901565b6080604051808303816000875af1158015611829573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184d91906159b4565b6118fa565b6040517fadcef3680000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063adcef368906118b7908490600401615901565b6080604051808303816000875af11580156118d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fa91906159b4565b929a50909850965094506040890135881080159061191c575088606001358710155b6119685760405162461bcd60e51b815260206004820152600e60248201527f507269636520736c6970706167650000000000000000000000000000000000006044820152606401610e3b565b600061197a60a08b0160808c01615248565b6001600160a01b0316146119ec576000611994878a6159ea565b905060006119a2878a6159ea565b905081156119c5576119c56119bd60a08d0160808e01615248565b85518461305c565b80156119e9576119e96119de60a08d0160808e01615248565b85602001518361305c565b50505b505050509193509193565b604080513360208083019190915282518083039091018152818301928390527faaa9acd2000000000000000000000000000000000000000000000000000000009092527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169163aaa9acd291611a8091889188918891889190604401615a02565b600060405180830381600087803b158015611a9a57600080fd5b505af115801561121e573d6000803e3d6000fd5b6000818152600960205260408120546001600160a01b031680610d1d5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201527f656e7420746f6b656e00000000000000000000000000000000000000000000006064820152608401610e3b565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611b6e57600080fd5b8215611b8a57611b8a84611b8483850185615248565b85613dde565b50505050565b60006001600160a01b038216611c0e5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a6560448201527f726f2061646472657373000000000000000000000000000000000000000000006064820152608401610e3b565b506001600160a01b031660009081526002602052604090205490565b42841015611c7a5760405162461bcd60e51b815260206004820152600e60248201527f5065726d697420457870697265640000000000000000000000000000000000006044820152606401610e3b565b6000611d24604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b600087815260076020526040812080547f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad928b928b9291611d6483615a3a565b909155506040805160208101959095526001600160a01b03909316928401929092526060830152608082015260a0810187905260c00160405160208183030381529060405280519060200120604051602001611df29291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b6040516020818303038152906040528051906020012090506000611e1587611aae565b9050803b15611f6a57604080516020810186905280820185905260f887901b7fff000000000000000000000000000000000000000000000000000000000000001660608201528151604181830301815260618201928390527f1626ba7e000000000000000000000000000000000000000000000000000000009092526001600160a01b03831691631626ba7e91611eb0918691606501615a55565b602060405180830381865afa158015611ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef19190615a6e565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916631626ba7e60e01b14611f655760405162461bcd60e51b815260206004820152600c60248201527f556e617574686f72697a656400000000000000000000000000000000000000006044820152606401610e3b565b612084565b6040805160008082526020820180845285905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611fbe573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166120215760405162461bcd60e51b815260206004820152601160248201527f496e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610e3b565b816001600160a01b0316816001600160a01b0316146120825760405162461bcd60e51b815260206004820152600c60248201527f556e617574686f72697a656400000000000000000000000000000000000000006044820152606401610e3b565b505b61121e8888613729565b60008080806120ad6120a861014087016101208801615248565b613fe7565b935060006040518060a00160405280876101200160208101906120d09190615248565b6001600160a01b03168152602090810190612106906120f1908a018a615248565b61210160408b0160208c01615248565b613c3c565b64ffffffffff1681526020016121226060890160408a01615a8b565b60ff16815260200161213a6080890160608a01615ab7565b60020b815260200161215260a0890160808a01615ab7565b60020b9052600086815260096020908152604091829020835181548584015186860151606088015160808901516001600160a01b039095167fffffffffffffff00000000000000000000000000000000000000000000000000909416939093177401000000000000000000000000000000000000000064ffffffffff90931692909202919091177fffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffff1679010000000000000000000000000000000000000000000000000060ff909216919091027fffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffff1617600160d01b62ffffff92831602177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e81b919092160217905581518083019092529192506122f891839190819061229c908b018b615248565b6001600160a01b031681526020018960200160208101906122bd9190615248565b6001600160a01b0316815250878960a001358a60c001358b60e001358c61010001358d6101400160208101906122f391906158e4565b614016565b969891975095945092505050565b7f000000000000000000000000000000000000000000000000000000000000000060006001600160a01b03821663dc6574656123758989604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815260048101919091526000602482015260440161010060405180830381865afa1580156123d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f59190615b0f565b60200151905061240787878387613ae7565b6000826001600160a01b031663c349e76989898961242433613be0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b1681526001600160a01b03948516600482015293909216602484015262ffffff16604483015260648201526084016020604051808303816000875af115801561249a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124be9190615bd9565b90508360ff168160ff1614806124d6575060ff848116145b6124df57600080fd5b6124e98888613c3c565b505050505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663aaa9acd23061252d86613be0565b6040805133602082015287918791016040516020818303038152906040526040518663ffffffff1660e01b815260040161256b959493929190615a02565b600060405180830381600087803b15801561258557600080fd5b505af1158015611546573d6000803e3d6000fd5b606060018054610d3290615739565b6001600160a01b0382163314156126015760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610e3b565b3360008181526004602090815260408083206001600160a01b0387168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60008161269781613d6b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c6883ec56040518060c001604052808d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506020016127168c613da8565b61271f906157f4565b81526020018761272f5789612731565b305b6001600160a01b031681526020018761274b576000612754565b6127548a613be0565b81526020018861276557600061276e565b61276e33613be0565b8152604080513360208281019190915290920191016040516020818303038152906040528152506040518263ffffffff1660e01b81526004016127b19190615bf6565b60408051808303816000875af11580156127cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f39190615889565b509150868211156128465760405162461bcd60e51b815260206004820152601260248201527f544f4f5f4d5543485f52455155455354454400000000000000000000000000006044820152606401610e3b565b5098975050505050505050565b60608167ffffffffffffffff81111561286e5761286e61556a565b6040519080825280602002602001820160405280156128a157816020015b606081526020019060019003908161288c5790505b50905060005b8281101561296a57600080308686858181106128c5576128c5615c69565b90506020028101906128d79190615c7f565b6040516128e5929190615ce4565b600060405180830381855af49150503d8060008114612920576040519150601f19603f3d011682016040523d82523d6000602084013e612925565b606091505b50915091508161294257805161293a57600080fd5b805181602001fd5b8084848151811061295557612955615c69565b602090810291909101015250506001016128a7565b5092915050565b6006546001600160a01b0316331461298857600080fd5b6005805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b60005b81811015610f8d5760008383838181106129d6576129d6615c69565b9050602002013590506129e881613f91565b6000818152600960209081526040808320805464ffffffffff740100000000000000000000000000000000000000008204168552600b84528285208054600182015485516001600160a01b03928316818901529082168187015285518082038701815260608201808852815191909801207fc625e3c300000000000000000000000000000000000000000000000000000000909752606481019690965230608487015260a4860188905260ff79010000000000000000000000000000000000000000000000000084041660c4870152600160d01b8304600290810b60e4880152600160e81b90930490920b6101048601529251919492937f00000000000000000000000000000000000000000000000000000000000000009091169163c625e3c3916101248082019260a0929091908290030181865afa158015612b30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b549190615cf4565b80519091506bffffffffffffffffffffffff1615612bb45760405162461bcd60e51b815260206004820152600960248201527f4e4f545f454d50545900000000000000000000000000000000000000000000006044820152606401610e3b565b612bbd846142e3565b50505060009081526009602052604081205580612bd981615a3a565b9150506129ba565b612beb33836137e9565b612c5d5760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f60448201527f776e6572206e6f7220617070726f7665640000000000000000000000000000006064820152608401610e3b565b611b8a84848484614397565b6000818152600960205260409020546060906001600160a01b0316612cd05760405162461bcd60e51b815260206004820152600f60248201527f746f6b656e206e6f7420657869737400000000000000000000000000000000006044820152606401610e3b565b6005546001600160a01b0316612cf55760405180602001604052806000815250610d1d565b6005546040517fe9dc6375000000000000000000000000000000000000000000000000000000008152306004820152602481018490526001600160a01b039091169063e9dc637590604401600060405180830381865afa158015612d5d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d1d9190810190615d91565b600080808335612d9481613f91565b600060096000876000013581526020019081526020016000206040518060a00160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016000820160149054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681526020016000820160199054906101000a900460ff1660ff1660ff16815260200160008201601a9054906101000a900460020b60020b60020b815260200160008201601d9054906101000a900460020b60020b60020b815250509050612f2781600b6000846020015164ffffffffff1664ffffffffff1681526020019081526020016000206040518060400160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681525050886000013589602001358a604001358b606001358c608001358d60a00160208101906122f391906158e4565b91989097509095509350505050565b81612f4a576008546001600160801b031691505b612f5382613f91565b6000828152600960209081526040808320805464ffffffffff740100000000000000000000000000000000000000008204168552600b909352928190208054600182015492517f8e9059d60000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152928116602484015260ff790100000000000000000000000000000000000000000000000000850481166044850152600160d01b8504600290810b6064860152600160e81b90950490940b608484015260a4830187905292851660c4830152917f00000000000000000000000000000000000000000000000000000000000000001690638e9059d69060e401611a80565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634b2084e38461309533613be0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b039283166004820152602481019190915290851660448201526064810184905260840161256b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613170573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319491906157db565b9050828110156131e65760405162461bcd60e51b815260206004820152601160248201527f496e73756666696369656e7420574554480000000000000000000000000000006044820152606401610e3b565b8015610f8d576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561326757600080fd5b505af115801561327b573d6000803e3d6000fd5b50505050610f8d82826137a4565b6040805160a0810182526000808252602080830182905282840182905260608301829052608083018290528482526009905291822080546001600160a01b03811693928392790100000000000000000000000000000000000000000000000000830460ff1692600160d01b8104600290810b93600160e81b909204900b91876133545760405162461bcd60e51b815260206004820152600a60248201527f4e4f545f455849535453000000000000000000000000000000000000000000006044820152606401610e3b565b805474010000000000000000000000000000000000000000900464ffffffffff166000908152600b60205260409020805460018201546001600160a01b039182169950811697507f00000000000000000000000000000000000000000000000000000000000000001663c625e3c36133ff8a8a604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152306024820152604481018d905260ff89166064820152600288810b608483015287900b60a482015260c40160a060405180830381865afa15801561347b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349f9190615cf4565b92505050919395979092949650565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146134e357600080fd5b83156134ff576134ff866134f983850185615248565b86613dde565b505050505050565b6040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018690526064810185905260ff8416608482015260a4810183905260c481018290526001600160a01b0387169063d505accf9060e4016115de565b60008161358381613d6b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c6883ec56040518060c001604052808d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506020016136028c613da8565b8152602001876136125789613614565b305b6001600160a01b031681526020018761362e576000613637565b6136378a613be0565b815260200188613648576000613651565b61365133613be0565b8152604080513360208281019190915290920191016040516020818303038152906040528152506040518263ffffffff1660e01b81526004016136949190615bf6565b60408051808303816000875af11580156136b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136d69190615889565b925050868210156128465760405162461bcd60e51b815260206004820152601360248201527f544f4f5f4c4954544c455f5245434549564544000000000000000000000000006044820152606401610e3b565b6000818152600360205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038416908117909155819061376b82611aae565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080600080600085875af1905080610f8d576040517fe293b56b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600960205260408120546001600160a01b03166138735760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201527f697374656e7420746f6b656e00000000000000000000000000000000000000006064820152608401610e3b565b600061387e83611aae565b9050806001600160a01b0316846001600160a01b031614806138b95750836001600160a01b03166138ae84610db5565b6001600160a01b0316145b806138e957506001600160a01b0380821660009081526004602090815260408083209388168352929052205460ff165b949350505050565b826001600160a01b031661390482611aae565b6001600160a01b0316146139805760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201527f73206e6f74206f776e00000000000000000000000000000000000000000000006064820152608401610e3b565b6001600160a01b0382166139fb5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610e3b565b613a06838383614420565b613a11600082613729565b6001600160a01b0383166000908152600260205260408120805460019290613a3a908490615dff565b90915550506001600160a01b0382166000908152600260205260408120805460019290613a689084906159ea565b90915550506000818152600960205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905580826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6000613b0d6a64000000000000000000006001600160801b038516808204910615150190565b90506000613b3660646001600160801b0386160268010000000000000000808204910615150190565b90508215613bca57600030613b4a33613be0565b604080516001600160a01b0390931660208401528201526060016040516020818303038152906040528051906020012090506000613b8888836144d2565b90506000613b9688846144d2565b905081851115613bad57613bad338a8488036124f4565b80841115613bc257613bc233898387036124f4565b5050506134ff565b613bd53387846124f4565b6134ff3386836124f4565b6001600160a01b03811680613c375760405162461bcd60e51b815260206004820152600f60248201527f5a45524f5f4143435f5245465f494400000000000000000000000000000000006044820152606401610e3b565b919050565b604080516001600160a01b0384811660208084019190915290841682840152825180830384018152606090920183528151918101919091206000818152600c9092529190205464ffffffffff16908161296a57600a805464ffffffffff16906000613ca683615e16565b82546101009290920a64ffffffffff8181021990931691831602179091556000928352600c6020908152604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169385169384179055805180820182526001600160a01b039889168152968816878301908152928552600b9091529092209351845473ffffffffffffffffffffffffffffffffffffffff199081169187169190911785559151600194909401805490921693909416929092179091555090565b80421115613da5576040517f70f65caa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115613dda57613dda615e3b565b5090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316148015613e1f5750804710155b15613f65577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613e7f57600080fd5b505af1158015613e93573d6000803e3d6000fd5b50506040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af1158015613f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8a9190615e51565b610f8d83837f000000000000000000000000000000000000000000000000000000000000000084614588565b613f9b33826137e9565b613da55760405162461bcd60e51b815260206004820152600c60248201527f4e4f545f415050524f56454400000000000000000000000000000000000000006044820152606401610e3b565b600854600090614001906001600160801b03166001615e6e565b6001600160801b03169050613c378282614628565b60008060006141277f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dc6574656140928d600001518e60200151604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b8e604001516040518363ffffffff1660e01b81526004016140c092919091825260ff16602082015260400190565b61010060405180830381865afa1580156140de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141029190615b0f565b602001516141138d6060015161478d565b6141208e6080015161478d565b8b8b614ac7565b92507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632fb07eac6040518061014001604052808d600001516001600160a01b031681526020018d602001516001600160a01b031681526020018e6040015160ff1681526020018e6060015160020b81526020018e6080015160020b8152602001866bffffffffffffffffffffffff168152602001306001600160a01b031681526020018c8152602001876141e65760006141ef565b6141ef33613be0565b8152604080513360208281019190915290920191016040516020818303038152906040528152506040518263ffffffff1660e01b81526004016142329190615e99565b60408051808303816000875af1158015614250573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142749190615889565b90925090508582108015906142895750848110155b6142d55760405162461bcd60e51b815260206004820152600e60248201527f507269636520736c6970706167650000000000000000000000000000000000006044820152606401610e3b565b985098509895505050505050565b60006142ee82611aae565b90506142fc81600084614420565b614307600083613729565b6001600160a01b0381166000908152600260205260408120805460019290614330908490615dff565b9091555050600082815260096020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6143a28484846138f1565b6143ae84848484614be3565b611b8a5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610e3b565b6001600160a01b03831661446d57600880546001600160801b031690600061444783615f6c565b91906101000a8154816001600160801b0302191690836001600160801b03160217905550505b6001600160a01b038216610f8d576008805470010000000000000000000000000000000090046001600160801b03169060106144a883615f6c565b91906101000a8154816001600160801b0302191690836001600160801b0316021790555050505050565b6040517f7266a0e40000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152602482018390526000917f000000000000000000000000000000000000000000000000000000000000000090911690637266a0e490604401602060405180830381865afa15801561455d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061458191906157db565b9392505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526001600160a01b038416602482015282604482015260008060648360008a5af19150506145eb81614d79565b614621576040517fa83c475400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6001600160a01b03821661467e5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610e3b565b6000818152600960205260409020546001600160a01b0316156146e35760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610e3b565b6146ef60008383614420565b6001600160a01b03821660009081526002602052604081208054600192906147189084906159ea565b90915550506000818152600960205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905560405181906001600160a01b038416906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000600282900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755138015906147ca5750620bd8ab600283900b13155b6147d357600080fd5b6000808360020b126147e557826147ea565b826000035b62ffffff811691507001000000000000000000000000000000009060011615614823576ffffcb933bd6fad37aa2d162d1a5940010260801c5b6002821615614842576ffff97272373d413259a46990580e213a0260801c5b6004821615614861576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614880576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b601082161561489f576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156148be576fff973b41fa98c081472e6896dfb254c00260801c5b60408216156148dd576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156148fc576ffe5dee046a99a2a811c461f1969c30530260801c5b61010082161561491c576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b61020082161561493c576ff987a7253ac413176f2b074cf7815e540260801c5b61040082161561495c576ff3392b0822b70005940c7a398e4b70f30260801c5b61080082161561497c576fe7159475a2c29b7443b29c7fa6e889d90260801c5b61100082161561499c576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156149bc576fa9f746462d870fdf8a65dc1f90e061e50260801c5b6140008216156149dc576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156149fc576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615614a1d576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615614a3d576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614a5c576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614a79576b048a170391f7dc42444e8fa20260801c5b60008460020b12614a99578060001981614a9557614a95615f89565b0490505b6000670100000000000000820611614ab2576000614ab5565b60015b60ff16603882901c0192505050919050565b600080856001600160801b0316876001600160801b031611614b2057614b1984866001600160801b0316886001600160801b03160269010000000000000000008989036001600160801b031602614dbe565b9050614bcb565b846001600160801b0316876001600160801b031610614b5957614b198369010000000000000000008888036001600160801b0316614dbe565b6000614b9185876001600160801b03168a6001600160801b03160269010000000000000000008b8a036001600160801b031602614dbe565b90506000614bb58569010000000000000000008a8c036001600160801b0316614dbe565b9050808210614bc45780614bc6565b815b925050505b614bd8600882901c614e6c565b979650505050505050565b60006001600160a01b0384163b15614d6e576040517f150b7a020000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063150b7a0290614c40903390899088908890600401615f9f565b6020604051808303816000875af1925050508015614c7b575060408051601f3d908101601f19168201909252614c7891810190615a6e565b60015b614d23573d808015614ca9576040519150601f19603f3d011682016040523d82523d6000602084013e614cae565b606091505b50805161293a5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610e3b565b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001490506138e9565b506001949350505050565b600081614d8a573d6000803e3d6000fd5b3d60208114614da2578015614db35760009150614db8565b3d6000803e60005115159150614db8565b600191505b50919050565b600080806000198587098587029250828110838203039150508060001415614df85760008411614ded57600080fd5b508290049050614581565b808411614e0457600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b60006bffffffffffffffffffffffff821115613dda57613dda615e3b565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114613da557600080fd5b600060208284031215614eca57600080fd5b813561458181614e8a565b60005b83811015614ef0578181015183820152602001614ed8565b83811115611b8a5750506000910152565b60008151808452614f19816020860160208601614ed5565b601f01601f19169290920160200192915050565b6020815260006145816020830184614f01565b600060208284031215614f5257600080fd5b5035919050565b6001600160a01b0381168114613da557600080fd5b60008060408385031215614f8157600080fd5b8235614f8c81614f59565b946020939093013593505050565b600080600060608486031215614faf57600080fd5b8335614fba81614f59565b92506020840135614fca81614f59565b929592945050506040919091013590565b62ffffff81168114613da557600080fd5b6001600160801b0381168114613da557600080fd5b8015158114613da557600080fd5b600080600080600060a0868803121561502757600080fd5b853561503281614f59565b9450602086013561504281614f59565b9350604086013561505281614fdb565b9250606086013561506281614fec565b9150608086013561507281615001565b809150509295509295909350565b60008060008060008060008060006101208a8c03121561509f57600080fd5b89356150aa81614f59565b985060208a01356150ba81614f59565b975060408a0135965060608a0135955060808a0135945060a08a01356150df81614f59565b935060c08a01356150ef81615001565b925060e08a01356150ff81615001565b809250506101008a013590509295985092959850929598565b60008083601f84011261512a57600080fd5b50813567ffffffffffffffff81111561514257600080fd5b60208301915083602082850101111561515a57600080fd5b9250929050565b60008060008060008060a0878903121561517a57600080fd5b863561518581614f59565b9550602087013561519581614f59565b94506040870135935060608701359250608087013567ffffffffffffffff8111156151bf57600080fd5b6151cb89828a01615118565b979a9699509497509295939492505050565b60ff81168114613da557600080fd5b60008060008060008060c0878903121561520557600080fd5b863561521081614f59565b95506020870135945060408701359350606087013561522e816151dd565b9598949750929560808101359460a0909101359350915050565b60006020828403121561525a57600080fd5b813561458181614f59565b600060e08284031215614db857600080fd5b6000806000806080858703121561528d57600080fd5b843561529881614f59565b93506020850135925060408501356152af81614f59565b9396929550929360600135925050565b600080600080606085870312156152d557600080fd5b84356152e081614f59565b935060208501359250604085013567ffffffffffffffff81111561530357600080fd5b61530f87828801615118565b95989497509550505050565b60006101608284031215614db857600080fd5b600080600080600060a0868803121561534657600080fd5b853561535181614f59565b9450602086013561536181614f59565b9350604086013561537181614fdb565b9250606086013561538181615001565b91506080860135615072816151dd565b600080604083850312156153a457600080fd5b82356153af81614f59565b915060208301356153bf81615001565b809150509250929050565b60008060008060008060008060e0898b0312156153e657600080fd5b883567ffffffffffffffff8111156153fd57600080fd5b6154098b828c01615118565b9099509750506020890135955060408901359450606089013561542b81614f59565b9350608089013561543b81615001565b925060a089013561544b81615001565b8092505060c089013590509295985092959890939650565b60008083601f84011261547557600080fd5b50813567ffffffffffffffff81111561548d57600080fd5b6020830191508360208260051b850101111561515a57600080fd5b600080602083850312156154bb57600080fd5b823567ffffffffffffffff8111156154d257600080fd5b6154de85828601615463565b90969095509350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561555d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261554b858351614f01565b94509285019290850190600101615511565b5092979650505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156155a9576155a961556a565b604052919050565b600067ffffffffffffffff8211156155cb576155cb61556a565b50601f01601f191660200190565b600080600080608085870312156155ef57600080fd5b84356155fa81614f59565b9350602085013561560a81614f59565b925060408501359150606085013567ffffffffffffffff81111561562d57600080fd5b8501601f8101871361563e57600080fd5b803561565161564c826155b1565b615580565b81815288602083850101111561566657600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b600060c08284031215614db857600080fd5b600080604083850312156156ad57600080fd5b8235915060208301356153bf816151dd565b600080604083850312156156d257600080fd5b8235915060208301356153bf81614f59565b600080604083850312156156f757600080fd5b823561570281614f59565b915060208301356153bf81614f59565b60006020828403121561572457600080fd5b813564ffffffffff8116811461458157600080fd5b600181811c9082168061574d57607f821691505b60208210811415614db857634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160801b03838116908316818110156157a4576157a461576e565b039392505050565b600080604083850312156157bf57600080fd5b82516157ca816151dd565b60208401519092506153bf816151dd565b6000602082840312156157ed57600080fd5b5051919050565b60007f80000000000000000000000000000000000000000000000000000000000000008214156158265761582661576e565b5060000390565b60006101006001600160a01b03808c168452808b1660208501528960408501528860608501528088166080850152508560a08401528460c08401528060e084015261587a81840185614f01565b9b9a5050505050505050505050565b6000806040838503121561589c57600080fd5b505080516020909101519092909150565b6bffffffffffffffffffffffff81168114613da557600080fd5b6000602082840312156158d957600080fd5b8135614581816158ad565b6000602082840312156158f657600080fd5b813561458181615001565b6000610120820190506001600160a01b03808451168352806020850151166020840152506040830151615939604084018260ff169052565b50606083015161594e606084018260020b9052565b506080830151615963608084018260020b9052565b5060a083015161598360a08401826bffffffffffffffffffffffff169052565b5060c083015160c083015260e083015160e0830152610100808401516159ac8285018215159052565b505092915050565b600080600080608085870312156159ca57600080fd5b505082516020840151604085015160609095015191969095509092509050565b600082198211156159fd576159fd61576e565b500190565b60006001600160a01b03808816835286602084015280861660408401525083606083015260a06080830152614bd860a0830184614f01565b6000600019821415615a4e57615a4e61576e565b5060010190565b8281526040602082015260006138e96040830184614f01565b600060208284031215615a8057600080fd5b815161458181614e8a565b600060208284031215615a9d57600080fd5b8135614581816151dd565b8060020b8114613da557600080fd5b600060208284031215615ac957600080fd5b813561458181615aa8565b8051613c3781614fec565b8051613c3781614fdb565b8051613c3781615aa8565b805169ffffffffffffffffffff81168114613c3757600080fd5b6000610100808385031215615b2357600080fd5b6040519081019067ffffffffffffffff82118183101715615b4657615b4661556a565b8160405283519150615b5782614fec565b818152615b6660208501615ad4565b6020820152615b7760408501615adf565b6040820152615b8860608501615aea565b6060820152615b9960808501615aea565b6080820152615baa60a08501615aea565b60a0820152615bbb60c08501615af5565b60c0820152615bcc60e08501615af5565b60e0820152949350505050565b600060208284031215615beb57600080fd5b8151614581816151dd565b602081526000825160c06020840152615c1260e0840182614f01565b9050602084015160408401526001600160a01b03604085015116606084015260608401516080840152608084015160a084015260a0840151601f198483030160c0850152615c608282614f01565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615cb457600080fd5b83018035915067ffffffffffffffff821115615ccf57600080fd5b60200191503681900382131561515a57600080fd5b8183823760009101908152919050565b600060a08284031215615d0657600080fd5b60405160a0810181811067ffffffffffffffff82111715615d2957615d2961556a565b6040528251615d37816158ad565b8152615d4560208401615af5565b6020820152615d5660408401615af5565b60408201526060830151615d69816151dd565b6060820152608083015163ffffffff81168114615d8557600080fd5b60808201529392505050565b600060208284031215615da357600080fd5b815167ffffffffffffffff811115615dba57600080fd5b8201601f81018413615dcb57600080fd5b8051615dd961564c826155b1565b818152856020838501011115615dee57600080fd5b615c60826020830160208601614ed5565b600082821015615e1157615e1161576e565b500390565b600064ffffffffff80831681811415615e3157615e3161576e565b6001019392505050565b634e487b7160e01b600052600160045260246000fd5b600060208284031215615e6357600080fd5b815161458181615001565b60006001600160801b03808316818516808303821115615e9057615e9061576e565b01949350505050565b60208152615eb36020820183516001600160a01b03169052565b60006020830151615ecf60408401826001600160a01b03169052565b50604083015160ff81166060840152506060830151615ef3608084018260020b9052565b506080830151615f0860a084018260020b9052565b5060a08301516bffffffffffffffffffffffff811660c08401525060c08301516001600160a01b03811660e08401525060e08301516101008381019190915283015161012080840191909152830151610140808401526138e9610160840182614f01565b60006001600160801b0380831681811415615e3157615e3161576e565b634e487b7160e01b600052601260045260246000fd5b60006001600160a01b03808716835280861660208401525083604083015260806060830152615fd16080830184614f01565b969550505050505056fea164736f6c634300080a000a0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd8000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Deployed Bytecode
0x6080604052600436106103385760003560e01c806370a08231116101b0578063b88d4fde116100ec578063e16d9ce511610095578063f0db800c1161006f578063f0db800c14610b97578063f1371dd514610bf8578063f3995c6714610c18578063f5a9817514610c2b57600080fd5b8063e16d9ce514610a75578063e985e9c514610a88578063eb02c30114610ad157600080fd5b8063ce3fa4dd116100c6578063ce3fa4dd14610a12578063d9899ff014610a4f578063d9caed1214610a6257600080fd5b8063b88d4fde146108ff578063c2814b0c1461091f578063c87b56dd146109f257600080fd5b806395d89b4111610159578063ac9650d811610133578063ac9650d81461088c578063ae4bf64b146108ac578063b6dc7ea3146108cc578063b80f55c9146108ec57600080fd5b806395d89b4114610844578063a22cb46514610859578063a73970cf1461087957600080fd5b80638137cdbf1161018a5780638137cdbf146108005780638340f549146108135780638c0e83491461082657600080fd5b806370a082311461078d5780637ac2ff7b146107ad5780637c2d10fd146107c057600080fd5b806330adf81f1161027f5780634aa4a4fc116102285780635a9d7a68116102025780635a9d7a681461071a5780635dd582041461073a5780636352211e1461074d578063641229d91461076d57600080fd5b80634aa4a4fc146106935780634f594557146106c75780635325261c146106e757600080fd5b80633d705707116102595780633d7057071461064057806342842e0e146106605780634659a4941461068057600080fd5b806330adf81f146105235780633644e51514610557578063365a86fc1461060c57600080fd5b806318160ddd116102e15780632809a659116102bb5780632809a659146104b35780632e85d3ae146104c65780632f8bc1ec146104d957600080fd5b806318160ddd1461046b57806323b872dd146104805780632513f7f2146104a057600080fd5b8063095ea7b311610312578063095ea7b31461040857806312210e8a14610428578063141a468c1461043057600080fd5b806301ffc9a71461037957806306fdde03146103ae578063081812fc146103d057600080fd5b3661037457336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2161461037257600080fd5b005b600080fd5b34801561038557600080fd5b50610399610394366004614eb8565b610c3e565b60405190151581526020015b60405180910390f35b3480156103ba57600080fd5b506103c3610d23565b6040516103a59190614f2d565b3480156103dc57600080fd5b506103f06103eb366004614f40565b610db5565b6040516001600160a01b0390911681526020016103a5565b34801561041457600080fd5b50610372610423366004614f6e565b610e60565b610372610f92565b34801561043c57600080fd5b5061045d61044b366004614f40565b60076020526000908152604090205481565b6040519081526020016103a5565b34801561047757600080fd5b5061045d610fa4565b34801561048c57600080fd5b5061037261049b366004614f9a565b610fe1565b6103726104ae36600461500f565b611068565b61045d6104c1366004615080565b611228565b61045d6104d4366004615080565b61138f565b3480156104e557600080fd5b5061050d6104f4366004614f40565b600c6020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016103a5565b34801561052f57600080fd5b5061045d7f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad81565b34801561056357600080fd5b5061045d604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f9fe3a2e4ddb6d63a930c14ff848e1cc7193e662da2d961c245e0daa99178ddf5918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b34801561061857600080fd5b506103f07f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd881565b34801561064c57600080fd5b5061037261065b366004615161565b6114df565b34801561066c57600080fd5b5061037261067b366004614f9a565b61154f565b61037261068e3660046151ec565b61156a565b34801561069f57600080fd5b506103f07f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b3480156106d357600080fd5b506103726106e2366004615248565b611618565b6106fa6106f5366004615265565b61165e565b6040805194855260208501939093529183015260608201526080016103a5565b34801561072657600080fd5b506005546103f0906001600160a01b031681565b610372610748366004615277565b6119f7565b34801561075957600080fd5b506103f0610768366004614f40565b611aae565b34801561077957600080fd5b506103726107883660046152bf565b611b39565b34801561079957600080fd5b5061045d6107a8366004615248565b611b90565b6103726107bb3660046151ec565b611c2a565b6107d36107ce36600461531b565b61208e565b604080519485526bffffffffffffffffffffffff90931660208501529183015260608201526080016103a5565b61037261080e36600461532e565b612306565b610372610821366004614f9a565b6124f4565b34801561083257600080fd5b506008546001600160801b031661045d565b34801561085057600080fd5b506103c3612599565b34801561086557600080fd5b50610372610874366004615391565b6125a8565b61045d6108873660046153ca565b61268b565b61089f61089a3660046154a8565b612853565b6040516103a591906154ea565b3480156108b857600080fd5b506006546103f0906001600160a01b031681565b3480156108d857600080fd5b506103726108e7366004615248565b612971565b6103726108fa3660046154a8565b6129b7565b34801561090b57600080fd5b5061037261091a3660046155d9565b612be1565b34801561092b57600080fd5b506109ae61093a366004614f40565b6009602052600090815260409020546001600160a01b0381169064ffffffffff740100000000000000000000000000000000000000008204169060ff79010000000000000000000000000000000000000000000000000082041690600160d01b8104600290810b91600160e81b9004900b85565b604080516001600160a01b03909616865264ffffffffff909416602086015260ff90921692840192909252600291820b6060840152900b608082015260a0016103a5565b3480156109fe57600080fd5b506103c3610a0d366004614f40565b612c69565b610a25610a20366004615688565b612d85565b604080516bffffffffffffffffffffffff90941684526020840192909252908201526060016103a5565b610372610a5d36600461569a565b612f36565b610372610a70366004614f9a565b61305c565b610372610a833660046156bf565b6130f0565b348015610a9457600080fd5b50610399610aa33660046156e4565b6001600160a01b03918216600090815260046020908152604080832093909416825291909152205460ff1690565b348015610add57600080fd5b50610af1610aec366004614f40565b613289565b604080516001600160a01b039889168152968816602080890191909152979095168686015260ff938416606080880191909152600293840b6080808901919091529290930b60a087015280516bffffffffffffffffffffffff1660c08701529586015169ffffffffffffffffffff90811660e087015293860151909316610100850152840151166101208301529091015163ffffffff16610140820152610160016103a5565b348015610ba357600080fd5b50610bd8610bb2366004615712565b600b60205260009081526040902080546001909101546001600160a01b03918216911682565b604080516001600160a01b039384168152929091166020830152016103a5565b348015610c0457600080fd5b50610372610c13366004615161565b6134ae565b610372610c263660046151ec565b613507565b61045d610c393660046153ca565b613577565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd000000000000000000000000000000000000000000000000000000001480610cd157507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610d1d57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b606060008054610d3290615739565b80601f0160208091040260200160405190810160405280929190818152602001828054610d5e90615739565b8015610dab5780601f10610d8057610100808354040283529160200191610dab565b820191906000526020600020905b815481529060010190602001808311610d8e57829003601f168201915b5050505050905090565b6000818152600960205260408120546001600160a01b0316610e445760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201527f697374656e7420746f6b656e000000000000000000000000000000000000000060648201526084015b60405180910390fd5b506000908152600360205260409020546001600160a01b031690565b6000610e6b82611aae565b9050806001600160a01b0316836001600160a01b03161415610ef55760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610e3b565b336001600160a01b0382161480610f115750610f118133610aa3565b610f835760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610e3b565b610f8d8383613729565b505050565b4715610fa257610fa233476137a4565b565b600854600090610fd3906001600160801b03700100000000000000000000000000000000820481169116615784565b6001600160801b0316905090565b610feb33826137e9565b61105d5760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f60448201527f776e6572206e6f7220617070726f7665640000000000000000000000000000006064820152608401610e3b565b610f8d8383836138f1565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd860006001600160a01b03821663aa5976c16110d78989604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b6040518263ffffffff1660e01b81526004016110f591815260200190565b6040805180830381865afa158015611111573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113591906157ac565b50905060ff81166112145761114c87878686613ae7565b816001600160a01b031663775dfc828888888861116833613be0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b03958616600482015294909316602485015262ffffff90911660448401526001600160801b03166064830152608482015260a4016020604051808303816000875af11580156111ee573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121291906157db565b505b61121e8787613c3c565b5050505050505050565b60008161123481613d6b565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b0316632ec31fbc8c8c8c61126f8d613da8565b611278906157f4565b89611283578b611285565b305b8a61129157600061129a565b61129a8d613be0565b8c6112a65760006112af565b6112af33613be0565b60408051336020820152016040516020818303038152906040526040518963ffffffff1660e01b81526004016112ec98979695949392919061582d565b60408051808303816000875af115801561130a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132e9190615889565b509150868211156113815760405162461bcd60e51b815260206004820152601260248201527f544f4f5f4d5543485f52455155455354454400000000000000000000000000006044820152606401610e3b565b509998505050505050505050565b60008161139b81613d6b565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b0316632ec31fbc8c8c8c6113d68d613da8565b896113e1578b6113e3565b305b8a6113ef5760006113f8565b6113f88d613be0565b8c61140457600061140d565b61140d33613be0565b60408051336020820152016040516020818303038152906040526040518963ffffffff1660e01b815260040161144a98979695949392919061582d565b60408051808303816000875af1158015611468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148c9190615889565b925050868210156113815760405162461bcd60e51b815260206004820152601360248201527f544f4f5f4c4954544c455f5245434549564544000000000000000000000000006044820152606401610e3b565b336001600160a01b037f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd8161461151457600080fd5b600061152282840184615248565b9050841561153557611535878287613dde565b831561154657611546868286613dde565b50505050505050565b610f8d83838360405180602001604052806000815250612be1565b6040517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290526001600160a01b03871690638fcbaf0c90610104015b600060405180830381600087803b1580156115f857600080fd5b505af115801561160c573d6000803e3d6000fd5b50505050505050505050565b6006546001600160a01b0316331461162f57600080fd5b6006805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6000808080843561166e81613f91565b85356000908152600960209081526040808320805464ffffffffff740100000000000000000000000000000000000000008204168552600b84528285208351808501855281546001600160a01b039081168252600190920154821681870190815285516101208101875282518416815290519092168287015260ff79010000000000000000000000000000000000000000000000000084041682860152600160d01b8304600290810b6060840152600160e81b90930490920b608082015291949093909260a0830191611745918d01908d016158c7565b6bffffffffffffffffffffffff1681528a3560208201528454604090910190611776906001600160a01b0316613be0565b815260200161178b60c08c0160a08d016158e4565b1515905290506117a160e08a0160c08b016158e4565b611852576040517fdec980ec0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd8169063dec980ec9061180a908490600401615901565b6080604051808303816000875af1158015611829573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184d91906159b4565b6118fa565b6040517fadcef3680000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd8169063adcef368906118b7908490600401615901565b6080604051808303816000875af11580156118d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fa91906159b4565b929a50909850965094506040890135881080159061191c575088606001358710155b6119685760405162461bcd60e51b815260206004820152600e60248201527f507269636520736c6970706167650000000000000000000000000000000000006044820152606401610e3b565b600061197a60a08b0160808c01615248565b6001600160a01b0316146119ec576000611994878a6159ea565b905060006119a2878a6159ea565b905081156119c5576119c56119bd60a08d0160808e01615248565b85518461305c565b80156119e9576119e96119de60a08d0160808e01615248565b85602001518361305c565b50505b505050509193509193565b604080513360208083019190915282518083039091018152818301928390527faaa9acd2000000000000000000000000000000000000000000000000000000009092527f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b03169163aaa9acd291611a8091889188918891889190604401615a02565b600060405180830381600087803b158015611a9a57600080fd5b505af115801561121e573d6000803e3d6000fd5b6000818152600960205260408120546001600160a01b031680610d1d5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201527f656e7420746f6b656e00000000000000000000000000000000000000000000006064820152608401610e3b565b336001600160a01b037f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd81614611b6e57600080fd5b8215611b8a57611b8a84611b8483850185615248565b85613dde565b50505050565b60006001600160a01b038216611c0e5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a6560448201527f726f2061646472657373000000000000000000000000000000000000000000006064820152608401610e3b565b506001600160a01b031660009081526002602052604090205490565b42841015611c7a5760405162461bcd60e51b815260206004820152600e60248201527f5065726d697420457870697265640000000000000000000000000000000000006044820152606401610e3b565b6000611d24604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f9fe3a2e4ddb6d63a930c14ff848e1cc7193e662da2d961c245e0daa99178ddf5918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b600087815260076020526040812080547f49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad928b928b9291611d6483615a3a565b909155506040805160208101959095526001600160a01b03909316928401929092526060830152608082015260a0810187905260c00160405160208183030381529060405280519060200120604051602001611df29291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b6040516020818303038152906040528051906020012090506000611e1587611aae565b9050803b15611f6a57604080516020810186905280820185905260f887901b7fff000000000000000000000000000000000000000000000000000000000000001660608201528151604181830301815260618201928390527f1626ba7e000000000000000000000000000000000000000000000000000000009092526001600160a01b03831691631626ba7e91611eb0918691606501615a55565b602060405180830381865afa158015611ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef19190615a6e565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916631626ba7e60e01b14611f655760405162461bcd60e51b815260206004820152600c60248201527f556e617574686f72697a656400000000000000000000000000000000000000006044820152606401610e3b565b612084565b6040805160008082526020820180845285905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611fbe573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166120215760405162461bcd60e51b815260206004820152601160248201527f496e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610e3b565b816001600160a01b0316816001600160a01b0316146120825760405162461bcd60e51b815260206004820152600c60248201527f556e617574686f72697a656400000000000000000000000000000000000000006044820152606401610e3b565b505b61121e8888613729565b60008080806120ad6120a861014087016101208801615248565b613fe7565b935060006040518060a00160405280876101200160208101906120d09190615248565b6001600160a01b03168152602090810190612106906120f1908a018a615248565b61210160408b0160208c01615248565b613c3c565b64ffffffffff1681526020016121226060890160408a01615a8b565b60ff16815260200161213a6080890160608a01615ab7565b60020b815260200161215260a0890160808a01615ab7565b60020b9052600086815260096020908152604091829020835181548584015186860151606088015160808901516001600160a01b039095167fffffffffffffff00000000000000000000000000000000000000000000000000909416939093177401000000000000000000000000000000000000000064ffffffffff90931692909202919091177fffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffff1679010000000000000000000000000000000000000000000000000060ff909216919091027fffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffff1617600160d01b62ffffff92831602177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e81b919092160217905581518083019092529192506122f891839190819061229c908b018b615248565b6001600160a01b031681526020018960200160208101906122bd9190615248565b6001600160a01b0316815250878960a001358a60c001358b60e001358c61010001358d6101400160208101906122f391906158e4565b614016565b969891975095945092505050565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd860006001600160a01b03821663dc6574656123758989604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815260048101919091526000602482015260440161010060405180830381865afa1580156123d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f59190615b0f565b60200151905061240787878387613ae7565b6000826001600160a01b031663c349e76989898961242433613be0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b1681526001600160a01b03948516600482015293909216602484015262ffffff16604483015260648201526084016020604051808303816000875af115801561249a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124be9190615bd9565b90508360ff168160ff1614806124d6575060ff848116145b6124df57600080fd5b6124e98888613c3c565b505050505050505050565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b031663aaa9acd23061252d86613be0565b6040805133602082015287918791016040516020818303038152906040526040518663ffffffff1660e01b815260040161256b959493929190615a02565b600060405180830381600087803b15801561258557600080fd5b505af1158015611546573d6000803e3d6000fd5b606060018054610d3290615739565b6001600160a01b0382163314156126015760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610e3b565b3360008181526004602090815260408083206001600160a01b0387168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60008161269781613d6b565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b031663c6883ec56040518060c001604052808d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506020016127168c613da8565b61271f906157f4565b81526020018761272f5789612731565b305b6001600160a01b031681526020018761274b576000612754565b6127548a613be0565b81526020018861276557600061276e565b61276e33613be0565b8152604080513360208281019190915290920191016040516020818303038152906040528152506040518263ffffffff1660e01b81526004016127b19190615bf6565b60408051808303816000875af11580156127cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f39190615889565b509150868211156128465760405162461bcd60e51b815260206004820152601260248201527f544f4f5f4d5543485f52455155455354454400000000000000000000000000006044820152606401610e3b565b5098975050505050505050565b60608167ffffffffffffffff81111561286e5761286e61556a565b6040519080825280602002602001820160405280156128a157816020015b606081526020019060019003908161288c5790505b50905060005b8281101561296a57600080308686858181106128c5576128c5615c69565b90506020028101906128d79190615c7f565b6040516128e5929190615ce4565b600060405180830381855af49150503d8060008114612920576040519150601f19603f3d011682016040523d82523d6000602084013e612925565b606091505b50915091508161294257805161293a57600080fd5b805181602001fd5b8084848151811061295557612955615c69565b602090810291909101015250506001016128a7565b5092915050565b6006546001600160a01b0316331461298857600080fd5b6005805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b60005b81811015610f8d5760008383838181106129d6576129d6615c69565b9050602002013590506129e881613f91565b6000818152600960209081526040808320805464ffffffffff740100000000000000000000000000000000000000008204168552600b84528285208054600182015485516001600160a01b03928316818901529082168187015285518082038701815260608201808852815191909801207fc625e3c300000000000000000000000000000000000000000000000000000000909752606481019690965230608487015260a4860188905260ff79010000000000000000000000000000000000000000000000000084041660c4870152600160d01b8304600290810b60e4880152600160e81b90930490920b6101048601529251919492937f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd89091169163c625e3c3916101248082019260a0929091908290030181865afa158015612b30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b549190615cf4565b80519091506bffffffffffffffffffffffff1615612bb45760405162461bcd60e51b815260206004820152600960248201527f4e4f545f454d50545900000000000000000000000000000000000000000000006044820152606401610e3b565b612bbd846142e3565b50505060009081526009602052604081205580612bd981615a3a565b9150506129ba565b612beb33836137e9565b612c5d5760405162461bcd60e51b815260206004820152603160248201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f60448201527f776e6572206e6f7220617070726f7665640000000000000000000000000000006064820152608401610e3b565b611b8a84848484614397565b6000818152600960205260409020546060906001600160a01b0316612cd05760405162461bcd60e51b815260206004820152600f60248201527f746f6b656e206e6f7420657869737400000000000000000000000000000000006044820152606401610e3b565b6005546001600160a01b0316612cf55760405180602001604052806000815250610d1d565b6005546040517fe9dc6375000000000000000000000000000000000000000000000000000000008152306004820152602481018490526001600160a01b039091169063e9dc637590604401600060405180830381865afa158015612d5d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d1d9190810190615d91565b600080808335612d9481613f91565b600060096000876000013581526020019081526020016000206040518060a00160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016000820160149054906101000a900464ffffffffff1664ffffffffff1664ffffffffff1681526020016000820160199054906101000a900460ff1660ff1660ff16815260200160008201601a9054906101000a900460020b60020b60020b815260200160008201601d9054906101000a900460020b60020b60020b815250509050612f2781600b6000846020015164ffffffffff1664ffffffffff1681526020019081526020016000206040518060400160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681525050886000013589602001358a604001358b606001358c608001358d60a00160208101906122f391906158e4565b91989097509095509350505050565b81612f4a576008546001600160801b031691505b612f5382613f91565b6000828152600960209081526040808320805464ffffffffff740100000000000000000000000000000000000000008204168552600b909352928190208054600182015492517f8e9059d60000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152928116602484015260ff790100000000000000000000000000000000000000000000000000850481166044850152600160d01b8504600290810b6064860152600160e81b90950490940b608484015260a4830187905292851660c4830152917f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd81690638e9059d69060e401611a80565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b0316634b2084e38461309533613be0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b039283166004820152602481019190915290851660448201526064810184905260840161256b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa158015613170573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319491906157db565b9050828110156131e65760405162461bcd60e51b815260206004820152601160248201527f496e73756666696369656e7420574554480000000000000000000000000000006044820152606401610e3b565b8015610f8d576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561326757600080fd5b505af115801561327b573d6000803e3d6000fd5b50505050610f8d82826137a4565b6040805160a0810182526000808252602080830182905282840182905260608301829052608083018290528482526009905291822080546001600160a01b03811693928392790100000000000000000000000000000000000000000000000000830460ff1692600160d01b8104600290810b93600160e81b909204900b91876133545760405162461bcd60e51b815260206004820152600a60248201527f4e4f545f455849535453000000000000000000000000000000000000000000006044820152606401610e3b565b805474010000000000000000000000000000000000000000900464ffffffffff166000908152600b60205260409020805460018201546001600160a01b039182169950811697507f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd81663c625e3c36133ff8a8a604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152306024820152604481018d905260ff89166064820152600288810b608483015287900b60a482015260c40160a060405180830381865afa15801561347b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349f9190615cf4565b92505050919395979092949650565b336001600160a01b037f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd816146134e357600080fd5b83156134ff576134ff866134f983850185615248565b86613dde565b505050505050565b6040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018690526064810185905260ff8416608482015260a4810183905260c481018290526001600160a01b0387169063d505accf9060e4016115de565b60008161358381613d6b565b7f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b031663c6883ec56040518060c001604052808d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506020016136028c613da8565b8152602001876136125789613614565b305b6001600160a01b031681526020018761362e576000613637565b6136378a613be0565b815260200188613648576000613651565b61365133613be0565b8152604080513360208281019190915290920191016040516020818303038152906040528152506040518263ffffffff1660e01b81526004016136949190615bf6565b60408051808303816000875af11580156136b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136d69190615889565b925050868210156128465760405162461bcd60e51b815260206004820152601360248201527f544f4f5f4c4954544c455f5245434549564544000000000000000000000000006044820152606401610e3b565b6000818152600360205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038416908117909155819061376b82611aae565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080600080600085875af1905080610f8d576040517fe293b56b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600960205260408120546001600160a01b03166138735760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201527f697374656e7420746f6b656e00000000000000000000000000000000000000006064820152608401610e3b565b600061387e83611aae565b9050806001600160a01b0316846001600160a01b031614806138b95750836001600160a01b03166138ae84610db5565b6001600160a01b0316145b806138e957506001600160a01b0380821660009081526004602090815260408083209388168352929052205460ff165b949350505050565b826001600160a01b031661390482611aae565b6001600160a01b0316146139805760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201527f73206e6f74206f776e00000000000000000000000000000000000000000000006064820152608401610e3b565b6001600160a01b0382166139fb5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610e3b565b613a06838383614420565b613a11600082613729565b6001600160a01b0383166000908152600260205260408120805460019290613a3a908490615dff565b90915550506001600160a01b0382166000908152600260205260408120805460019290613a689084906159ea565b90915550506000818152600960205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905580826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6000613b0d6a64000000000000000000006001600160801b038516808204910615150190565b90506000613b3660646001600160801b0386160268010000000000000000808204910615150190565b90508215613bca57600030613b4a33613be0565b604080516001600160a01b0390931660208401528201526060016040516020818303038152906040528051906020012090506000613b8888836144d2565b90506000613b9688846144d2565b905081851115613bad57613bad338a8488036124f4565b80841115613bc257613bc233898387036124f4565b5050506134ff565b613bd53387846124f4565b6134ff3386836124f4565b6001600160a01b03811680613c375760405162461bcd60e51b815260206004820152600f60248201527f5a45524f5f4143435f5245465f494400000000000000000000000000000000006044820152606401610e3b565b919050565b604080516001600160a01b0384811660208084019190915290841682840152825180830384018152606090920183528151918101919091206000818152600c9092529190205464ffffffffff16908161296a57600a805464ffffffffff16906000613ca683615e16565b82546101009290920a64ffffffffff8181021990931691831602179091556000928352600c6020908152604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000169385169384179055805180820182526001600160a01b039889168152968816878301908152928552600b9091529092209351845473ffffffffffffffffffffffffffffffffffffffff199081169187169190911785559151600194909401805490921693909416929092179091555090565b80421115613da5576040517f70f65caa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115613dda57613dda615e3b565b5090565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316836001600160a01b0316148015613e1f5750804710155b15613f65577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613e7f57600080fd5b505af1158015613e93573d6000803e3d6000fd5b50506040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd881166004830152602482018690527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216935063a9059cbb925060440190506020604051808303816000875af1158015613f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8a9190615e51565b610f8d83837f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd884614588565b613f9b33826137e9565b613da55760405162461bcd60e51b815260206004820152600c60248201527f4e4f545f415050524f56454400000000000000000000000000000000000000006044820152606401610e3b565b600854600090614001906001600160801b03166001615e6e565b6001600160801b03169050613c378282614628565b60008060006141277f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b031663dc6574656140928d600001518e60200151604080516001600160a01b03938416602080830191909152929093168382015280518084038201815260609093019052815191012090565b8e604001516040518363ffffffff1660e01b81526004016140c092919091825260ff16602082015260400190565b61010060405180830381865afa1580156140de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141029190615b0f565b602001516141138d6060015161478d565b6141208e6080015161478d565b8b8b614ac7565b92507f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd86001600160a01b0316632fb07eac6040518061014001604052808d600001516001600160a01b031681526020018d602001516001600160a01b031681526020018e6040015160ff1681526020018e6060015160020b81526020018e6080015160020b8152602001866bffffffffffffffffffffffff168152602001306001600160a01b031681526020018c8152602001876141e65760006141ef565b6141ef33613be0565b8152604080513360208281019190915290920191016040516020818303038152906040528152506040518263ffffffff1660e01b81526004016142329190615e99565b60408051808303816000875af1158015614250573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142749190615889565b90925090508582108015906142895750848110155b6142d55760405162461bcd60e51b815260206004820152600e60248201527f507269636520736c6970706167650000000000000000000000000000000000006044820152606401610e3b565b985098509895505050505050565b60006142ee82611aae565b90506142fc81600084614420565b614307600083613729565b6001600160a01b0381166000908152600260205260408120805460019290614330908490615dff565b9091555050600082815260096020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6143a28484846138f1565b6143ae84848484614be3565b611b8a5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610e3b565b6001600160a01b03831661446d57600880546001600160801b031690600061444783615f6c565b91906101000a8154816001600160801b0302191690836001600160801b03160217905550505b6001600160a01b038216610f8d576008805470010000000000000000000000000000000090046001600160801b03169060106144a883615f6c565b91906101000a8154816001600160801b0302191690836001600160801b0316021790555050505050565b6040517f7266a0e40000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152602482018390526000917f0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd890911690637266a0e490604401602060405180830381865afa15801561455d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061458191906157db565b9392505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526001600160a01b038416602482015282604482015260008060648360008a5af19150506145eb81614d79565b614621576040517fa83c475400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6001600160a01b03821661467e5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610e3b565b6000818152600960205260409020546001600160a01b0316156146e35760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610e3b565b6146ef60008383614420565b6001600160a01b03821660009081526002602052604081208054600192906147189084906159ea565b90915550506000818152600960205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905560405181906001600160a01b038416906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000600282900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755138015906147ca5750620bd8ab600283900b13155b6147d357600080fd5b6000808360020b126147e557826147ea565b826000035b62ffffff811691507001000000000000000000000000000000009060011615614823576ffffcb933bd6fad37aa2d162d1a5940010260801c5b6002821615614842576ffff97272373d413259a46990580e213a0260801c5b6004821615614861576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614880576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b601082161561489f576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156148be576fff973b41fa98c081472e6896dfb254c00260801c5b60408216156148dd576fff2ea16466c96a3843ec78b326b528610260801c5b60808216156148fc576ffe5dee046a99a2a811c461f1969c30530260801c5b61010082161561491c576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b61020082161561493c576ff987a7253ac413176f2b074cf7815e540260801c5b61040082161561495c576ff3392b0822b70005940c7a398e4b70f30260801c5b61080082161561497c576fe7159475a2c29b7443b29c7fa6e889d90260801c5b61100082161561499c576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156149bc576fa9f746462d870fdf8a65dc1f90e061e50260801c5b6140008216156149dc576f70d869a156d2a1b890bb3df62baf32f70260801c5b6180008216156149fc576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615614a1d576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615614a3d576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614a5c576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614a79576b048a170391f7dc42444e8fa20260801c5b60008460020b12614a99578060001981614a9557614a95615f89565b0490505b6000670100000000000000820611614ab2576000614ab5565b60015b60ff16603882901c0192505050919050565b600080856001600160801b0316876001600160801b031611614b2057614b1984866001600160801b0316886001600160801b03160269010000000000000000008989036001600160801b031602614dbe565b9050614bcb565b846001600160801b0316876001600160801b031610614b5957614b198369010000000000000000008888036001600160801b0316614dbe565b6000614b9185876001600160801b03168a6001600160801b03160269010000000000000000008b8a036001600160801b031602614dbe565b90506000614bb58569010000000000000000008a8c036001600160801b0316614dbe565b9050808210614bc45780614bc6565b815b925050505b614bd8600882901c614e6c565b979650505050505050565b60006001600160a01b0384163b15614d6e576040517f150b7a020000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063150b7a0290614c40903390899088908890600401615f9f565b6020604051808303816000875af1925050508015614c7b575060408051601f3d908101601f19168201909252614c7891810190615a6e565b60015b614d23573d808015614ca9576040519150601f19603f3d011682016040523d82523d6000602084013e614cae565b606091505b50805161293a5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610e3b565b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001490506138e9565b506001949350505050565b600081614d8a573d6000803e3d6000fd5b3d60208114614da2578015614db35760009150614db8565b3d6000803e60005115159150614db8565b600191505b50919050565b600080806000198587098587029250828110838203039150508060001415614df85760008411614ded57600080fd5b508290049050614581565b808411614e0457600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b60006bffffffffffffffffffffffff821115613dda57613dda615e3b565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114613da557600080fd5b600060208284031215614eca57600080fd5b813561458181614e8a565b60005b83811015614ef0578181015183820152602001614ed8565b83811115611b8a5750506000910152565b60008151808452614f19816020860160208601614ed5565b601f01601f19169290920160200192915050565b6020815260006145816020830184614f01565b600060208284031215614f5257600080fd5b5035919050565b6001600160a01b0381168114613da557600080fd5b60008060408385031215614f8157600080fd5b8235614f8c81614f59565b946020939093013593505050565b600080600060608486031215614faf57600080fd5b8335614fba81614f59565b92506020840135614fca81614f59565b929592945050506040919091013590565b62ffffff81168114613da557600080fd5b6001600160801b0381168114613da557600080fd5b8015158114613da557600080fd5b600080600080600060a0868803121561502757600080fd5b853561503281614f59565b9450602086013561504281614f59565b9350604086013561505281614fdb565b9250606086013561506281614fec565b9150608086013561507281615001565b809150509295509295909350565b60008060008060008060008060006101208a8c03121561509f57600080fd5b89356150aa81614f59565b985060208a01356150ba81614f59565b975060408a0135965060608a0135955060808a0135945060a08a01356150df81614f59565b935060c08a01356150ef81615001565b925060e08a01356150ff81615001565b809250506101008a013590509295985092959850929598565b60008083601f84011261512a57600080fd5b50813567ffffffffffffffff81111561514257600080fd5b60208301915083602082850101111561515a57600080fd5b9250929050565b60008060008060008060a0878903121561517a57600080fd5b863561518581614f59565b9550602087013561519581614f59565b94506040870135935060608701359250608087013567ffffffffffffffff8111156151bf57600080fd5b6151cb89828a01615118565b979a9699509497509295939492505050565b60ff81168114613da557600080fd5b60008060008060008060c0878903121561520557600080fd5b863561521081614f59565b95506020870135945060408701359350606087013561522e816151dd565b9598949750929560808101359460a0909101359350915050565b60006020828403121561525a57600080fd5b813561458181614f59565b600060e08284031215614db857600080fd5b6000806000806080858703121561528d57600080fd5b843561529881614f59565b93506020850135925060408501356152af81614f59565b9396929550929360600135925050565b600080600080606085870312156152d557600080fd5b84356152e081614f59565b935060208501359250604085013567ffffffffffffffff81111561530357600080fd5b61530f87828801615118565b95989497509550505050565b60006101608284031215614db857600080fd5b600080600080600060a0868803121561534657600080fd5b853561535181614f59565b9450602086013561536181614f59565b9350604086013561537181614fdb565b9250606086013561538181615001565b91506080860135615072816151dd565b600080604083850312156153a457600080fd5b82356153af81614f59565b915060208301356153bf81615001565b809150509250929050565b60008060008060008060008060e0898b0312156153e657600080fd5b883567ffffffffffffffff8111156153fd57600080fd5b6154098b828c01615118565b9099509750506020890135955060408901359450606089013561542b81614f59565b9350608089013561543b81615001565b925060a089013561544b81615001565b8092505060c089013590509295985092959890939650565b60008083601f84011261547557600080fd5b50813567ffffffffffffffff81111561548d57600080fd5b6020830191508360208260051b850101111561515a57600080fd5b600080602083850312156154bb57600080fd5b823567ffffffffffffffff8111156154d257600080fd5b6154de85828601615463565b90969095509350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561555d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261554b858351614f01565b94509285019290850190600101615511565b5092979650505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156155a9576155a961556a565b604052919050565b600067ffffffffffffffff8211156155cb576155cb61556a565b50601f01601f191660200190565b600080600080608085870312156155ef57600080fd5b84356155fa81614f59565b9350602085013561560a81614f59565b925060408501359150606085013567ffffffffffffffff81111561562d57600080fd5b8501601f8101871361563e57600080fd5b803561565161564c826155b1565b615580565b81815288602083850101111561566657600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b600060c08284031215614db857600080fd5b600080604083850312156156ad57600080fd5b8235915060208301356153bf816151dd565b600080604083850312156156d257600080fd5b8235915060208301356153bf81614f59565b600080604083850312156156f757600080fd5b823561570281614f59565b915060208301356153bf81614f59565b60006020828403121561572457600080fd5b813564ffffffffff8116811461458157600080fd5b600181811c9082168061574d57607f821691505b60208210811415614db857634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001600160801b03838116908316818110156157a4576157a461576e565b039392505050565b600080604083850312156157bf57600080fd5b82516157ca816151dd565b60208401519092506153bf816151dd565b6000602082840312156157ed57600080fd5b5051919050565b60007f80000000000000000000000000000000000000000000000000000000000000008214156158265761582661576e565b5060000390565b60006101006001600160a01b03808c168452808b1660208501528960408501528860608501528088166080850152508560a08401528460c08401528060e084015261587a81840185614f01565b9b9a5050505050505050505050565b6000806040838503121561589c57600080fd5b505080516020909101519092909150565b6bffffffffffffffffffffffff81168114613da557600080fd5b6000602082840312156158d957600080fd5b8135614581816158ad565b6000602082840312156158f657600080fd5b813561458181615001565b6000610120820190506001600160a01b03808451168352806020850151166020840152506040830151615939604084018260ff169052565b50606083015161594e606084018260020b9052565b506080830151615963608084018260020b9052565b5060a083015161598360a08401826bffffffffffffffffffffffff169052565b5060c083015160c083015260e083015160e0830152610100808401516159ac8285018215159052565b505092915050565b600080600080608085870312156159ca57600080fd5b505082516020840151604085015160609095015191969095509092509050565b600082198211156159fd576159fd61576e565b500190565b60006001600160a01b03808816835286602084015280861660408401525083606083015260a06080830152614bd860a0830184614f01565b6000600019821415615a4e57615a4e61576e565b5060010190565b8281526040602082015260006138e96040830184614f01565b600060208284031215615a8057600080fd5b815161458181614e8a565b600060208284031215615a9d57600080fd5b8135614581816151dd565b8060020b8114613da557600080fd5b600060208284031215615ac957600080fd5b813561458181615aa8565b8051613c3781614fec565b8051613c3781614fdb565b8051613c3781615aa8565b805169ffffffffffffffffffff81168114613c3757600080fd5b6000610100808385031215615b2357600080fd5b6040519081019067ffffffffffffffff82118183101715615b4657615b4661556a565b8160405283519150615b5782614fec565b818152615b6660208501615ad4565b6020820152615b7760408501615adf565b6040820152615b8860608501615aea565b6060820152615b9960808501615aea565b6080820152615baa60a08501615aea565b60a0820152615bbb60c08501615af5565b60c0820152615bcc60e08501615af5565b60e0820152949350505050565b600060208284031215615beb57600080fd5b8151614581816151dd565b602081526000825160c06020840152615c1260e0840182614f01565b9050602084015160408401526001600160a01b03604085015116606084015260608401516080840152608084015160a084015260a0840151601f198483030160c0850152615c608282614f01565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615cb457600080fd5b83018035915067ffffffffffffffff821115615ccf57600080fd5b60200191503681900382131561515a57600080fd5b8183823760009101908152919050565b600060a08284031215615d0657600080fd5b60405160a0810181811067ffffffffffffffff82111715615d2957615d2961556a565b6040528251615d37816158ad565b8152615d4560208401615af5565b6020820152615d5660408401615af5565b60408201526060830151615d69816151dd565b6060820152608083015163ffffffff81168114615d8557600080fd5b60808201529392505050565b600060208284031215615da357600080fd5b815167ffffffffffffffff811115615dba57600080fd5b8201601f81018413615dcb57600080fd5b8051615dd961564c826155b1565b818152856020838501011115615dee57600080fd5b615c60826020830160208601614ed5565b600082821015615e1157615e1161576e565b500390565b600064ffffffffff80831681811415615e3157615e3161576e565b6001019392505050565b634e487b7160e01b600052600160045260246000fd5b600060208284031215615e6357600080fd5b815161458181615001565b60006001600160801b03808316818516808303821115615e9057615e9061576e565b01949350505050565b60208152615eb36020820183516001600160a01b03169052565b60006020830151615ecf60408401826001600160a01b03169052565b50604083015160ff81166060840152506060830151615ef3608084018260020b9052565b506080830151615f0860a084018260020b9052565b5060a08301516bffffffffffffffffffffffff811660c08401525060c08301516001600160a01b03811660e08401525060e08301516101008381019190915283015161012080840191909152830151610140808401526138e9610160840182614f01565b60006001600160801b0380831681811415615e3157615e3161576e565b634e487b7160e01b600052601260045260246000fd5b60006001600160a01b03808716835280861660208401525083604083015260806060830152615fd16080830184614f01565b969550505050505056fea164736f6c634300080a000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd8000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
-----Decoded View---------------
Arg [0] : _hub (address): 0x6690384822afF0B65fE0C21a809F187F5c3fcdd8
Arg [1] : _WETH9 (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000006690384822aff0b65fe0c21a809f187f5c3fcdd8
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.