20M+ users trust MoonPay worldwide. Checkout with your preferred payment method.
Ready to onboard to Ethereum? With MetaMask Portfolio, you're in control.
Don’t invest unless you’re prepared to lose all the money you invest.
Everyday giveaways up to 100 ETH, Lucky Spins. Deposit BONUS 300% and Cashbacks!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Slots, Roulette, Poker & more - Proud sponsors of UFC, Everton & StakeF1 team!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Anonymous play on awesome games - sign up now for 25 free jackpot spins - worth $100s!
100s of games, generous bonuses, 20+ years of trusted gaming. Join CryptoWins & start winning today!
Overview
ETH Balance
Eth Value
$0.00Token Holdings
Could not find any matches!
- ERC-20 Tokens (26)19.99999932 GFIGoldfinch (GFI)$32.40@1.620 gOHMGovernance O... (gOHM)$0.00@5,362.320.00034491 OHMOlympus (OHM)$0.01@20.110.00000004 STGStargateToke... (STG)$0.00@0.329626.29469724 SUSHISushiToken (SUSHI)$29.45@1.12216.51399688 UNIUniswap (UNI)$2,321.03@10.720 LUNCWrapped LUNC... (LUNC)$0.00@0.00724.66635541 DAIDai Stableco... (DAI)$724.67@1.006,963.589063 USDTTether USD (USDT)$6,970.55@1.00164,303.03796 USDCUSDC (USDC)$64,367.34@1.0010.05691078 WBTCWrapped BTC (WBTC)$5,552.50@97,565.0020.29511292 WETHWrapped Ethe... (WETH)$69,223.26@3,410.83394.75ERC20 ***0 CPIConsumer Price Index0.00010384 OHM-20230322Olympus 2023... (OHM-20...)10,000,000 PHIPhi (PHI)0.3 CRICrypto International1.2 TokenERC-20 TOKEN*[Suspicious]3,999.99 TokenERC-20 TOKEN*[Suspicious]3,999.99 TokenERC-20 TOKEN*[Suspicious]0.7 TokenERC-20 TOKEN*[Suspicious]9,000 TokenERC-20 TOKEN*[Suspicious]7,000 TokenERC-20 TOKEN*[Suspicious]7,000 TokenERC-20 TOKEN*[Suspicious]7,000 TokenERC-20 TOKEN*[Unsafe]7,000 TokenERC-20 TOKEN*[Spam]NFT Tokens (17)claim rewards on apyusd.netapyusd.netERC-1155Reward Club [Gu6Z7sfc]Reward Club [2vVlzIw8]ERC-1155claim rewards on univ4labs.orguniv4labs.orgERC-1155claim rewards on wbtcnetwork.netwbtcnetwork.netERC-1155ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]
More Info
Private Name Tags
ContractCreator
- Transactions
- Internal Transactions
- Token Transfers (ERC-20)
- NFT Transfers
- Contract
- Events
- Analytics
- Multichain Portfolio
- Cards New
Advanced Filter- Filter by Tx Type:
- Tx
- Internal Tx
- ERC-20
- NFTs
Latest 25 from a total of 32 transactions
Transaction Hash MethodBlockFromToSet Governance 15522168 2022-09-12 17:39:51 803 days ago 1663004391 IN 0 ETH$0.00 0.00054953 17.74585296 Set Pool Allowed... 15478080 2022-09-05 13:16:51 810 days ago 1662383811 IN 0 ETH$0.00 0.0017163 22.49447581 Set Pool Default... 15478079 2022-09-05 13:16:33 810 days ago 1662383793 IN 0 ETH$0.00 0.00110693 22.42256669 Set Pool Allowed... 15478071 2022-09-05 13:13:00 810 days ago 1662383580 IN 0 ETH$0.00 0.00125639 16.46678149 Set Pool Default... 15478069 2022-09-05 13:12:33 810 days ago 1662383553 IN 0 ETH$0.00 0.00086357 17.49292134 Set Pool Allowed... 15478068 2022-09-05 13:12:04 810 days ago 1662383524 IN 0 ETH$0.00 0.00122937 16.11769454 Set Pool Default... 15478067 2022-09-05 13:11:28 810 days ago 1662383488 IN 0 ETH$0.00 0.00071298 14.44951906 Set Pool Allowed... 15478066 2022-09-05 13:11:03 810 days ago 1662383463 IN 0 ETH$0.00 0.00100078 13.07861701 Set Pool Default... 15478064 2022-09-05 13:10:31 810 days ago 1662383431 IN 0 ETH$0.00 0.00065083 13.18363648 Set Pool Allowed... 15478063 2022-09-05 13:10:19 810 days ago 1662383419 IN 0 ETH$0.00 0.00103126 13.47682578 Set Pool Default... 15478061 2022-09-05 13:09:47 810 days ago 1662383387 IN 0 ETH$0.00 0.0006528 13.22346028 Set Pool Allowed... 15478060 2022-09-05 13:09:27 810 days ago 1662383367 IN 0 ETH$0.00 0.00091446 11.95051928 Set Pool Default... 15478057 2022-09-05 13:09:13 810 days ago 1662383353 IN 0 ETH$0.00 0.00070905 14.36298653 Set Pool Allowed... 15478055 2022-09-05 13:08:29 810 days ago 1662383309 IN 0 ETH$0.00 0.00103835 13.60898198 Set Pool Default... 15478054 2022-09-05 13:08:09 810 days ago 1662383289 IN 0 ETH$0.00 0.00062484 12.65720743 Set Pool Allowed... 15478052 2022-09-05 13:07:53 810 days ago 1662383273 IN 0 ETH$0.00 0.00106235 13.92357776 Set Pool Default... 15478051 2022-09-05 13:07:37 810 days ago 1662383257 IN 0 ETH$0.00 0.00065989 13.36717616 Set Pool Allowed... 15478050 2022-09-05 13:07:29 810 days ago 1662383249 IN 0 ETH$0.00 0.0009367 12.27675255 Set Pool Default... 15478046 2022-09-05 13:06:17 810 days ago 1662383177 IN 0 ETH$0.00 0.0006557 13.2822936 Set Pool Allowed... 15478045 2022-09-05 13:06:11 810 days ago 1662383171 IN 0 ETH$0.00 0.00099019 12.9401401 Set Pool Default... 15478042 2022-09-05 13:04:53 810 days ago 1662383093 IN 0 ETH$0.00 0.00055046 11.15044202 Set Pool Allowed... 15478041 2022-09-05 13:04:39 810 days ago 1662383079 IN 0 ETH$0.00 0.00084725 11.07217708 Set Pool Default... 15478038 2022-09-05 13:04:13 810 days ago 1662383053 IN 0 ETH$0.00 0.00057801 11.70861368 Set Pool Allowed... 15478036 2022-09-05 13:03:34 810 days ago 1662383014 IN 0 ETH$0.00 0.00093755 12.25223418 Set Pool Default... 15478033 2022-09-05 13:03:24 810 days ago 1662383004 IN 0 ETH$0.00 0.00072254 14.63619175 View more zero value Internal Transactions in Advanced View mode
Advanced mode:Loading...LoadingContract Name:MuffinHub
Compiler Versionv0.8.10+commit.fc410830
Optimization Enabled:Yes with 99999 runs
Other Settings:default evmVersionContract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.10; import "./interfaces/hub/IMuffinHub.sol"; import "./interfaces/IMuffinHubCallbacks.sol"; import "./libraries/utils/SafeTransferLib.sol"; import "./libraries/utils/PathLib.sol"; import "./libraries/math/Math.sol"; import "./libraries/Pools.sol"; import "./MuffinHubBase.sol"; contract MuffinHub is IMuffinHub, MuffinHubBase { using Math for uint256; using Pools for Pools.Pool; using Pools for mapping(bytes32 => Pools.Pool); using PathLib for bytes; error InvalidTokenOrder(); error NotAllowedSqrtGamma(); error InvalidSwapPath(); error NotEnoughIntermediateOutput(); error NotEnoughFundToWithdraw(); /// @dev To reduce bytecode size of this contract, we offload position-related functions, governance functions and /// various view functions to a second contract (i.e. MuffinHubPositions.sol) and use delegatecall to call it. address internal immutable positionController; constructor(address _positionController) { positionController = _positionController; governance = msg.sender; } /*=============================================================== * ACCOUNTS *==============================================================*/ /// @inheritdoc IMuffinHubActions function deposit( address recipient, uint256 recipientAccRefId, address token, uint256 amount, bytes calldata data ) external { uint256 balanceBefore = getBalanceAndLock(token); IMuffinHubCallbacks(msg.sender).muffinDepositCallback(token, amount, data); checkBalanceAndUnlock(token, balanceBefore + amount); accounts[token][getAccHash(recipient, recipientAccRefId)] += amount; emit Deposit(recipient, recipientAccRefId, token, amount, msg.sender); } /// @inheritdoc IMuffinHubActions function withdraw( address recipient, uint256 senderAccRefId, address token, uint256 amount ) external { bytes32 accHash = getAccHash(msg.sender, senderAccRefId); uint256 balance = accounts[token][accHash]; if (balance < amount) revert NotEnoughFundToWithdraw(); unchecked { accounts[token][accHash] = balance - amount; } SafeTransferLib.safeTransfer(token, recipient, amount); emit Withdraw(msg.sender, senderAccRefId, token, amount, recipient); } /*=============================================================== * CREATE POOL / TIER *==============================================================*/ /// @notice Check if the given sqrtGamma is allowed to be used to create a pool or tier /// @dev It first checks if the sqrtGamma is in the whitelist, then check if the pool hasn't had that fee tier created. function isSqrtGammaAllowed(bytes32 poolId, uint24 sqrtGamma) public view returns (bool) { uint24[] storage allowed = poolAllowedSqrtGammas[poolId].length != 0 ? poolAllowedSqrtGammas[poolId] : defaultAllowedSqrtGammas; unchecked { for (uint256 i; i < allowed.length; i++) { if (allowed[i] == sqrtGamma) { Tiers.Tier[] storage tiers = pools[poolId].tiers; for (uint256 j; j < tiers.length; j++) if (tiers[j].sqrtGamma == sqrtGamma) return false; return true; } } } return false; } /// @inheritdoc IMuffinHubActions function createPool( address token0, address token1, uint24 sqrtGamma, uint128 sqrtPrice, uint256 senderAccRefId ) external returns (bytes32 poolId) { if (token0 >= token1 || token0 == address(0)) revert InvalidTokenOrder(); Pools.Pool storage pool; (pool, poolId) = pools.getPoolAndId(token0, token1); if (!isSqrtGammaAllowed(poolId, sqrtGamma)) revert NotAllowedSqrtGamma(); uint8 tickSpacing = poolDefaultTickSpacing[poolId]; if (tickSpacing == 0) tickSpacing = defaultTickSpacing; (uint256 amount0, uint256 amount1) = pool.initialize(sqrtGamma, sqrtPrice, tickSpacing, defaultProtocolFee); accounts[token0][getAccHash(msg.sender, senderAccRefId)] -= amount0; accounts[token1][getAccHash(msg.sender, senderAccRefId)] -= amount1; emit PoolCreated(token0, token1, poolId); emit UpdateTier(poolId, 0, sqrtGamma, sqrtPrice, 1); pool.unlock(); underlyings[poolId] = Pair(token0, token1); } /// @inheritdoc IMuffinHubActions function addTier( address token0, address token1, uint24 sqrtGamma, uint256 senderAccRefId ) external returns (uint8 tierId) { (Pools.Pool storage pool, bytes32 poolId) = pools.getPoolAndId(token0, token1); if (!isSqrtGammaAllowed(poolId, sqrtGamma)) revert NotAllowedSqrtGamma(); uint256 amount0; uint256 amount1; (amount0, amount1, tierId) = pool.addTier(sqrtGamma); accounts[token0][getAccHash(msg.sender, senderAccRefId)] -= amount0; accounts[token1][getAccHash(msg.sender, senderAccRefId)] -= amount1; emit UpdateTier(poolId, tierId, sqrtGamma, pool.tiers[tierId].sqrtPrice, 0); pool.unlock(); } /*=============================================================== * SWAP *==============================================================*/ /// @inheritdoc IMuffinHubActions 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) { Pools.Pool storage pool; (pool, , amountIn, amountOut) = _computeSwap( tokenIn, tokenOut, tierChoices, amountDesired, SwapEventVars(senderAccRefId, recipient, recipientAccRefId) ); _transferSwap(tokenIn, tokenOut, amountIn, amountOut, recipient, recipientAccRefId, senderAccRefId, data); pool.unlock(); } /// @inheritdoc IMuffinHubActions function swapMultiHop(SwapMultiHopParams calldata p) external returns (uint256 amountIn, uint256 amountOut) { bytes memory path = p.path; if (path.invalid()) revert InvalidSwapPath(); bool exactIn = p.amountDesired > 0; bytes32[] memory poolIds = new bytes32[](path.hopCount()); unchecked { int256 amtDesired = p.amountDesired; SwapEventVars memory evtData = exactIn ? SwapEventVars(p.senderAccRefId, msg.sender, p.senderAccRefId) : SwapEventVars(p.senderAccRefId, p.recipient, p.recipientAccRefId); for (uint256 i; i < poolIds.length; i++) { if (exactIn) { if (i == poolIds.length - 1) { evtData.recipient = p.recipient; evtData.recipientAccRefId = p.recipientAccRefId; } } else { if (i == 1) { evtData.recipient = msg.sender; evtData.recipientAccRefId = p.senderAccRefId; } } (address tokenIn, address tokenOut, uint256 tierChoices) = path.decodePool(i, exactIn); // For an "exact output" swap, it's possible to not receive the full desired output amount. therefore, in // the 2nd (and following) swaps, we request more token output so as to ensure we get enough tokens to pay // for the previous swap. The extra token is not refunded and thus results in an extra cost (small in common // token pairs). uint256 amtIn; uint256 amtOut; (, poolIds[i], amtIn, amtOut) = _computeSwap( tokenIn, tokenOut, tierChoices, (exactIn || i == 0) ? amtDesired : amtDesired - Pools.SWAP_AMOUNT_TOLERANCE, evtData ); if (exactIn) { if (i == 0) amountIn = amtIn; amtDesired = int256(amtOut); } else { if (i == 0) amountOut = amtOut; else if (amtOut < uint256(-amtDesired)) revert NotEnoughIntermediateOutput(); amtDesired = -int256(amtIn); } } if (exactIn) { amountOut = uint256(amtDesired); } else { amountIn = uint256(-amtDesired); } } (address _tokenIn, address _tokenOut) = path.tokensInOut(exactIn); _transferSwap(_tokenIn, _tokenOut, amountIn, amountOut, p.recipient, p.recipientAccRefId, p.senderAccRefId, p.data); unchecked { for (uint256 i; i < poolIds.length; i++) pools[poolIds[i]].unlock(); } } /// @dev Data to emit in "Swap" event in "_computeSwap" function struct SwapEventVars { uint256 senderAccRefId; address recipient; uint256 recipientAccRefId; } function _computeSwap( address tokenIn, address tokenOut, uint256 tierChoices, int256 amountDesired, // Desired swap amount (positive: exact input, negative: exact output) SwapEventVars memory evtData ) internal returns ( Pools.Pool storage pool, bytes32 poolId, uint256 amountIn, uint256 amountOut ) { bool isExactIn = tokenIn < tokenOut; bool isToken0 = (amountDesired > 0) == isExactIn; // i.e. isToken0In == isExactIn (pool, poolId) = isExactIn ? pools.getPoolAndId(tokenIn, tokenOut) : pools.getPoolAndId(tokenOut, tokenIn); Pools.SwapResult memory result = pool.swap(isToken0, amountDesired, tierChoices, poolId); emit Swap( poolId, msg.sender, evtData.recipient, evtData.senderAccRefId, evtData.recipientAccRefId, result.amount0, result.amount1, result.amountInDistribution, result.amountOutDistribution, result.tierData ); unchecked { // overflow is acceptable and protocol is expected to collect protocol fee before overflow if (result.protocolFeeAmt != 0) tokens[tokenIn].protocolFeeAmt += uint248(result.protocolFeeAmt); (amountIn, amountOut) = isExactIn ? (uint256(result.amount0), uint256(-result.amount1)) : (uint256(result.amount1), uint256(-result.amount0)); } } function _transferSwap( address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut, address recipient, uint256 recipientAccRefId, uint256 senderAccRefId, bytes calldata data ) internal { if (tokenIn == tokenOut) { (amountIn, amountOut) = amountIn.subUntilZero(amountOut); } if (recipientAccRefId == 0) { SafeTransferLib.safeTransfer(tokenOut, recipient, amountOut); } else { accounts[tokenOut][getAccHash(recipient, recipientAccRefId)] += amountOut; } if (senderAccRefId != 0) { bytes32 accHash = getAccHash(msg.sender, senderAccRefId); (accounts[tokenIn][accHash], amountIn) = accounts[tokenIn][accHash].subUntilZero(amountIn); } if (amountIn > 0) { uint256 balanceBefore = getBalanceAndLock(tokenIn); IMuffinHubCallbacks(msg.sender).muffinSwapCallback(tokenIn, tokenOut, amountIn, amountOut, data); checkBalanceAndUnlock(tokenIn, balanceBefore + amountIn); } } /*=============================================================== * VIEW FUNCTIONS *==============================================================*/ /// @inheritdoc IMuffinHubView function getDefaultParameters() external view returns (uint8 tickSpacing, uint8 protocolFee) { return (defaultTickSpacing, defaultProtocolFee); } /// @inheritdoc IMuffinHubView function getPoolParameters(bytes32 poolId) external view returns (uint8 tickSpacing, uint8 protocolFee) { Pools.Pool storage pool = pools[poolId]; return (pool.tickSpacing, pool.protocolFee); } /// @inheritdoc IMuffinHubView function getTier(bytes32 poolId, uint8 tierId) external view returns (Tiers.Tier memory) { return pools[poolId].tiers[tierId]; } /// @inheritdoc IMuffinHubView function getTiersCount(bytes32 poolId) external view returns (uint256) { return pools[poolId].tiers.length; } /// @inheritdoc IMuffinHubView function getTick( bytes32 poolId, uint8 tierId, int24 tick ) external view returns (Ticks.Tick memory) { return pools[poolId].ticks[tierId][tick]; } /// @inheritdoc IMuffinHubView function getPosition( bytes32 poolId, address owner, uint256 positionRefId, uint8 tierId, int24 tickLower, int24 tickUpper ) external view returns (Positions.Position memory) { return Positions.get(pools[poolId].positions, owner, positionRefId, tierId, tickLower, tickUpper); } /// @inheritdoc IMuffinHubView function getStorageAt(bytes32 slot) external view returns (bytes32 word) { assembly { word := sload(slot) } } /*=============================================================== * FALLBACK TO POSITION CONTROLLER *==============================================================*/ /// @dev Adapted from openzepplin v4.4.1 proxy implementation fallback() external { address _positionController = positionController; assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), _positionController, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } }
// 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: GPL-3.0-only pragma solidity ^0.8.0; interface IMuffinHubCallbacks { /// @notice Called by Muffin hub to request for tokens to finish deposit /// @param token Token that you are depositing /// @param amount Amount that you are depositing /// @param data Arbitrary data initially passed by you function muffinDepositCallback( address token, uint256 amount, bytes calldata data ) external; /// @notice Called by Muffin hub to request for tokens to finish minting liquidity /// @param token0 Token0 of the pool /// @param token1 Token1 of the pool /// @param amount0 Token0 amount you are owing to Muffin /// @param amount1 Token1 amount you are owing to Muffin /// @param data Arbitrary data initially passed by you function muffinMintCallback( address token0, address token1, uint256 amount0, uint256 amount1, bytes calldata data ) external; /// @notice Called by Muffin hub to request for tokens to finish swapping /// @param tokenIn Input token /// @param tokenOut Output token /// @param amountIn Input token amount you are owing to Muffin /// @param amountOut Output token amount you have just received /// @param data Arbitrary data initially passed by you function muffinSwapCallback( address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut, bytes calldata data ) external; }
// 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; library PathLib { uint256 internal constant ADDR_BYTES = 20; uint256 internal constant ADDR_UINT16_BYTES = ADDR_BYTES + 2; uint256 internal constant PATH_MAX_BYTES = ADDR_UINT16_BYTES * 256 + ADDR_BYTES; // 256 pools (i.e. 5652 bytes) function invalid(bytes memory path) internal pure returns (bool) { unchecked { return path.length > PATH_MAX_BYTES || path.length <= ADDR_BYTES || (path.length - ADDR_BYTES) % ADDR_UINT16_BYTES != 0; } } /// @dev Assume the path is valid function hopCount(bytes memory path) internal pure returns (uint256) { unchecked { return path.length / ADDR_UINT16_BYTES; } } /// @dev Assume the path is valid function decodePool( bytes memory path, uint256 poolIndex, bool exactIn ) internal pure returns ( address tokenIn, address tokenOut, uint256 tierChoices ) { unchecked { uint256 offset = ADDR_UINT16_BYTES * poolIndex; tokenIn = _readAddressAt(path, offset); tokenOut = _readAddressAt(path, ADDR_UINT16_BYTES + offset); tierChoices = _readUint16At(path, ADDR_BYTES + offset); if (!exactIn) (tokenIn, tokenOut) = (tokenOut, tokenIn); } } /// @dev Assume the path is valid function tokensInOut(bytes memory path, bool exactIn) internal pure returns (address tokenIn, address tokenOut) { unchecked { tokenIn = _readAddressAt(path, 0); tokenOut = _readAddressAt(path, path.length - ADDR_BYTES); if (!exactIn) (tokenIn, tokenOut) = (tokenOut, tokenIn); } } function _readAddressAt(bytes memory data, uint256 offset) internal pure returns (address addr) { assembly { addr := mload(add(add(data, 20), offset)) } } function _readUint16At(bytes memory data, uint256 offset) internal pure returns (uint16 value) { assembly { value := mload(add(add(data, 2), offset)) } } }
// 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: 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 "./interfaces/hub/IMuffinHubBase.sol"; import "./interfaces/common/IERC20Minimal.sol"; import "./libraries/Pools.sol"; abstract contract MuffinHubBase is IMuffinHubBase { error FailedBalanceOf(); error NotEnoughTokenInput(); /// @param locked 1 means locked. 0 or 2 means unlocked. /// @param protocolFeeAmt Amount of token accrued as the protocol fee struct TokenData { uint8 locked; uint248 protocolFeeAmt; } struct Pair { address token0; address token1; } /// @inheritdoc IMuffinHubBase address public governance; /// @dev Default tick spacing of new pool uint8 internal defaultTickSpacing = 100; /// @dev Default protocl fee of new pool (base 255) uint8 internal defaultProtocolFee = 0; /// @dev Whitelist of swap fees that LPs can choose to create a pool uint24[] internal defaultAllowedSqrtGammas = [99900, 99800, 99700, 99600, 99499]; // 20, 40, 60, 80, 100 bps /// @dev Pool-specific default tick spacing mapping(bytes32 => uint8) internal poolDefaultTickSpacing; /// @dev Pool-specific whitelist of swap fees mapping(bytes32 => uint24[]) internal poolAllowedSqrtGammas; /// @dev Mapping of poolId to pool state mapping(bytes32 => Pools.Pool) internal pools; /// @inheritdoc IMuffinHubBase mapping(address => mapping(bytes32 => uint256)) public accounts; /// @inheritdoc IMuffinHubBase mapping(address => TokenData) public tokens; /// @inheritdoc IMuffinHubBase mapping(bytes32 => Pair) public underlyings; /// @dev We blacklist TUSD legacy address on Ethereum to prevent TUSD from getting exploited here. /// In general, tokens with multiple addresses are not supported here and will cost losts of fund. address internal constant TUSD_LEGACY_ADDRESS = 0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E; /// @notice Maximum number of tiers each pool can technically have. This number might vary in different networks. function maxNumOfTiers() external pure returns (uint256) { return MAX_TIERS; } /// @dev Get token balance of this contract function getBalance(address token) private view returns (uint256) { (bool success, bytes memory data) = token.staticcall( abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) ); if (!success || data.length != 32) revert FailedBalanceOf(); return abi.decode(data, (uint256)); } /// @dev "Lock" the token so the token cannot be used as input token again until unlocked function getBalanceAndLock(address token) internal returns (uint256) { require(token != TUSD_LEGACY_ADDRESS); TokenData storage tokenData = tokens[token]; require(tokenData.locked != 1); // 1 means locked tokenData.locked = 1; return getBalance(token); } /// @dev "Unlock" the token after ensuring the contract reaches an expected token balance function checkBalanceAndUnlock(address token, uint256 balanceMinimum) internal { if (getBalance(token) < balanceMinimum) revert NotEnoughTokenInput(); tokens[token].locked = 2; } /// @dev Hash (owner, accRefId) as the key for the internal account function getAccHash(address owner, uint256 accRefId) internal pure returns (bytes32) { require(accRefId != 0); return keccak256(abi.encode(owner, accRefId)); } modifier onlyGovernance() { require(msg.sender == governance); _; } }
// 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: 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; 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: 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: 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"; 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: 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: 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 "./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; interface IERC20Minimal { function approve(address spender, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); }
{ "optimizer": { "enabled": true, "runs": 99999 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
[{"inputs":[{"internalType":"address","name":"_positionController","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FailedBalanceOf","type":"error"},{"inputs":[],"name":"FailedTransfer","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidSwapPath","type":"error"},{"inputs":[],"name":"InvalidTierChoices","type":"error"},{"inputs":[],"name":"InvalidTokenOrder","type":"error"},{"inputs":[],"name":"NotAllowedSqrtGamma","type":"error"},{"inputs":[],"name":"NotEnoughFundToWithdraw","type":"error"},{"inputs":[],"name":"NotEnoughIntermediateOutput","type":"error"},{"inputs":[],"name":"NotEnoughTokenInput","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"positionRefId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"tierId","type":"uint8"},{"indexed":false,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint256","name":"ownerAccRefId","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"liquidityD8","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount1","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CollectProtocol","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"positionRefId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"tierId","type":"uint8"},{"indexed":false,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint256","name":"ownerAccRefId","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"liquidityD8","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount1","type":"uint256"}],"name":"CollectSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"recipientAccRefId","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"governance","type":"address"}],"name":"GovernanceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"positionRefId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"tierId","type":"uint8"},{"indexed":false,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"senderAccRefId","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"liquidityD8","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"PoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"positionRefId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"tierId","type":"uint8"},{"indexed":false,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint8","name":"limitOrderType","type":"uint8"}],"name":"SetLimitOrderType","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":true,"internalType":"uint8","name":"tierId","type":"uint8"},{"indexed":true,"internalType":"int24","name":"tickEnd","type":"int24"},{"indexed":false,"internalType":"int24","name":"tickStart","type":"int24"},{"indexed":false,"internalType":"uint96","name":"liquidityD8","type":"uint96"}],"name":"Settle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"senderAccRefId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"recipientAccRefId","type":"uint256"},{"indexed":false,"internalType":"int256","name":"amount0","type":"int256"},{"indexed":false,"internalType":"int256","name":"amount1","type":"int256"},{"indexed":false,"internalType":"uint256","name":"amountInDistribution","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOutDistribution","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"tierData","type":"uint256[]"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"tickSpacing","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"protocolFee","type":"uint8"}],"name":"UpdateDefaultParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"uint8","name":"tickSpacing","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"protocolFee","type":"uint8"}],"name":"UpdatePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":true,"internalType":"uint8","name":"tierId","type":"uint8"},{"indexed":true,"internalType":"uint24","name":"sqrtGamma","type":"uint24"},{"indexed":false,"internalType":"uint128","name":"sqrtPrice","type":"uint128"},{"indexed":false,"internalType":"uint8","name":"limitOrderTickSpacingMultiplier","type":"uint8"}],"name":"UpdateTier","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"senderAccRefId","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"Withdraw","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"accounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"sqrtGamma","type":"uint24"},{"internalType":"uint256","name":"senderAccRefId","type":"uint256"}],"name":"addTier","outputs":[{"internalType":"uint8","name":"tierId","type":"uint8"}],"stateMutability":"nonpayable","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":"uint256","name":"senderAccRefId","type":"uint256"}],"name":"createPool","outputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"stateMutability":"nonpayable","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"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDefaultParameters","outputs":[{"internalType":"uint8","name":"tickSpacing","type":"uint8"},{"internalType":"uint8","name":"protocolFee","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"getPoolParameters","outputs":[{"internalType":"uint8","name":"tickSpacing","type":"uint8"},{"internalType":"uint8","name":"protocolFee","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"positionRefId","type":"uint256"},{"internalType":"uint8","name":"tierId","type":"uint8"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"getPosition","outputs":[{"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":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"getStorageAt","outputs":[{"internalType":"bytes32","name":"word","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint8","name":"tierId","type":"uint8"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTick","outputs":[{"components":[{"internalType":"uint96","name":"liquidityLowerD8","type":"uint96"},{"internalType":"uint96","name":"liquidityUpperD8","type":"uint96"},{"internalType":"int24","name":"nextBelow","type":"int24"},{"internalType":"int24","name":"nextAbove","type":"int24"},{"internalType":"bool","name":"needSettle0","type":"bool"},{"internalType":"bool","name":"needSettle1","type":"bool"},{"internalType":"uint80","name":"feeGrowthOutside0","type":"uint80"},{"internalType":"uint80","name":"feeGrowthOutside1","type":"uint80"}],"internalType":"struct Ticks.Tick","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint8","name":"tierId","type":"uint8"}],"name":"getTier","outputs":[{"components":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint128","name":"sqrtPrice","type":"uint128"},{"internalType":"uint24","name":"sqrtGamma","type":"uint24"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"int24","name":"nextTickBelow","type":"int24"},{"internalType":"int24","name":"nextTickAbove","type":"int24"},{"internalType":"uint80","name":"feeGrowthGlobal0","type":"uint80"},{"internalType":"uint80","name":"feeGrowthGlobal1","type":"uint80"}],"internalType":"struct Tiers.Tier","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"getTiersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint24","name":"sqrtGamma","type":"uint24"}],"name":"isSqrtGammaAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxNumOfTiers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"tierChoices","type":"uint256"},{"internalType":"int256","name":"amountDesired","type":"int256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"recipientAccRefId","type":"uint256"},{"internalType":"uint256","name":"senderAccRefId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"int256","name":"amountDesired","type":"int256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"recipientAccRefId","type":"uint256"},{"internalType":"uint256","name":"senderAccRefId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IMuffinHubActions.SwapMultiHopParams","name":"p","type":"tuple"}],"name":"swapMultiHop","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokens","outputs":[{"internalType":"uint8","name":"locked","type":"uint8"},{"internalType":"uint248","name":"protocolFeeAmt","type":"uint248"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"underlyings","outputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"senderAccRefId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6000805461ffff60a01b1916601960a21b1790556101406040526201863c60a0908152620185d860c0526201857460e0526201851061010052620184ab6101205262000050906001906005620000a5565b503480156200005e57600080fd5b50604051620061203803806200612083398101604081905262000081916200016f565b6001600160a01b0316608052600080546001600160a01b03191633179055620001a1565b82805482825590600052602060002090600901600a90048101928215620001465791602002820160005b838211156200011357835183826101000a81548162ffffff021916908362ffffff1602179055509260200192600301602081600201049283019260010302620000cf565b8015620001445782816101000a81549062ffffff021916905560030160208160020104928301926001030262000113565b505b506200015492915062000158565b5090565b5b8082111562000154576000815560010162000159565b6000602082840312156200018257600080fd5b81516001600160a01b03811681146200019a57600080fd5b9392505050565b608051615f63620001bd60003960006101530152615f636000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806384ec1e15116100cd578063c625e3c311610081578063dc65746511610066578063dc657465146105d7578063e3642509146105f7578063e48603391461061757610151565b8063c625e3c3146103f1578063c6883ec5146105c457610151565b8063aa5976c1116100b2578063aa5976c114610387578063aaa9acd2146103b9578063c349e769146103cc57610151565b806384ec1e15146102f65780639aca112e1461036457610151565b80634b2084e3116101245780637266a0e4116101095780637266a0e414610295578063775dfc82146102c05780637951532d146102d357610151565b80634b2084e31461023b5780635aa6e6751461025057610151565b806313fd4c80146101965780631ca0027a146101ac5780632ec31fbc146101be578063476cfd25146101e6575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610191573d6000f35b3d6000fd5b60065b6040519081526020015b60405180910390f35b6101996101ba36600461550b565b5490565b6101d16101cc36600461558f565b61069b565b604080519283526020830191909152016101a3565b60005460ff74010000000000000000000000000000000000000000820481169175010000000000000000000000000000000000000000009004165b6040805160ff9384168152929091166020830152016101a3565b61024e61024936600461562e565b610736565b005b6000546102709073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101a3565b6101996102a3366004615672565b600560209081526000928352604080842090915290825290205481565b6101996102ce3660046156af565b61084e565b6101996102e136600461550b565b60009081526004602052604090206001015490565b61033761030436600461550b565b6007602052600090815260409020805460019091015473ffffffffffffffffffffffffffffffffffffffff918216911682565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152016101a3565b610377610372366004615722565b610b98565b60405190151581526020016101a3565b61022161039536600461550b565b60009081526004602052604090205460ff6101008204811692620100009092041690565b61024e6103c736600461574e565b610ca3565b6103df6103da3660046157c6565b610ded565b60405160ff90911681526020016101a3565b6105596103ff366004615824565b6040805160a081018252600080825260208083018290528284018290526060808401839052608084018390528a8352600482528483208551918b901b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168284015260f889901b7fff0000000000000000000000000000000000000000000000000000000000000016603483015260e888811b603584015287901b6038830152603b8083018b905286518084039091018152605b909201865281519183019190912083526005019052919091206040805160a08101825282546bffffffffffffffffffffffff811682526c01000000000000000000000000810469ffffffffffffffffffff9081166020840152760100000000000000000000000000000000000000000000909104169181019190915260019091015460ff81166060830152610100900463ffffffff166080820152979650505050505050565b6040516101a39190600060a0820190506bffffffffffffffffffffffff8351168252602083015169ffffffffffffffffffff8082166020850152806040860151166040850152505060ff606084015116606083015263ffffffff608084015116608083015292915050565b6101d16105d236600461588a565b610fcf565b6105ea6105e53660046158c5565b61135a565b6040516101a391906158e8565b61060a61060536600461599d565b6114a8565b6040516101a391906159d9565b610662610625366004615a3b565b60066020526000908152604090205460ff81169061010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682565b6040805160ff90931683527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091166020830152016101a3565b60008060006106dd8c8c8c8c60405180606001604052808c81526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018d81525061162a565b90955093509091506106f890508c8c85858c8c8c8c8c6117bf565b6107278180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b50995099975050505050505050565b6000610742338561199b565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600560209081526040808320848452909152902054909150828110156107b0576040517f99f874cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166000908152600560209081526040808320858452909152902083820390556107f08487856119ee565b6040805184815273ffffffffffffffffffffffffffffffffffffffff8881166020830152861691879133917fc5de321f20136e2f86609c5eab638083a3300f55766433c1243a6ea1610f7792910160405180910390a4505050505050565b60008473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1610158061089f575073ffffffffffffffffffffffffffffffffffffffff8616155b156108d6576040517f3f06bf8100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108e460048888611a8b565b925090506108f28286610b98565b610928576040517f423d4ff700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526002602052604090205460ff168061095f575060005474010000000000000000000000000000000000000000900460ff165b6000805481906109929085908a908a9087907501000000000000000000000000000000000000000000900460ff16611b0d565b73ffffffffffffffffffffffffffffffffffffffff8c1660009081526005602052604081209294509092508391906109ca338a61199b565b815260200190815260200160002060008282546109e79190615a85565b909155505073ffffffffffffffffffffffffffffffffffffffff891660009081526005602052604081208291610a1d338a61199b565b81526020019081526020016000206000828254610a3a9190615a85565b9091555050604051859073ffffffffffffffffffffffffffffffffffffffff808c1691908d16907fec5dc6309c83a50f60f4a1fae9422b2c406da78c579b9b12b92d033db37c719490600090a4604080516fffffffffffffffffffffffffffffffff891681526001602082015262ffffff8a169160009188917f8ba9b3074e943a040d7cc32a0a69db4cada877568ff71021a69579a1f004e440910160405180910390a4610b0d8480547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b505060408051808201825273ffffffffffffffffffffffffffffffffffffffff998a1681529789166020808a01918252600086815260079091529190912097518854908a167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161789559051600190980180549890991697169690961790965595945050505050565b6000828152600360205260408120548190610bb4576001610bc3565b60008481526003602052604090205b905060005b8154811015610c96578362ffffff16828281548110610be957610be9615a9c565b90600052602060002090600a91828204019190066003029054906101000a900462ffffff1662ffffff161415610c8e576000858152600460205260408120600101905b8154811015610c81578562ffffff16828281548110610c4d57610c4d615a9c565b600091825260209091206001600290920201015462ffffff161415610c79576000945050505050610c9d565b600101610c2c565b5060019350505050610c9d565b600101610bc8565b5060009150505b92915050565b6000610cae85611c2d565b6040517f641229d9000000000000000000000000000000000000000000000000000000008152909150339063641229d990610cf3908890889088908890600401615b14565b600060405180830381600087803b158015610d0d57600080fd5b505af1158015610d21573d6000803e3d6000fd5b50505050610d3a858583610d359190615b54565b611ccf565b73ffffffffffffffffffffffffffffffffffffffff851660009081526005602052604081208591610d6b8a8a61199b565b81526020019081526020016000206000828254610d889190615b54565b90915550506040805185815233602082015273ffffffffffffffffffffffffffffffffffffffff808816928992918b16917f0b6c6cb502d2da9ef6887b17afbeb0034852ce0a2394ac48028d6b0f7810b63c910160405180910390a450505050505050565b60008080610dfd60048888611a8b565b91509150610e0b8186610b98565b610e41576040517f423d4ff700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610e4e8488611d61565b73ffffffffffffffffffffffffffffffffffffffff8c1660009081526005602052604081209198509294509092508391610e88338a61199b565b81526020019081526020016000206000828254610ea59190615a85565b909155505073ffffffffffffffffffffffffffffffffffffffff881660009081526005602052604081208291610edb338a61199b565b81526020019081526020016000206000828254610ef89190615a85565b925050819055508662ffffff168560ff16847f8ba9b3074e943a040d7cc32a0a69db4cada877568ff71021a69579a1f004e440876001018960ff1681548110610f4357610f43615a9c565b6000918252602080832060029290920290910154604080517001000000000000000000000000000000009092046fffffffffffffffffffffffffffffffff168252918101929092520160405180910390a4610fc38480547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b50505050949350505050565b60008080610fdd8480615b6c565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293506110209250839150611de69050565b15611057576040517f3378279300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602085013581129061106d83516016900490565b67ffffffffffffffff81111561108557611085615bd1565b6040519080825280602002602001820160405280156110ae578160200160208202803683370190505b509050602086013560008361110d576040518060600160405280896080013581526020018960400160208101906110e59190615a3b565b73ffffffffffffffffffffffffffffffffffffffff168152602001896060013581525061112d565b6040805160608101825260808a0135808252336020830152918101919091525b905060005b83518110156112805784156111885760018451038114156111835761115d60608a0160408b01615a3b565b73ffffffffffffffffffffffffffffffffffffffff166020830152606089013560408301525b6111a2565b80600114156111a257336020830152608089013560408301525b600080806111b189858a611e30565b9250925092506000806111df8585858d806111ca575089155b6111d75760648c036111d9565b8b5b8b61162a565b909192508b89815181106111f5576111f5615a9c565b602090810291909101019290925292509050891561121e578561121657819c505b80975061126f565b8561122b57809b50611268565b87600003811015611268576040517f4b5f1a1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160000397505b505060019093019250611132915050565b50831561128f57819550611296565b8160000396505b5060009050806112a68585611e71565b90925090506112e1828289896112c260608e0160408f01615a3b565b8d606001358e608001358f8060a001906112dc9190615b6c565b6117bf565b60005b835181101561134f576113476004600086848151811061130657611306615a9c565b6020026020010151815260200190815260200160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6001016112e4565b505050505050915091565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101919091526000838152600460205260409020600101805460ff84169081106113c1576113c1615a9c565b60009182526020918290206040805161010081018252600293840290920180546fffffffffffffffffffffffffffffffff8082168552700100000000000000000000000000000000909104169483019490945260019093015462ffffff81169382019390935263010000008304820b606082015266010000000000008304820b60808201526901000000000000000000830490910b60a082015269ffffffffffffffffffff6c010000000000000000000000008304811660c083015276010000000000000000000000000000000000000000000090920490911660e0820152905092915050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915250600083815260046020908152604080832060ff808716855260039091018352818420600286810b86529084529382902082516101008101845281546bffffffffffffffffffffffff80821683526c010000000000000000000000008204169582019590955278010000000000000000000000000000000000000000000000008504860b938101939093527b01000000000000000000000000000000000000000000000000000000840490940b60608301527e0100000000000000000000000000000000000000000000000000000000000083048116151560808301527f0100000000000000000000000000000000000000000000000000000000000000909204909116151560a082015260019091015469ffffffffffffffffffff80821660c08401526a01000000000000000000009091041660e08201525b9392505050565b600080808073ffffffffffffffffffffffffffffffffffffffff808916908a16108187138114816116665761166160048b8d611a8b565b611672565b61167260048c8c611a8b565b9096509450600061168687838b8d8a611e89565b9050876020015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16877f6d264ed3495dadcbb82f8e1bf8c4a8498812b3f0543f86b6513630e455dcfe178b600001518c6040015186600001518760200151886040015189606001518a608001516040516117129796959493929190615c00565b60405180910390a460a08101511561178b5760a081015173ffffffffffffffffffffffffffffffffffffffff8d16600090815260066020526040902080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482169094011690920260ff9092169190911790555b8261179f57602081015181516000036117aa565b805160208201516000035b979d969c509a50959850939650505050505050565b8773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415611803576117fd8787612494565b90975095505b83611818576118138886886119ee565b61186c565b73ffffffffffffffffffffffffffffffffffffffff881660009081526005602052604081208791611849888861199b565b815260200190815260200160002060008282546118669190615b54565b90915550505b82156118f257600061187e338561199b565b73ffffffffffffffffffffffffffffffffffffffff8b1660009081526005602090815260408083208484529091529020549091506118bc9089612494565b73ffffffffffffffffffffffffffffffffffffffff8c166000908152600560209081526040808320958352949052929092205596505b86156119905760006119038a611c2d565b6040517ff1371dd5000000000000000000000000000000000000000000000000000000008152909150339063f1371dd59061194c908d908d908d908d908a908a90600401615c72565b600060405180830381600087803b15801561196657600080fd5b505af115801561197a573d6000803e3d6000fd5b5050505061198e8a8983610d359190615b54565b505b505050505050505050565b6000816119a757600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff8516602082015290810183905260600160405160208183030381529060405280519060200120905092915050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050611a4f816124b0565b611a85576040517fbfa871c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6000808383604051602001611ac392919073ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120600081815297909252909520959350505050565b84546000908190610100900460ff1615611b2657600080fd5b6fffffffffffffffffffffffffffffffff85166201000311801590611b6d57506ffffdd8371ce3ef742f98c78a4732240d6fffffffffffffffffffffffffffffffff861611155b611b7657600080fd5b60008460ff1611611b8657600080fd5b865460ff84811662010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff91871661010002919091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff90921691909117178755611bf48787876124f5565b60069890980180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559795505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8216738dd5fbce2f6a956c3022ba3663759011dd51e73e1415611c6657600080fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600660205260409020805460ff1660011415611c9d57600080fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178155611623836128ed565b80611cd9836128ed565b1015611d11576040517fde2cd50d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5073ffffffffffffffffffffffffffffffffffffffff16600090815260066020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002179055565b6000806000611d6f85612a26565b50600184015460ff8116611d8257600080fd5b611dd9858587600101600081548110611d9d57611d9d615a9c565b600091825260209091206002909102015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff166124f5565b9096909550909350915050565b80516000906116141080611dfc57506014825111155b80610c9d5750505160167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec90910106151590565b6000808060168502611e458782016014015190565b878201602a810151601690910151919550935061ffff16915084611e67579192915b5093509350939050565b6014820151825183015182611e8257905b9250929050565b611ec26040518060c001604052806000815260200160008152602001600081526020016000815260200160608152602001600081525090565b611ecb86612a26565b6060611ed5615489565b60018881015490811b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0195861695871580611f3057507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff88145b15611f67576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86611f9e576040517fd2a0a82900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808714156120c55789600101805480602002602001604051908101604052809291908181526020016000905b828210156120ba5760008481526020908190206040805161010081018252600280870290930180546fffffffffffffffffffffffffffffffff8082168452700100000000000000000000000000000000909104168286015260019081015462ffffff81169383019390935263010000008304840b606083015266010000000000008304840b60808301526901000000000000000000830490930b60a082015269ffffffffffffffffffff6c010000000000000000000000008304811660c083015276010000000000000000000000000000000000000000000090920490911660e082015283529092019101611fca565b5050505093506122a3565b8167ffffffffffffffff8111156120de576120de615bd1565b60405190808252806020026020018201604052801561216b57816020015b604080516101008101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816120fc5790505b50935060005b84518110156122a1576001811b881615612299578a600101818154811061219a5761219a615a9c565b60009182526020918290206040805161010081018252600293840290920180546fffffffffffffffffffffffffffffffff8082168552700100000000000000000000000000000000909104169483019490945260019093015462ffffff81169382019390935263010000008304820b606082015266010000000000008304820b60808201526901000000000000000000830490910b60a082015269ffffffffffffffffffff6c010000000000000000000000008304811660c083015276010000000000000000000000000000000000000000000090920490911660e0820152855186908390811061228d5761228d615a9c565b60200260200101819052505b600101612171565b505b5050604080516101008101825260008089138a1515811483526020808401919091528b5462010000900460ff168385015260608301829052608083018990528351808501909452627fffff8452830181905260a082019290925260c08101612309612a5c565b81526020018690529050866000805b836020015161233657612331868c8c8760800151612a67565b612346565b612346868c8c8760800151612d17565b60c085015260005b86518110156123be576000806123978f8f898b876006811061237257612372615a9c565b60200201518d888151811061238957612389615a9c565b602002602001015188613080565b90925090506123a68286615cf3565b94506123b28185615cf3565b9350505060010161234e565b506123c98284615d67565b995083602001516123fd577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c8a1215612403565b60648a13155b8061241057506080840151155b1561241a5761241f565b612318565b83606001518760a001818152505061245f8c868887602001516124425784612444565b855b88602001516124565786600003613602565b85600003613602565b60808a0152606089015260408801528a61247a57808261247d565b81815b6020890152875250949a9950505050505050505050565b6000808284106124a8578284039150611e82565b509291900390565b6000816124c1573d6000803e3d6000fd5b3d602081146124d95780156124ea57600091506124ef565b3d6000803e600051151591506124ef565b600191505b50919050565b600183015460009081906006811061250c57600080fd5b620186a062ffffff8616111561252157600080fd5b604080516101008101825261640081526fffffffffffffffffffffffffffffffff8616602082015262ffffff87169181019190915260009060608101612566876139c3565b60020b81527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff427556020820152620bd8ab604082015260006060820181905260809091015290506fffffffffffffffffffffffffffffffff85166ffffdd8371ce3ef742f98c78a4732240d14156125ec57606081018051906125e582615ddb565b60020b9052505b600180880180548083018255600091825260208083208551828701516fffffffffffffffffffffffffffffffff9081167001000000000000000000000000000000000291161760029384029091019081556040808701519190950180546060880151608089015160a08a015160c08b015160e08c015169ffffffffffffffffffff9081167601000000000000000000000000000000000000000000000275ffffffffffffffffffffffffffffffffffffffffffff919092166c0100000000000000000000000002166bffffffffffffffffffffffff62ffffff9384166901000000000000000000027fffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff958516660100000000000002959095167fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff9685166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000909816949099169390931795909517939093169590951717939093161791909117905585835260038b0181528383207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755808552818352858520620bd8ab865291835285852082547d0bd8abf427550000000000000000000000000000000000000000000000647fffff000000000000ffffffffffffffffffffffff00000000000000000000000090911617835580547d0bd8abf427550000000000000000000000640000000000000000000000007fffff000000000000000000000000000000000000ffffffffffffffffffffffff909116178155888652938d0190925293909220909161285e9190613f46565b600084815260028a016020526040902061287b90620bd8ab613f46565b6128a86a64000000000000000000006fffffffffffffffffffffffffffffffff8916808204910615150190565b95506128df6128ca6fffffffffffffffffffffffffffffffff89166064615e39565b68010000000000000000808204910615150190565b945050505050935093915050565b604080513060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905290516000918291829173ffffffffffffffffffffffffffffffffffffffff86169161297f9190615e76565b600060405180830381855afa9150503d80600081146129ba576040519150601f19603f3d011682016040523d82523d6000602084013e6129bf565b606091505b50915091508115806129d357508051602014155b15612a0a576040517f77b1b94e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80806020019051810190612a1e9190615eb1565b949350505050565b805460ff16612a3457600080fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b612a646154ed565b90565b612a6f6154ed565b60008312612a7f57612a7f615eca565b612a876154ed565b612a8f6154ed565b60008060005b8951811015612bd4576001811b8716612ae5577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612adb57612adb615a9c565b6020020152612bcc565b60008a8281518110612af957612af9615a9c565b60200260200101519050600081600001516fffffffffffffffffffffffffffffffff169050816040015162ffffff1681620186a00281612b3b57612b3b615cc4565b04878460068110612b4e57612b4e615a9c565b6020020181815250850194508a612b805760208201516fffffffffffffffffffffffffffffffff16810260481c612bab565b81602001516fffffffffffffffffffffffffffffffff16604882901b81612ba957612ba9615cc4565b045b868460068110612bbd57612bbd615a9c565b60200201818152508401935050505b600101612a95565b50612bdf8782615cf3565b905060005b8951811015612d0a577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612c2057612c20615a9c565b602002015114612d02576000612c6d858360068110612c4157612c41615a9c565b6020020151612c6785898660068110612c5c57612c5c615a9c565b602002015188613fa2565b90613fda565b878360068110612c7f57612c7f615a9c565b602002018190521315612d02577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612cbf57612cbf615a9c565b6020020152848160068110612cd657612cd6615a9c565b602002015183039250838160068110612cf157612cf1615a9c565b602002015190910390506000612be4565b600101612be4565b5050505050949350505050565b612d1f6154ed565b60008313612d2f57612d2f615eca565b612d376154ed565b612d3f6154ed565b60008060005b8951811015612ebc576001811b8716612d95577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612d8b57612d8b615a9c565b6020020152612eb4565b60008a8281518110612da957612da9615a9c565b6020908102919091010151805160408201519192506fffffffffffffffffffffffffffffffff1690612deb620186a0830262ffffff8316808204910615150190565b888560068110612dfd57612dfd615a9c565b6020020181815250860195508b612e56576020830151612e51906fffffffffffffffffffffffffffffffff1683026402540be40062ffffff8416800269010000000000000000000204808204910615150190565b612e92565b60208301516fffffffffffffffffffffffffffffffff1662ffffff8216908102026d02540be40000000000000000000083028181049190061515015b878560068110612ea457612ea4615a9c565b6020020181815250850194505050505b600101612d45565b50612ec78782615b54565b9050818102600083838381612ede57612ede615cc4565b04148015612f0c57507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8211155b905060005b8b51811015613071577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff888260068110612f4d57612f4d615a9c565b602002015114613069576000612fd4878360068110612f6e57612f6e615a9c565b602002015184612fa657612fa1612f9c888c8760068110612f9157612f91615a9c565b60200201518b613fe6565b6140b3565b612c67565b878a8560068110612fb957612fb9615a9c565b6020020151880281612fcd57612fcd615cc4565b0490613fda565b898360068110612fe657612fe6615a9c565b602002018190521215613069577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff88826006811061302657613026615a9c565b602002015286816006811061303d5761303d615a9c565b60200201518503945085816006811061305857613058615a9c565b602002015190930392506000612f11565b600101612f11565b50505050505050949350505050565b6000807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8660c0015184600681106130ba576130ba615a9c565b602002015114156130d0575060009050806135f7565b84516fffffffffffffffffffffffffffffffff166131225761310d8660a001518760000151613103578560a001516140e9565b85608001516140e9565b6fffffffffffffffffffffffffffffffff1685525b60006131618888602001518960c00151876006811061314357613143615a9c565b602002015188602001518a600001518a600001518b60400151614137565b6fffffffffffffffffffffffffffffffff9091166020890152919450925090507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8314156131b65760008092509250506135f7565b8660200151156131dd576020860180518401905260408601805160008490030190526131f6565b6020860180518301905260408601805160008590030190525b60408781015160608901805160ff92831685029290920491820190528651928190039290916000916fffffffffffffffffffffffffffffffff169084901b8161324157613241615cc4565b0490508860000151156132695760c087018051820169ffffffffffffffffffff169052613280565b60e087018051820169ffffffffffffffffffff1690525b50505084600001516fffffffffffffffffffffffffffffffff1684602001516fffffffffffffffffffffffffffffffff1614156135f75785516000906132ca578460a001516132d0565b84608001515b9050600281900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755148061330a5750600281900b620bd8ab145b1561332357506080860180516001851b191690526135f7565b60008087526001606088015284815260038a0160209081526040808320600285900b8452909152902060c086015160e08701516133e1918391600190920180546a010000000000000000000069ffffffffffffffffffff80831690940384167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000008316811782900485169095039093169092027fffffffffffffffffffffffff0000000000000000000000000000000000000000909216909217179055565b805488516bffffffffffffffffffffffff808316926c01000000000000000000000000900416901561347d57875170ffffffffffffffffffffffffffffffff00600884811b82169084901b90911691909101036fffffffffffffffffffffffffffffffff168852825478010000000000000000000000000000000000000000000000009004600290810b60808a015284900b60a08901526134eb565b875170ffffffffffffffffffffffffffffffff00600883811b82169085901b90911691909101036fffffffffffffffffffffffffffffffff168852600284810b60808a015283547b010000000000000000000000000000000000000000000000000000009004900b60a08901525b505087516135205780547f0100000000000000000000000000000000000000000000000000000000000000900460ff16613548565b80547e01000000000000000000000000000000000000000000000000000000000000900460ff165b156135f457600085815260048b016020908152604080832060038e01835281842060028f019093529083208b518493613587939290918c90899061436f565b60e08c01519193509150156135f15760e08a015160408051600285810b82526bffffffffffffffffffffffff8516602083015287900b9260ff8b169290917f2a7e6f8c2d4129d4221502dbf7923a55b65530b0630c7b4a2303e5a4f8f46557910160405180910390a45b50505b50505b965096945050505050565b6000806060855167ffffffffffffffff81111561362157613621615bd1565b60405190808252806020026020018201604052801561364a578160200160208202803683370190505b5090507a80000000000000000000000000000000000000000000000000000080861090851060005b88518110156139b55760008a826006811061368f5761368f615a9c565b602002015190506000816020015111806136aa575080606001515b156139ac5760008a83815181106136c3576136c3615a9c565b602002602001015190506136da81602001516139c3565b600290810b6060830181905260a083015190910b1415613722576060810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0160020b90525b808d600101848154811061373857613738615a9c565b60009182526020918290208351848401516fffffffffffffffffffffffffffffffff90811670010000000000000000000000000000000002918116919091176002909302909101918255604084015160019092018054606086015160808088015160a089015160c08a015160e0909a015169ffffffffffffffffffff9081167601000000000000000000000000000000000000000000000275ffffffffffffffffffffffffffffffffffffffffffff91909b166c0100000000000000000000000002166bffffffffffffffffffffffff62ffffff9283166901000000000000000000027fffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff948416660100000000000002949094167fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff9684166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000090981693909a1692909217959095179390931696909617959095171617949094179093558351918401518951911b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000169190921617908790859081106138ff576138ff615a9c565b6020908102919091010152891561395a57602a83028561393a57602083015160298c901c600101908161393457613934615cc4565b04613953565b60208301518b9060291b8161395157613951615cc4565b045b901b881797505b88156139aa57602a83028461398a57604083015160298b901c600101908161398457613984615cc4565b046139a3565b60408301518a9060291b816139a1576139a1615cc4565b045b901b871796505b505b50600101613672565b505050955095509592505050565b60006fffffffffffffffffffffffffffffffff82166201000311801590613a0c57506ffffdd8371ce3ef742f98c78a4732240d6fffffffffffffffffffffffffffffffff831611155b613a1557600080fd5b6fffffffffffffffffffffffffffffffff8216806000680100000000000000008210613a4357604091821c91015b6401000000008210613a5757602091821c91015b620100008210613a6957601091821c91015b6101008210613a7a57600891821c91015b60108210613a8a57600491821c91015b60048210613a9a57600291821c91015b60028210613aaa57600191821c91015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8810160401b607f82810385901b8002901c7001000000000000000000000000000000008110613b0657678000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613b3557674000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613b6457672000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613b9357671000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613bc257670800000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613bf157670400000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613c2057670200000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613c4f57670100000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613c7d576680000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613cab576640000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613cd9576620000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d07576610000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d35576608000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d63576604000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d91576602000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613dbf576601000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613dec5765800000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613e195765400000000000919091179060011c5b693627a301d71055774c8591909102906f0d89e7aecf44b7384157000000000001820160801d60007ffffffffffffffffffffffffffff5adf5000000000000000000000000000000008412613ec4577ffffffffffffffffffffffffffff8bb35000000000000000000000000000000008412613e9957608084901d613eeb565b7fffffffffffffffffffffffffffffffffff9a58f534e18c8f54aaffffffffffff840160801d613eeb565b7fffffffffffffffffffffffffffffffffb5d6bb93c93a19febd193fffffffffff840160801d5b90508060020b8260020b1480613f2d5750613f0582614ae2565b6fffffffffffffffffffffffffffffffff16896fffffffffffffffffffffffffffffffff1610155b613f375780613f39565b815b9998505050505050505050565b6000806000613f5484614e3a565b600082815260028901602090815260408083208054600160ff96871681901b909117909155868452808c0190925290912080549290931681901b9091179091558654911b1790945550505050565b6000808412613fbe57613fb9612f9c858585614e67565b612a1e565b613fcf612f9c856000038585613fe6565b600003949350505050565b60006116238284615d67565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858709858702925082811083820303915050806000141561403e576000841161403357600080fd5b508290049050611623565b80841161404a57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8211156140e5576140e5615eca565b5090565b6000826000015160020b8260020b141561410857506020820151610c9d565b61411182614ae2565b6fffffffffffffffffffffffffffffffff8116602085015260029290920b909252919050565b846000808080808c6141535761414e8a8a8a614ea2565b61415e565b61415e8a8a8a614f5d565b905062ffffff871680028c15614264577b6df37f675ef6eadf5ab9a2072d44268d97df837e6748956e5c6c211787126141a057806402540be4008804026141ab565b6402540be400878202045b9250818312156141dc578d6141ca576141c58b8a8561505e565b6141d5565b6141d58b8a856151cf565b9450614238565b8994508192506142357b6df37f675ef6eadf5ab9a2072d44268d97df837e6748956e5c6c2117841261421e578184061515828504015b6402540be400026140b3565b612f9c846402540be4000283808204910615150190565b96505b8d61424d576142488b868b614f5d565b614258565b6142588b868b614ea2565b955082870393506142f8565b81871315614293578d6142815761427c8b8a8961505e565b61428c565b61428c8b8a896151cf565b945061429a565b8994508196505b8d6142af576142aa8b868b614f5d565b6142ba565b6142ba8b868b614ea2565b92506142f07b6df37f675ef6eadf5ab9a2072d44268d97df837e6748956e5c6c2117841261421e57818406151582850401614212565b955082860393505b8215801561432a5750896fffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff1614155b1561435e577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9650600095508a9450600093505b505050975097509750979350505050565b600282900b6000908152602086905260408120819081908190851561446557600287900b600090815260208c90526040812090600290810291909101805461ffff6c010000000000000000000000008083049190911660010b8b019384900b600090815260208f90526040902080546bffffffffffffffffffffffff938416838204851681900385169093027fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff90911617815585547fff00ffffffffffffffffffffffffffffffffffff000000000000000000000000811690841683900390931692909217855592975091955093509150614556565b600287900b600090815260208c905260409020600160029081029190910180546c0100000000000000000000000080820461ffff1660010b8b039384900b600090815260208f90526040902080547fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081166bffffffffffffffffffffffff948516918516829003851617825586547effffffffffffff000000000000000000000000ffffffffffffffffffffffff81169084900485168290039094169092027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16929092178555929750919550935091505b8660020b8560020b141561456c5761456c615eca565b6040805180820182526001808501548482015469ffffffffffffffffffff80831682821603811685526a01000000000000000000009283900481169183900481169190910381166020808601918252895463ffffffff6e0100000000000000000000000000009182900481166000908152968c019092529690942094518554915183169093027fffffffffffffffffffffffff00000000000000000000000000000000000000009091169290911691909117179091558454919091041683600e61463583615ef9565b825463ffffffff9182166101009390930a92830291909202199091161790555082547fffffffffffffffffffffffffffffffffffff000000000000000000000000000016835581546bffffffffffffffffffffffff161580156146b5575081546c0100000000000000000000000090046bffffffffffffffffffffffff16155b156148a057600285900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755148015906146f55750600285900b620bd8ab14155b61470157614701615eca565b60008260000160189054906101000a900460020b9050600083600001601b9054906101000a900460020b9050808c60008460020b60020b8152602001908152602001600020600001601b6101000a81548162ffffff021916908360020b62ffffff160217905550818c60008360020b60020b815260200190815260200160002060000160186101000a81548162ffffff021916908360020b62ffffff1602179055508b60008860020b60020b8152602001908152602001600020600080820160006101000a8154906bffffffffffffffffffffffff021916905560008201600c6101000a8154906bffffffffffffffffffffffff02191690556000820160186101000a81549062ffffff021916905560008201601b6101000a81549062ffffff021916905560008201601e6101000a81549060ff021916905560008201601f6101000a81549060ff02191690556001820160006101000a81549069ffffffffffffffffffff021916905560018201600a6101000a81549069ffffffffffffffffffff0219169055505061489d878c6153dc90919063ffffffff16565b50505b80546bffffffffffffffffffffffff161580156148da575080546c0100000000000000000000000090046bffffffffffffffffffffffff16155b15614ad457600287900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff427551480159061491a5750600287900b620bd8ab14155b61492657614926615eca565b60008160000160189054906101000a900460020b9050600082600001601b9054906101000a900460020b9050808c60008460020b60020b8152602001908152602001600020600001601b6101000a81548162ffffff021916908360020b62ffffff160217905550818c60008360020b60020b815260200190815260200160002060000160186101000a81548162ffffff021916908360020b62ffffff1602179055508b60008a60020b60020b8152602001908152602001600020600080820160006101000a8154906bffffffffffffffffffffffff021916905560008201600c6101000a8154906bffffffffffffffffffffffff02191690556000820160186101000a81549062ffffff021916905560008201601b6101000a81549062ffffff021916905560008201601e6101000a81549060ff021916905560008201601f6101000a81549060ff02191690556001820160006101000a81549069ffffffffffffffffffff021916905560018201600a6101000a81549069ffffffffffffffffffff02191690555050614ac2898c6153dc90919063ffffffff16565b600291820b60808b0152900b60a08901525b505050965096945050505050565b6000600282900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4275513801590614b1f5750620bd8ab600283900b13155b614b2857600080fd5b6000808360020b12614b3a5782614b3f565b826000035b62ffffff811691507001000000000000000000000000000000009060011615614b78576ffffcb933bd6fad37aa2d162d1a5940010260801c5b6002821615614b97576ffff97272373d413259a46990580e213a0260801c5b6004821615614bb6576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614bd5576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615614bf4576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615614c13576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615614c32576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615614c51576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615614c71576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615614c91576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615614cb1576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615614cd1576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615614cf1576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615614d11576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615614d31576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615614d51576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615614d72576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615614d92576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614db1576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614dce576b048a170391f7dc42444e8fa20260801c5b60008460020b12614e0c57807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81614e0857614e08615cc4565b0490505b6000670100000000000000820611614e25576000614e28565b60015b60ff16603882901c0192505050919050565b620bd8ab810160020b601081901c90600881901c906101008310614e6057614e60615eca565b9193909250565b6000614e74848484613fe6565b905060008280614e8657614e86615cc4565b84860911156116235780614e9981615f1d565b95945050505050565b60006fffffffffffffffffffffffffffffffff808516908416108015614ec6579293925b60008585036fffffffffffffffffffffffffffffffff16846fffffffffffffffffffffffffffffffff16029050614f2682614f1c5768ffffffffffffffffff8216151569010000000000000000008304016140b3565b604882901c6140b3565b92508115614f54577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830292505b50509392505050565b60006fffffffffffffffffffffffffffffffff808516908416118015614f81579293925b6fffffffffffffffffffffffffffffffff8381168587038216029080871690861602615026770100000000000000000000000000000000000000000000008310614ff45783614fdf57612f9c83690100000000000000000084614e67565b612f9c83690100000000000000000084613fe6565b8361500d57612f9c604884901b83808204910615150190565b81604884901b8161502057615020615cc4565b046140b3565b93508215615054577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff840293505b5050509392505050565b600080821215615132576fffffffffffffffffffffffffffffffff831661508457600080fd5b6000828103907701000000000000000000000000000000000000000000000082106150d5576150d0826901000000000000000000876fffffffffffffffffffffffffffffffff16614e67565b615103565b61510369010000000000000000008302866fffffffffffffffffffffffffffffffff16808204910615150190565b90506151296151246fffffffffffffffffffffffffffffffff88168361544f565b61545b565b92505050611623565b600077010000000000000000000000000000000000000000000000831061517f5761517a836901000000000000000000866fffffffffffffffffffffffffffffffff16613fe6565b6151ae565b836fffffffffffffffffffffffffffffffff1669010000000000000000008402816151ac576151ac615cc4565b045b9050614e996151246fffffffffffffffffffffffffffffffff87168361547d565b6000816151dd575082611623565b60008083126151ec57826151f1565b826000035b90506fffffffffffffffffffffffffffffffff8516810278ffffffffffffffffffffffffffffffff000000000000000000604886901b1660008086131561531a57876fffffffffffffffffffffffffffffffff1684848161525457615254615cc4565b04148015615266575050818101818110155b156152d8576fffffffffffffffffffffffffffffffff878116908916027701000000000000000000000000000000000000000000000081106152bc576152b781690100000000000000000084614e67565b6152d0565b6152d0604882901b83808204910615150190565b9550506153d1565b61531382615308868b6fffffffffffffffffffffffffffffffff16868161530157615301615cc4565b049061547d565b808204910615150190565b94506153d1565b876fffffffffffffffffffffffffffffffff1684848161533c5761533c615cc4565b041461534757600080fd5b508181038181111561535857600080fd5b8061536257600080fd5b6fffffffffffffffffffffffffffffffff878116908916027701000000000000000000000000000000000000000000000081106153b6576153b161512482690100000000000000000085614e67565b6153cd565b6153cd615124604883901b84808204910615150190565b9550505b505050509392505050565b60008060006153ea84614e3a565b600082815260028901602052604090208054600160ff84161b191690819055929550909350915061544857600083815260018681016020526040909120805460ff85169290921b19909116908190556154485784546001841b191685555b5050505050565b60006116238284615a85565b60006fffffffffffffffffffffffffffffffff8211156140e5576140e5615eca565b60006116238284615b54565b6040518060c001604052806006905b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816154985790505090565b6040518060c001604052806006906020820280368337509192915050565b60006020828403121561551d57600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461554857600080fd5b919050565b60008083601f84011261555f57600080fd5b50813567ffffffffffffffff81111561557757600080fd5b602083019150836020828501011115611e8257600080fd5b60008060008060008060008060006101008a8c0312156155ae57600080fd5b6155b78a615524565b98506155c560208b01615524565b975060408a0135965060608a013595506155e160808b01615524565b945060a08a0135935060c08a0135925060e08a013567ffffffffffffffff81111561560b57600080fd5b6156178c828d0161554d565b915080935050809150509295985092959850929598565b6000806000806080858703121561564457600080fd5b61564d85615524565b93506020850135925061566260408601615524565b9396929550929360600135925050565b6000806040838503121561568557600080fd5b61568e83615524565b946020939093013593505050565b803562ffffff8116811461554857600080fd5b600080600080600060a086880312156156c757600080fd5b6156d086615524565b94506156de60208701615524565b93506156ec6040870161569c565b925060608601356fffffffffffffffffffffffffffffffff8116811461571157600080fd5b949793965091946080013592915050565b6000806040838503121561573557600080fd5b823591506157456020840161569c565b90509250929050565b60008060008060008060a0878903121561576757600080fd5b61577087615524565b95506020870135945061578560408801615524565b935060608701359250608087013567ffffffffffffffff8111156157a857600080fd5b6157b489828a0161554d565b979a9699509497509295939492505050565b600080600080608085870312156157dc57600080fd5b6157e585615524565b93506157f360208601615524565b92506156626040860161569c565b803560ff8116811461554857600080fd5b8035600281900b811461554857600080fd5b60008060008060008060c0878903121561583d57600080fd5b8635955061584d60208801615524565b94506040870135935061586260608801615801565b925061587060808801615812565b915061587e60a08801615812565b90509295509295509295565b60006020828403121561589c57600080fd5b813567ffffffffffffffff8111156158b357600080fd5b820160c0818503121561162357600080fd5b600080604083850312156158d857600080fd5b8235915061574560208401615801565b6000610100820190506fffffffffffffffffffffffffffffffff8084511683528060208501511660208401525062ffffff6040840151166040830152606083015160020b60608301526080830151615945608084018260020b9052565b5060a083015161595a60a084018260020b9052565b5060c083015161597860c084018269ffffffffffffffffffff169052565b5060e083015161599660e084018269ffffffffffffffffffff169052565b5092915050565b6000806000606084860312156159b257600080fd5b833592506159c260208501615801565b91506159d060408501615812565b90509250925092565b6000610100820190506bffffffffffffffffffffffff80845116835280602085015116602084015250604083015160020b6040830152606083015160020b606083015260808301511515608083015260a083015161595a60a084018215159052565b600060208284031215615a4d57600080fd5b61162382615524565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015615a9757615a97615a56565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000615b4a606083018486615acb565b9695505050505050565b60008219821115615b6757615b67615a56565b500190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615ba157600080fd5b83018035915067ffffffffffffffff821115615bbc57600080fd5b602001915036819003821315611e8257600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060e08201898352602089818501528860408501528760608501528660808501528560a085015260e060c085015281855180845261010086019150828701935060005b81811015615c6057845183529383019391830191600101615c44565b50909c9b505050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a06080830152615cb860a083018486615acb565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615615d2d57615d2d615a56565b827f8000000000000000000000000000000000000000000000000000000000000000038412811615615d6157615d61615a56565b50500190565b6000808312837f800000000000000000000000000000000000000000000000000000000000000001831281151615615da157615da1615a56565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018313811615615dd557615dd5615a56565b50500390565b60008160020b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000811415615e1157615e11615a56565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615e7157615e71615a56565b500290565b6000825160005b81811015615e975760208186018101518583015201615e7d565b81811115615ea6576000828501525b509190910192915050565b600060208284031215615ec357600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600063ffffffff80831681811415615f1357615f13615a56565b6001019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615f4f57615f4f615a56565b506001019056fea164736f6c634300080a000a0000000000000000000000005dd2444a17edc079210077924906d5bdf432a858Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101515760003560e01c806384ec1e15116100cd578063c625e3c311610081578063dc65746511610066578063dc657465146105d7578063e3642509146105f7578063e48603391461061757610151565b8063c625e3c3146103f1578063c6883ec5146105c457610151565b8063aa5976c1116100b2578063aa5976c114610387578063aaa9acd2146103b9578063c349e769146103cc57610151565b806384ec1e15146102f65780639aca112e1461036457610151565b80634b2084e3116101245780637266a0e4116101095780637266a0e414610295578063775dfc82146102c05780637951532d146102d357610151565b80634b2084e31461023b5780635aa6e6751461025057610151565b806313fd4c80146101965780631ca0027a146101ac5780632ec31fbc146101be578063476cfd25146101e6575b7f0000000000000000000000005dd2444a17edc079210077924906d5bdf432a8583660008037600080366000845af43d6000803e808015610191573d6000f35b3d6000fd5b60065b6040519081526020015b60405180910390f35b6101996101ba36600461550b565b5490565b6101d16101cc36600461558f565b61069b565b604080519283526020830191909152016101a3565b60005460ff74010000000000000000000000000000000000000000820481169175010000000000000000000000000000000000000000009004165b6040805160ff9384168152929091166020830152016101a3565b61024e61024936600461562e565b610736565b005b6000546102709073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101a3565b6101996102a3366004615672565b600560209081526000928352604080842090915290825290205481565b6101996102ce3660046156af565b61084e565b6101996102e136600461550b565b60009081526004602052604090206001015490565b61033761030436600461550b565b6007602052600090815260409020805460019091015473ffffffffffffffffffffffffffffffffffffffff918216911682565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152016101a3565b610377610372366004615722565b610b98565b60405190151581526020016101a3565b61022161039536600461550b565b60009081526004602052604090205460ff6101008204811692620100009092041690565b61024e6103c736600461574e565b610ca3565b6103df6103da3660046157c6565b610ded565b60405160ff90911681526020016101a3565b6105596103ff366004615824565b6040805160a081018252600080825260208083018290528284018290526060808401839052608084018390528a8352600482528483208551918b901b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168284015260f889901b7fff0000000000000000000000000000000000000000000000000000000000000016603483015260e888811b603584015287901b6038830152603b8083018b905286518084039091018152605b909201865281519183019190912083526005019052919091206040805160a08101825282546bffffffffffffffffffffffff811682526c01000000000000000000000000810469ffffffffffffffffffff9081166020840152760100000000000000000000000000000000000000000000909104169181019190915260019091015460ff81166060830152610100900463ffffffff166080820152979650505050505050565b6040516101a39190600060a0820190506bffffffffffffffffffffffff8351168252602083015169ffffffffffffffffffff8082166020850152806040860151166040850152505060ff606084015116606083015263ffffffff608084015116608083015292915050565b6101d16105d236600461588a565b610fcf565b6105ea6105e53660046158c5565b61135a565b6040516101a391906158e8565b61060a61060536600461599d565b6114a8565b6040516101a391906159d9565b610662610625366004615a3b565b60066020526000908152604090205460ff81169061010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682565b6040805160ff90931683527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091166020830152016101a3565b60008060006106dd8c8c8c8c60405180606001604052808c81526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018d81525061162a565b90955093509091506106f890508c8c85858c8c8c8c8c6117bf565b6107278180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b50995099975050505050505050565b6000610742338561199b565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600560209081526040808320848452909152902054909150828110156107b0576040517f99f874cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166000908152600560209081526040808320858452909152902083820390556107f08487856119ee565b6040805184815273ffffffffffffffffffffffffffffffffffffffff8881166020830152861691879133917fc5de321f20136e2f86609c5eab638083a3300f55766433c1243a6ea1610f7792910160405180910390a4505050505050565b60008473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1610158061089f575073ffffffffffffffffffffffffffffffffffffffff8616155b156108d6576040517f3f06bf8100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108e460048888611a8b565b925090506108f28286610b98565b610928576040517f423d4ff700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526002602052604090205460ff168061095f575060005474010000000000000000000000000000000000000000900460ff165b6000805481906109929085908a908a9087907501000000000000000000000000000000000000000000900460ff16611b0d565b73ffffffffffffffffffffffffffffffffffffffff8c1660009081526005602052604081209294509092508391906109ca338a61199b565b815260200190815260200160002060008282546109e79190615a85565b909155505073ffffffffffffffffffffffffffffffffffffffff891660009081526005602052604081208291610a1d338a61199b565b81526020019081526020016000206000828254610a3a9190615a85565b9091555050604051859073ffffffffffffffffffffffffffffffffffffffff808c1691908d16907fec5dc6309c83a50f60f4a1fae9422b2c406da78c579b9b12b92d033db37c719490600090a4604080516fffffffffffffffffffffffffffffffff891681526001602082015262ffffff8a169160009188917f8ba9b3074e943a040d7cc32a0a69db4cada877568ff71021a69579a1f004e440910160405180910390a4610b0d8480547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b505060408051808201825273ffffffffffffffffffffffffffffffffffffffff998a1681529789166020808a01918252600086815260079091529190912097518854908a167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161789559051600190980180549890991697169690961790965595945050505050565b6000828152600360205260408120548190610bb4576001610bc3565b60008481526003602052604090205b905060005b8154811015610c96578362ffffff16828281548110610be957610be9615a9c565b90600052602060002090600a91828204019190066003029054906101000a900462ffffff1662ffffff161415610c8e576000858152600460205260408120600101905b8154811015610c81578562ffffff16828281548110610c4d57610c4d615a9c565b600091825260209091206001600290920201015462ffffff161415610c79576000945050505050610c9d565b600101610c2c565b5060019350505050610c9d565b600101610bc8565b5060009150505b92915050565b6000610cae85611c2d565b6040517f641229d9000000000000000000000000000000000000000000000000000000008152909150339063641229d990610cf3908890889088908890600401615b14565b600060405180830381600087803b158015610d0d57600080fd5b505af1158015610d21573d6000803e3d6000fd5b50505050610d3a858583610d359190615b54565b611ccf565b73ffffffffffffffffffffffffffffffffffffffff851660009081526005602052604081208591610d6b8a8a61199b565b81526020019081526020016000206000828254610d889190615b54565b90915550506040805185815233602082015273ffffffffffffffffffffffffffffffffffffffff808816928992918b16917f0b6c6cb502d2da9ef6887b17afbeb0034852ce0a2394ac48028d6b0f7810b63c910160405180910390a450505050505050565b60008080610dfd60048888611a8b565b91509150610e0b8186610b98565b610e41576040517f423d4ff700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080610e4e8488611d61565b73ffffffffffffffffffffffffffffffffffffffff8c1660009081526005602052604081209198509294509092508391610e88338a61199b565b81526020019081526020016000206000828254610ea59190615a85565b909155505073ffffffffffffffffffffffffffffffffffffffff881660009081526005602052604081208291610edb338a61199b565b81526020019081526020016000206000828254610ef89190615a85565b925050819055508662ffffff168560ff16847f8ba9b3074e943a040d7cc32a0a69db4cada877568ff71021a69579a1f004e440876001018960ff1681548110610f4357610f43615a9c565b6000918252602080832060029290920290910154604080517001000000000000000000000000000000009092046fffffffffffffffffffffffffffffffff168252918101929092520160405180910390a4610fc38480547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b50505050949350505050565b60008080610fdd8480615b6c565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293506110209250839150611de69050565b15611057576040517f3378279300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602085013581129061106d83516016900490565b67ffffffffffffffff81111561108557611085615bd1565b6040519080825280602002602001820160405280156110ae578160200160208202803683370190505b509050602086013560008361110d576040518060600160405280896080013581526020018960400160208101906110e59190615a3b565b73ffffffffffffffffffffffffffffffffffffffff168152602001896060013581525061112d565b6040805160608101825260808a0135808252336020830152918101919091525b905060005b83518110156112805784156111885760018451038114156111835761115d60608a0160408b01615a3b565b73ffffffffffffffffffffffffffffffffffffffff166020830152606089013560408301525b6111a2565b80600114156111a257336020830152608089013560408301525b600080806111b189858a611e30565b9250925092506000806111df8585858d806111ca575089155b6111d75760648c036111d9565b8b5b8b61162a565b909192508b89815181106111f5576111f5615a9c565b602090810291909101019290925292509050891561121e578561121657819c505b80975061126f565b8561122b57809b50611268565b87600003811015611268576040517f4b5f1a1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160000397505b505060019093019250611132915050565b50831561128f57819550611296565b8160000396505b5060009050806112a68585611e71565b90925090506112e1828289896112c260608e0160408f01615a3b565b8d606001358e608001358f8060a001906112dc9190615b6c565b6117bf565b60005b835181101561134f576113476004600086848151811061130657611306615a9c565b6020026020010151815260200190815260200160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6001016112e4565b505050505050915091565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101919091526000838152600460205260409020600101805460ff84169081106113c1576113c1615a9c565b60009182526020918290206040805161010081018252600293840290920180546fffffffffffffffffffffffffffffffff8082168552700100000000000000000000000000000000909104169483019490945260019093015462ffffff81169382019390935263010000008304820b606082015266010000000000008304820b60808201526901000000000000000000830490910b60a082015269ffffffffffffffffffff6c010000000000000000000000008304811660c083015276010000000000000000000000000000000000000000000090920490911660e0820152905092915050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915250600083815260046020908152604080832060ff808716855260039091018352818420600286810b86529084529382902082516101008101845281546bffffffffffffffffffffffff80821683526c010000000000000000000000008204169582019590955278010000000000000000000000000000000000000000000000008504860b938101939093527b01000000000000000000000000000000000000000000000000000000840490940b60608301527e0100000000000000000000000000000000000000000000000000000000000083048116151560808301527f0100000000000000000000000000000000000000000000000000000000000000909204909116151560a082015260019091015469ffffffffffffffffffff80821660c08401526a01000000000000000000009091041660e08201525b9392505050565b600080808073ffffffffffffffffffffffffffffffffffffffff808916908a16108187138114816116665761166160048b8d611a8b565b611672565b61167260048c8c611a8b565b9096509450600061168687838b8d8a611e89565b9050876020015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16877f6d264ed3495dadcbb82f8e1bf8c4a8498812b3f0543f86b6513630e455dcfe178b600001518c6040015186600001518760200151886040015189606001518a608001516040516117129796959493929190615c00565b60405180910390a460a08101511561178b5760a081015173ffffffffffffffffffffffffffffffffffffffff8d16600090815260066020526040902080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482169094011690920260ff9092169190911790555b8261179f57602081015181516000036117aa565b805160208201516000035b979d969c509a50959850939650505050505050565b8773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415611803576117fd8787612494565b90975095505b83611818576118138886886119ee565b61186c565b73ffffffffffffffffffffffffffffffffffffffff881660009081526005602052604081208791611849888861199b565b815260200190815260200160002060008282546118669190615b54565b90915550505b82156118f257600061187e338561199b565b73ffffffffffffffffffffffffffffffffffffffff8b1660009081526005602090815260408083208484529091529020549091506118bc9089612494565b73ffffffffffffffffffffffffffffffffffffffff8c166000908152600560209081526040808320958352949052929092205596505b86156119905760006119038a611c2d565b6040517ff1371dd5000000000000000000000000000000000000000000000000000000008152909150339063f1371dd59061194c908d908d908d908d908a908a90600401615c72565b600060405180830381600087803b15801561196657600080fd5b505af115801561197a573d6000803e3d6000fd5b5050505061198e8a8983610d359190615b54565b505b505050505050505050565b6000816119a757600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff8516602082015290810183905260600160405160208183030381529060405280519060200120905092915050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af1915050611a4f816124b0565b611a85576040517fbfa871c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6000808383604051602001611ac392919073ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120600081815297909252909520959350505050565b84546000908190610100900460ff1615611b2657600080fd5b6fffffffffffffffffffffffffffffffff85166201000311801590611b6d57506ffffdd8371ce3ef742f98c78a4732240d6fffffffffffffffffffffffffffffffff861611155b611b7657600080fd5b60008460ff1611611b8657600080fd5b865460ff84811662010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff91871661010002919091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff90921691909117178755611bf48787876124f5565b60069890980180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559795505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8216738dd5fbce2f6a956c3022ba3663759011dd51e73e1415611c6657600080fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600660205260409020805460ff1660011415611c9d57600080fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178155611623836128ed565b80611cd9836128ed565b1015611d11576040517fde2cd50d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5073ffffffffffffffffffffffffffffffffffffffff16600090815260066020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002179055565b6000806000611d6f85612a26565b50600184015460ff8116611d8257600080fd5b611dd9858587600101600081548110611d9d57611d9d615a9c565b600091825260209091206002909102015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff166124f5565b9096909550909350915050565b80516000906116141080611dfc57506014825111155b80610c9d5750505160167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec90910106151590565b6000808060168502611e458782016014015190565b878201602a810151601690910151919550935061ffff16915084611e67579192915b5093509350939050565b6014820151825183015182611e8257905b9250929050565b611ec26040518060c001604052806000815260200160008152602001600081526020016000815260200160608152602001600081525090565b611ecb86612a26565b6060611ed5615489565b60018881015490811b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0195861695871580611f3057507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff88145b15611f67576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86611f9e576040517fd2a0a82900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808714156120c55789600101805480602002602001604051908101604052809291908181526020016000905b828210156120ba5760008481526020908190206040805161010081018252600280870290930180546fffffffffffffffffffffffffffffffff8082168452700100000000000000000000000000000000909104168286015260019081015462ffffff81169383019390935263010000008304840b606083015266010000000000008304840b60808301526901000000000000000000830490930b60a082015269ffffffffffffffffffff6c010000000000000000000000008304811660c083015276010000000000000000000000000000000000000000000090920490911660e082015283529092019101611fca565b5050505093506122a3565b8167ffffffffffffffff8111156120de576120de615bd1565b60405190808252806020026020018201604052801561216b57816020015b604080516101008101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816120fc5790505b50935060005b84518110156122a1576001811b881615612299578a600101818154811061219a5761219a615a9c565b60009182526020918290206040805161010081018252600293840290920180546fffffffffffffffffffffffffffffffff8082168552700100000000000000000000000000000000909104169483019490945260019093015462ffffff81169382019390935263010000008304820b606082015266010000000000008304820b60808201526901000000000000000000830490910b60a082015269ffffffffffffffffffff6c010000000000000000000000008304811660c083015276010000000000000000000000000000000000000000000090920490911660e0820152855186908390811061228d5761228d615a9c565b60200260200101819052505b600101612171565b505b5050604080516101008101825260008089138a1515811483526020808401919091528b5462010000900460ff168385015260608301829052608083018990528351808501909452627fffff8452830181905260a082019290925260c08101612309612a5c565b81526020018690529050866000805b836020015161233657612331868c8c8760800151612a67565b612346565b612346868c8c8760800151612d17565b60c085015260005b86518110156123be576000806123978f8f898b876006811061237257612372615a9c565b60200201518d888151811061238957612389615a9c565b602002602001015188613080565b90925090506123a68286615cf3565b94506123b28185615cf3565b9350505060010161234e565b506123c98284615d67565b995083602001516123fd577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c8a1215612403565b60648a13155b8061241057506080840151155b1561241a5761241f565b612318565b83606001518760a001818152505061245f8c868887602001516124425784612444565b855b88602001516124565786600003613602565b85600003613602565b60808a0152606089015260408801528a61247a57808261247d565b81815b6020890152875250949a9950505050505050505050565b6000808284106124a8578284039150611e82565b509291900390565b6000816124c1573d6000803e3d6000fd5b3d602081146124d95780156124ea57600091506124ef565b3d6000803e600051151591506124ef565b600191505b50919050565b600183015460009081906006811061250c57600080fd5b620186a062ffffff8616111561252157600080fd5b604080516101008101825261640081526fffffffffffffffffffffffffffffffff8616602082015262ffffff87169181019190915260009060608101612566876139c3565b60020b81527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff427556020820152620bd8ab604082015260006060820181905260809091015290506fffffffffffffffffffffffffffffffff85166ffffdd8371ce3ef742f98c78a4732240d14156125ec57606081018051906125e582615ddb565b60020b9052505b600180880180548083018255600091825260208083208551828701516fffffffffffffffffffffffffffffffff9081167001000000000000000000000000000000000291161760029384029091019081556040808701519190950180546060880151608089015160a08a015160c08b015160e08c015169ffffffffffffffffffff9081167601000000000000000000000000000000000000000000000275ffffffffffffffffffffffffffffffffffffffffffff919092166c0100000000000000000000000002166bffffffffffffffffffffffff62ffffff9384166901000000000000000000027fffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff958516660100000000000002959095167fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff9685166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000909816949099169390931795909517939093169590951717939093161791909117905585835260038b0181528383207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755808552818352858520620bd8ab865291835285852082547d0bd8abf427550000000000000000000000000000000000000000000000647fffff000000000000ffffffffffffffffffffffff00000000000000000000000090911617835580547d0bd8abf427550000000000000000000000640000000000000000000000007fffff000000000000000000000000000000000000ffffffffffffffffffffffff909116178155888652938d0190925293909220909161285e9190613f46565b600084815260028a016020526040902061287b90620bd8ab613f46565b6128a86a64000000000000000000006fffffffffffffffffffffffffffffffff8916808204910615150190565b95506128df6128ca6fffffffffffffffffffffffffffffffff89166064615e39565b68010000000000000000808204910615150190565b945050505050935093915050565b604080513060248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905290516000918291829173ffffffffffffffffffffffffffffffffffffffff86169161297f9190615e76565b600060405180830381855afa9150503d80600081146129ba576040519150601f19603f3d011682016040523d82523d6000602084013e6129bf565b606091505b50915091508115806129d357508051602014155b15612a0a576040517f77b1b94e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80806020019051810190612a1e9190615eb1565b949350505050565b805460ff16612a3457600080fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b612a646154ed565b90565b612a6f6154ed565b60008312612a7f57612a7f615eca565b612a876154ed565b612a8f6154ed565b60008060005b8951811015612bd4576001811b8716612ae5577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612adb57612adb615a9c565b6020020152612bcc565b60008a8281518110612af957612af9615a9c565b60200260200101519050600081600001516fffffffffffffffffffffffffffffffff169050816040015162ffffff1681620186a00281612b3b57612b3b615cc4565b04878460068110612b4e57612b4e615a9c565b6020020181815250850194508a612b805760208201516fffffffffffffffffffffffffffffffff16810260481c612bab565b81602001516fffffffffffffffffffffffffffffffff16604882901b81612ba957612ba9615cc4565b045b868460068110612bbd57612bbd615a9c565b60200201818152508401935050505b600101612a95565b50612bdf8782615cf3565b905060005b8951811015612d0a577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612c2057612c20615a9c565b602002015114612d02576000612c6d858360068110612c4157612c41615a9c565b6020020151612c6785898660068110612c5c57612c5c615a9c565b602002015188613fa2565b90613fda565b878360068110612c7f57612c7f615a9c565b602002018190521315612d02577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612cbf57612cbf615a9c565b6020020152848160068110612cd657612cd6615a9c565b602002015183039250838160068110612cf157612cf1615a9c565b602002015190910390506000612be4565b600101612be4565b5050505050949350505050565b612d1f6154ed565b60008313612d2f57612d2f615eca565b612d376154ed565b612d3f6154ed565b60008060005b8951811015612ebc576001811b8716612d95577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff868260068110612d8b57612d8b615a9c565b6020020152612eb4565b60008a8281518110612da957612da9615a9c565b6020908102919091010151805160408201519192506fffffffffffffffffffffffffffffffff1690612deb620186a0830262ffffff8316808204910615150190565b888560068110612dfd57612dfd615a9c565b6020020181815250860195508b612e56576020830151612e51906fffffffffffffffffffffffffffffffff1683026402540be40062ffffff8416800269010000000000000000000204808204910615150190565b612e92565b60208301516fffffffffffffffffffffffffffffffff1662ffffff8216908102026d02540be40000000000000000000083028181049190061515015b878560068110612ea457612ea4615a9c565b6020020181815250850194505050505b600101612d45565b50612ec78782615b54565b9050818102600083838381612ede57612ede615cc4565b04148015612f0c57507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8211155b905060005b8b51811015613071577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff888260068110612f4d57612f4d615a9c565b602002015114613069576000612fd4878360068110612f6e57612f6e615a9c565b602002015184612fa657612fa1612f9c888c8760068110612f9157612f91615a9c565b60200201518b613fe6565b6140b3565b612c67565b878a8560068110612fb957612fb9615a9c565b6020020151880281612fcd57612fcd615cc4565b0490613fda565b898360068110612fe657612fe6615a9c565b602002018190521215613069577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff88826006811061302657613026615a9c565b602002015286816006811061303d5761303d615a9c565b60200201518503945085816006811061305857613058615a9c565b602002015190930392506000612f11565b600101612f11565b50505050505050949350505050565b6000807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8660c0015184600681106130ba576130ba615a9c565b602002015114156130d0575060009050806135f7565b84516fffffffffffffffffffffffffffffffff166131225761310d8660a001518760000151613103578560a001516140e9565b85608001516140e9565b6fffffffffffffffffffffffffffffffff1685525b60006131618888602001518960c00151876006811061314357613143615a9c565b602002015188602001518a600001518a600001518b60400151614137565b6fffffffffffffffffffffffffffffffff9091166020890152919450925090507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8314156131b65760008092509250506135f7565b8660200151156131dd576020860180518401905260408601805160008490030190526131f6565b6020860180518301905260408601805160008590030190525b60408781015160608901805160ff92831685029290920491820190528651928190039290916000916fffffffffffffffffffffffffffffffff169084901b8161324157613241615cc4565b0490508860000151156132695760c087018051820169ffffffffffffffffffff169052613280565b60e087018051820169ffffffffffffffffffff1690525b50505084600001516fffffffffffffffffffffffffffffffff1684602001516fffffffffffffffffffffffffffffffff1614156135f75785516000906132ca578460a001516132d0565b84608001515b9050600281900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755148061330a5750600281900b620bd8ab145b1561332357506080860180516001851b191690526135f7565b60008087526001606088015284815260038a0160209081526040808320600285900b8452909152902060c086015160e08701516133e1918391600190920180546a010000000000000000000069ffffffffffffffffffff80831690940384167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000008316811782900485169095039093169092027fffffffffffffffffffffffff0000000000000000000000000000000000000000909216909217179055565b805488516bffffffffffffffffffffffff808316926c01000000000000000000000000900416901561347d57875170ffffffffffffffffffffffffffffffff00600884811b82169084901b90911691909101036fffffffffffffffffffffffffffffffff168852825478010000000000000000000000000000000000000000000000009004600290810b60808a015284900b60a08901526134eb565b875170ffffffffffffffffffffffffffffffff00600883811b82169085901b90911691909101036fffffffffffffffffffffffffffffffff168852600284810b60808a015283547b010000000000000000000000000000000000000000000000000000009004900b60a08901525b505087516135205780547f0100000000000000000000000000000000000000000000000000000000000000900460ff16613548565b80547e01000000000000000000000000000000000000000000000000000000000000900460ff165b156135f457600085815260048b016020908152604080832060038e01835281842060028f019093529083208b518493613587939290918c90899061436f565b60e08c01519193509150156135f15760e08a015160408051600285810b82526bffffffffffffffffffffffff8516602083015287900b9260ff8b169290917f2a7e6f8c2d4129d4221502dbf7923a55b65530b0630c7b4a2303e5a4f8f46557910160405180910390a45b50505b50505b965096945050505050565b6000806060855167ffffffffffffffff81111561362157613621615bd1565b60405190808252806020026020018201604052801561364a578160200160208202803683370190505b5090507a80000000000000000000000000000000000000000000000000000080861090851060005b88518110156139b55760008a826006811061368f5761368f615a9c565b602002015190506000816020015111806136aa575080606001515b156139ac5760008a83815181106136c3576136c3615a9c565b602002602001015190506136da81602001516139c3565b600290810b6060830181905260a083015190910b1415613722576060810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0160020b90525b808d600101848154811061373857613738615a9c565b60009182526020918290208351848401516fffffffffffffffffffffffffffffffff90811670010000000000000000000000000000000002918116919091176002909302909101918255604084015160019092018054606086015160808088015160a089015160c08a015160e0909a015169ffffffffffffffffffff9081167601000000000000000000000000000000000000000000000275ffffffffffffffffffffffffffffffffffffffffffff91909b166c0100000000000000000000000002166bffffffffffffffffffffffff62ffffff9283166901000000000000000000027fffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff948416660100000000000002949094167fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff9684166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000090981693909a1692909217959095179390931696909617959095171617949094179093558351918401518951911b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000169190921617908790859081106138ff576138ff615a9c565b6020908102919091010152891561395a57602a83028561393a57602083015160298c901c600101908161393457613934615cc4565b04613953565b60208301518b9060291b8161395157613951615cc4565b045b901b881797505b88156139aa57602a83028461398a57604083015160298b901c600101908161398457613984615cc4565b046139a3565b60408301518a9060291b816139a1576139a1615cc4565b045b901b871796505b505b50600101613672565b505050955095509592505050565b60006fffffffffffffffffffffffffffffffff82166201000311801590613a0c57506ffffdd8371ce3ef742f98c78a4732240d6fffffffffffffffffffffffffffffffff831611155b613a1557600080fd5b6fffffffffffffffffffffffffffffffff8216806000680100000000000000008210613a4357604091821c91015b6401000000008210613a5757602091821c91015b620100008210613a6957601091821c91015b6101008210613a7a57600891821c91015b60108210613a8a57600491821c91015b60048210613a9a57600291821c91015b60028210613aaa57600191821c91015b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8810160401b607f82810385901b8002901c7001000000000000000000000000000000008110613b0657678000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613b3557674000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613b6457672000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613b9357671000000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613bc257670800000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613bf157670400000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613c2057670200000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613c4f57670100000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613c7d576680000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613cab576640000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613cd9576620000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d07576610000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d35576608000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d63576604000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613d91576602000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613dbf576601000000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613dec5765800000000000919091179060011c5b8002607f1c7001000000000000000000000000000000008110613e195765400000000000919091179060011c5b693627a301d71055774c8591909102906f0d89e7aecf44b7384157000000000001820160801d60007ffffffffffffffffffffffffffff5adf5000000000000000000000000000000008412613ec4577ffffffffffffffffffffffffffff8bb35000000000000000000000000000000008412613e9957608084901d613eeb565b7fffffffffffffffffffffffffffffffffff9a58f534e18c8f54aaffffffffffff840160801d613eeb565b7fffffffffffffffffffffffffffffffffb5d6bb93c93a19febd193fffffffffff840160801d5b90508060020b8260020b1480613f2d5750613f0582614ae2565b6fffffffffffffffffffffffffffffffff16896fffffffffffffffffffffffffffffffff1610155b613f375780613f39565b815b9998505050505050505050565b6000806000613f5484614e3a565b600082815260028901602090815260408083208054600160ff96871681901b909117909155868452808c0190925290912080549290931681901b9091179091558654911b1790945550505050565b6000808412613fbe57613fb9612f9c858585614e67565b612a1e565b613fcf612f9c856000038585613fe6565b600003949350505050565b60006116238284615d67565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff858709858702925082811083820303915050806000141561403e576000841161403357600080fd5b508290049050611623565b80841161404a57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8211156140e5576140e5615eca565b5090565b6000826000015160020b8260020b141561410857506020820151610c9d565b61411182614ae2565b6fffffffffffffffffffffffffffffffff8116602085015260029290920b909252919050565b846000808080808c6141535761414e8a8a8a614ea2565b61415e565b61415e8a8a8a614f5d565b905062ffffff871680028c15614264577b6df37f675ef6eadf5ab9a2072d44268d97df837e6748956e5c6c211787126141a057806402540be4008804026141ab565b6402540be400878202045b9250818312156141dc578d6141ca576141c58b8a8561505e565b6141d5565b6141d58b8a856151cf565b9450614238565b8994508192506142357b6df37f675ef6eadf5ab9a2072d44268d97df837e6748956e5c6c2117841261421e578184061515828504015b6402540be400026140b3565b612f9c846402540be4000283808204910615150190565b96505b8d61424d576142488b868b614f5d565b614258565b6142588b868b614ea2565b955082870393506142f8565b81871315614293578d6142815761427c8b8a8961505e565b61428c565b61428c8b8a896151cf565b945061429a565b8994508196505b8d6142af576142aa8b868b614f5d565b6142ba565b6142ba8b868b614ea2565b92506142f07b6df37f675ef6eadf5ab9a2072d44268d97df837e6748956e5c6c2117841261421e57818406151582850401614212565b955082860393505b8215801561432a5750896fffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff1614155b1561435e577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9650600095508a9450600093505b505050975097509750979350505050565b600282900b6000908152602086905260408120819081908190851561446557600287900b600090815260208c90526040812090600290810291909101805461ffff6c010000000000000000000000008083049190911660010b8b019384900b600090815260208f90526040902080546bffffffffffffffffffffffff938416838204851681900385169093027fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff90911617815585547fff00ffffffffffffffffffffffffffffffffffff000000000000000000000000811690841683900390931692909217855592975091955093509150614556565b600287900b600090815260208c905260409020600160029081029190910180546c0100000000000000000000000080820461ffff1660010b8b039384900b600090815260208f90526040902080547fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081166bffffffffffffffffffffffff948516918516829003851617825586547effffffffffffff000000000000000000000000ffffffffffffffffffffffff81169084900485168290039094169092027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16929092178555929750919550935091505b8660020b8560020b141561456c5761456c615eca565b6040805180820182526001808501548482015469ffffffffffffffffffff80831682821603811685526a01000000000000000000009283900481169183900481169190910381166020808601918252895463ffffffff6e0100000000000000000000000000009182900481166000908152968c019092529690942094518554915183169093027fffffffffffffffffffffffff00000000000000000000000000000000000000009091169290911691909117179091558454919091041683600e61463583615ef9565b825463ffffffff9182166101009390930a92830291909202199091161790555082547fffffffffffffffffffffffffffffffffffff000000000000000000000000000016835581546bffffffffffffffffffffffff161580156146b5575081546c0100000000000000000000000090046bffffffffffffffffffffffff16155b156148a057600285900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff42755148015906146f55750600285900b620bd8ab14155b61470157614701615eca565b60008260000160189054906101000a900460020b9050600083600001601b9054906101000a900460020b9050808c60008460020b60020b8152602001908152602001600020600001601b6101000a81548162ffffff021916908360020b62ffffff160217905550818c60008360020b60020b815260200190815260200160002060000160186101000a81548162ffffff021916908360020b62ffffff1602179055508b60008860020b60020b8152602001908152602001600020600080820160006101000a8154906bffffffffffffffffffffffff021916905560008201600c6101000a8154906bffffffffffffffffffffffff02191690556000820160186101000a81549062ffffff021916905560008201601b6101000a81549062ffffff021916905560008201601e6101000a81549060ff021916905560008201601f6101000a81549060ff02191690556001820160006101000a81549069ffffffffffffffffffff021916905560018201600a6101000a81549069ffffffffffffffffffff0219169055505061489d878c6153dc90919063ffffffff16565b50505b80546bffffffffffffffffffffffff161580156148da575080546c0100000000000000000000000090046bffffffffffffffffffffffff16155b15614ad457600287900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff427551480159061491a5750600287900b620bd8ab14155b61492657614926615eca565b60008160000160189054906101000a900460020b9050600082600001601b9054906101000a900460020b9050808c60008460020b60020b8152602001908152602001600020600001601b6101000a81548162ffffff021916908360020b62ffffff160217905550818c60008360020b60020b815260200190815260200160002060000160186101000a81548162ffffff021916908360020b62ffffff1602179055508b60008a60020b60020b8152602001908152602001600020600080820160006101000a8154906bffffffffffffffffffffffff021916905560008201600c6101000a8154906bffffffffffffffffffffffff02191690556000820160186101000a81549062ffffff021916905560008201601b6101000a81549062ffffff021916905560008201601e6101000a81549060ff021916905560008201601f6101000a81549060ff02191690556001820160006101000a81549069ffffffffffffffffffff021916905560018201600a6101000a81549069ffffffffffffffffffff02191690555050614ac2898c6153dc90919063ffffffff16565b600291820b60808b0152900b60a08901525b505050965096945050505050565b6000600282900b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4275513801590614b1f5750620bd8ab600283900b13155b614b2857600080fd5b6000808360020b12614b3a5782614b3f565b826000035b62ffffff811691507001000000000000000000000000000000009060011615614b78576ffffcb933bd6fad37aa2d162d1a5940010260801c5b6002821615614b97576ffff97272373d413259a46990580e213a0260801c5b6004821615614bb6576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615614bd5576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615614bf4576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615614c13576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615614c32576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615614c51576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615614c71576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615614c91576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615614cb1576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615614cd1576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615614cf1576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615614d11576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615614d31576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615614d51576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615614d72576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615614d92576e5d6af8dedb81196699c329225ee6040260801c5b62040000821615614db1576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614dce576b048a170391f7dc42444e8fa20260801c5b60008460020b12614e0c57807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81614e0857614e08615cc4565b0490505b6000670100000000000000820611614e25576000614e28565b60015b60ff16603882901c0192505050919050565b620bd8ab810160020b601081901c90600881901c906101008310614e6057614e60615eca565b9193909250565b6000614e74848484613fe6565b905060008280614e8657614e86615cc4565b84860911156116235780614e9981615f1d565b95945050505050565b60006fffffffffffffffffffffffffffffffff808516908416108015614ec6579293925b60008585036fffffffffffffffffffffffffffffffff16846fffffffffffffffffffffffffffffffff16029050614f2682614f1c5768ffffffffffffffffff8216151569010000000000000000008304016140b3565b604882901c6140b3565b92508115614f54577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830292505b50509392505050565b60006fffffffffffffffffffffffffffffffff808516908416118015614f81579293925b6fffffffffffffffffffffffffffffffff8381168587038216029080871690861602615026770100000000000000000000000000000000000000000000008310614ff45783614fdf57612f9c83690100000000000000000084614e67565b612f9c83690100000000000000000084613fe6565b8361500d57612f9c604884901b83808204910615150190565b81604884901b8161502057615020615cc4565b046140b3565b93508215615054577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff840293505b5050509392505050565b600080821215615132576fffffffffffffffffffffffffffffffff831661508457600080fd5b6000828103907701000000000000000000000000000000000000000000000082106150d5576150d0826901000000000000000000876fffffffffffffffffffffffffffffffff16614e67565b615103565b61510369010000000000000000008302866fffffffffffffffffffffffffffffffff16808204910615150190565b90506151296151246fffffffffffffffffffffffffffffffff88168361544f565b61545b565b92505050611623565b600077010000000000000000000000000000000000000000000000831061517f5761517a836901000000000000000000866fffffffffffffffffffffffffffffffff16613fe6565b6151ae565b836fffffffffffffffffffffffffffffffff1669010000000000000000008402816151ac576151ac615cc4565b045b9050614e996151246fffffffffffffffffffffffffffffffff87168361547d565b6000816151dd575082611623565b60008083126151ec57826151f1565b826000035b90506fffffffffffffffffffffffffffffffff8516810278ffffffffffffffffffffffffffffffff000000000000000000604886901b1660008086131561531a57876fffffffffffffffffffffffffffffffff1684848161525457615254615cc4565b04148015615266575050818101818110155b156152d8576fffffffffffffffffffffffffffffffff878116908916027701000000000000000000000000000000000000000000000081106152bc576152b781690100000000000000000084614e67565b6152d0565b6152d0604882901b83808204910615150190565b9550506153d1565b61531382615308868b6fffffffffffffffffffffffffffffffff16868161530157615301615cc4565b049061547d565b808204910615150190565b94506153d1565b876fffffffffffffffffffffffffffffffff1684848161533c5761533c615cc4565b041461534757600080fd5b508181038181111561535857600080fd5b8061536257600080fd5b6fffffffffffffffffffffffffffffffff878116908916027701000000000000000000000000000000000000000000000081106153b6576153b161512482690100000000000000000085614e67565b6153cd565b6153cd615124604883901b84808204910615150190565b9550505b505050509392505050565b60008060006153ea84614e3a565b600082815260028901602052604090208054600160ff84161b191690819055929550909350915061544857600083815260018681016020526040909120805460ff85169290921b19909116908190556154485784546001841b191685555b5050505050565b60006116238284615a85565b60006fffffffffffffffffffffffffffffffff8211156140e5576140e5615eca565b60006116238284615b54565b6040518060c001604052806006905b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816154985790505090565b6040518060c001604052806006906020820280368337509192915050565b60006020828403121561551d57600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461554857600080fd5b919050565b60008083601f84011261555f57600080fd5b50813567ffffffffffffffff81111561557757600080fd5b602083019150836020828501011115611e8257600080fd5b60008060008060008060008060006101008a8c0312156155ae57600080fd5b6155b78a615524565b98506155c560208b01615524565b975060408a0135965060608a013595506155e160808b01615524565b945060a08a0135935060c08a0135925060e08a013567ffffffffffffffff81111561560b57600080fd5b6156178c828d0161554d565b915080935050809150509295985092959850929598565b6000806000806080858703121561564457600080fd5b61564d85615524565b93506020850135925061566260408601615524565b9396929550929360600135925050565b6000806040838503121561568557600080fd5b61568e83615524565b946020939093013593505050565b803562ffffff8116811461554857600080fd5b600080600080600060a086880312156156c757600080fd5b6156d086615524565b94506156de60208701615524565b93506156ec6040870161569c565b925060608601356fffffffffffffffffffffffffffffffff8116811461571157600080fd5b949793965091946080013592915050565b6000806040838503121561573557600080fd5b823591506157456020840161569c565b90509250929050565b60008060008060008060a0878903121561576757600080fd5b61577087615524565b95506020870135945061578560408801615524565b935060608701359250608087013567ffffffffffffffff8111156157a857600080fd5b6157b489828a0161554d565b979a9699509497509295939492505050565b600080600080608085870312156157dc57600080fd5b6157e585615524565b93506157f360208601615524565b92506156626040860161569c565b803560ff8116811461554857600080fd5b8035600281900b811461554857600080fd5b60008060008060008060c0878903121561583d57600080fd5b8635955061584d60208801615524565b94506040870135935061586260608801615801565b925061587060808801615812565b915061587e60a08801615812565b90509295509295509295565b60006020828403121561589c57600080fd5b813567ffffffffffffffff8111156158b357600080fd5b820160c0818503121561162357600080fd5b600080604083850312156158d857600080fd5b8235915061574560208401615801565b6000610100820190506fffffffffffffffffffffffffffffffff8084511683528060208501511660208401525062ffffff6040840151166040830152606083015160020b60608301526080830151615945608084018260020b9052565b5060a083015161595a60a084018260020b9052565b5060c083015161597860c084018269ffffffffffffffffffff169052565b5060e083015161599660e084018269ffffffffffffffffffff169052565b5092915050565b6000806000606084860312156159b257600080fd5b833592506159c260208501615801565b91506159d060408501615812565b90509250925092565b6000610100820190506bffffffffffffffffffffffff80845116835280602085015116602084015250604083015160020b6040830152606083015160020b606083015260808301511515608083015260a083015161595a60a084018215159052565b600060208284031215615a4d57600080fd5b61162382615524565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015615a9757615a97615a56565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000615b4a606083018486615acb565b9695505050505050565b60008219821115615b6757615b67615a56565b500190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615ba157600080fd5b83018035915067ffffffffffffffff821115615bbc57600080fd5b602001915036819003821315611e8257600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060e08201898352602089818501528860408501528760608501528660808501528560a085015260e060c085015281855180845261010086019150828701935060005b81811015615c6057845183529383019391830191600101615c44565b50909c9b505050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a06080830152615cb860a083018486615acb565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615615d2d57615d2d615a56565b827f8000000000000000000000000000000000000000000000000000000000000000038412811615615d6157615d61615a56565b50500190565b6000808312837f800000000000000000000000000000000000000000000000000000000000000001831281151615615da157615da1615a56565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018313811615615dd557615dd5615a56565b50500390565b60008160020b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000811415615e1157615e11615a56565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615e7157615e71615a56565b500290565b6000825160005b81811015615e975760208186018101518583015201615e7d565b81811115615ea6576000828501525b509190910192915050565b600060208284031215615ec357600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600063ffffffff80831681811415615f1357615f13615a56565b6001019392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615f4f57615f4f615a56565b506001019056fea164736f6c634300080a000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000005dd2444a17edc079210077924906d5bdf432a858
-----Decoded View---------------
Arg [0] : _positionController (address): 0x5dD2444a17EDc079210077924906D5BDF432A858
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000005dd2444a17edc079210077924906d5bdf432a858Loading...LoadingLoading...LoadingABI for the implementation contract at 0x5dd2444a17edc079210077924906d5bdf432a858, likely using a custom proxy implementation.Learn more about proxy contracts in our Knowledge Base
Loading...LoadingABI for the implementation contract at 0x5dd2444a17edc079210077924906d5bdf432a858, likely using a custom proxy implementation.Learn more about proxy contracts in our Knowledge Base
Loading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingMultichain Portfolio | 30 Chains
Loading...Loading[ Download: CSV Export ]A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.
Address QR Code
My Address - Private Name Tag or Note
My Name Tag:
Private Name Tags (up to 35 characters) can be used for easy identification of addressesPrivate Note:
A private note (up to 500 characters) can be attached to this address.
Please DO NOT store any passwords or private keys here.Compiler specific version warnings:
The compiled contract might be susceptible to VerbatimInvalidDeduplication (low-severity), FullInlinerNonExpressionSplitArgumentEvaluationOrder (low-severity), MissingSideEffectsOnSelectorAccess (low-severity), AbiReencodingHeadOverflowWithStaticArrayCleanup (medium-severity), DirtyBytesArrayToStorage (low-severity), DataLocationChangeInInternalOverride (very low-severity), NestedCalldataArrayAbiReencodingSizeValidation (very low-severity) Solidity Compiler Bugs.
Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.SignIn
Address Cards
To use this feature, please login to your Etherscan account and return to this page.Before You Copy
Transaction Private Note
This website uses cookies to improve your experience. By continuing to use this website, you agree to its Terms and Privacy Policy.