HYPE Price: $32.58 (+5.55%)
 

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Deploy Token27988092025-04-21 20:38:54281 days ago1745267934IN
0x00000000...728357827
0.0001 HYPE0.000205080.11
Deploy Token27455052025-04-20 15:59:24282 days ago1745164764IN
0x00000000...728357827
0.0001 HYPE0.000205080.11
Deploy Token27454982025-04-20 15:59:10282 days ago1745164750IN
0x00000000...728357827
0.0001 HYPE0.000205080.11
Deploy Token14484112025-03-22 14:27:00311 days ago1742653620IN
0x00000000...728357827
0.0001 HYPE0.000233050.125
Deploy Token13807572025-03-21 2:03:00313 days ago1742522580IN
0x00000000...728357827
0.00001 HYPE0.000914810.1
Deploy Token13792382025-03-21 1:14:00313 days ago1742519640IN
0x00000000...728357827
0.0001 HYPE0.000841470.1
Deploy Token13791762025-03-21 1:12:00313 days ago1742519520IN
0x00000000...728357827
0.000001 HYPE0.000841470.1
Update Liquidity...13790362025-03-21 1:07:30313 days ago1742519250IN
0x00000000...728357827
0 HYPE0.000002930.1
Deploy Token13724182025-03-20 21:34:00313 days ago1742506440IN
0x00000000...728357827
0.0001 HYPE0.000848410.1
Update Position ...13724022025-03-20 21:33:30313 days ago1742506410IN
0x00000000...728357827
0 HYPE0.000002910.1
Update Liquidity...13723972025-03-20 21:33:20313 days ago1742506400IN
0x00000000...728357827
0 HYPE0.000002930.1
Update Position ...13723522025-03-20 21:31:54313 days ago1742506314IN
0x00000000...728357827
0 HYPE0.000002910.1
Deploy Token13719532025-03-20 21:19:00313 days ago1742505540IN
0x00000000...728357827
0.0001 HYPE0.000847740.1
Update Swap Rout...13719412025-03-20 21:18:38313 days ago1742505518IN
0x00000000...728357827
0 HYPE0.000002930.1
Update Uni V3Fac...13719322025-03-20 21:18:20313 days ago1742505500IN
0x00000000...728357827
0 HYPE0.00000290.1
Update Position ...13719212025-03-20 21:18:00313 days ago1742505480IN
0x00000000...728357827
0 HYPE0.000002910.1
Update Liquidity...13704192025-03-20 20:29:32313 days ago1742502572IN
0x00000000...728357827
0 HYPE0.000002930.1
Update Swap Rout...13704192025-03-20 20:29:32313 days ago1742502572IN
0x00000000...728357827
0 HYPE0.000002930.1
Update Uni V3Fac...13704152025-03-20 20:29:24313 days ago1742502564IN
0x00000000...728357827
0 HYPE0.00000290.1
Update Position ...13704032025-03-20 20:29:00313 days ago1742502540IN
0x00000000...728357827
0 HYPE0.000002910.1
Deploy Token5489962025-03-02 10:47:12331 days ago1740912432IN
0x00000000...728357827
0.0001 HYPE0.00023060.12368266
Deploy Token5488532025-03-02 10:42:36331 days ago1740912156IN
0x00000000...728357827
0.0001 HYPE0.000223730.12
Deploy Token5478162025-03-02 10:09:08331 days ago1740910148IN
0x00000000...728357827
0.0001 HYPE0.000223730.12
Deploy Token5478082025-03-02 10:08:54331 days ago1740910134IN
0x00000000...728357827
0.0001 HYPE0.000223730.12
Deploy Token5263422025-03-01 22:36:26332 days ago1740868586IN
0x00000000...728357827
0.1 HYPE0.00023330.125
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
27988092025-04-21 20:38:54281 days ago1745267934
0x00000000...728357827
 Contract Creation0 HYPE
27455052025-04-20 15:59:24282 days ago1745164764
0x00000000...728357827
 Contract Creation0 HYPE
27454982025-04-20 15:59:10282 days ago1745164750
0x00000000...728357827
 Contract Creation0 HYPE
14484112025-03-22 14:27:00311 days ago1742653620
0x00000000...728357827
 Contract Creation0 HYPE
13807572025-03-21 2:03:00313 days ago1742522580
0x00000000...728357827
0.00000975 HYPE
13807572025-03-21 2:03:00313 days ago1742522580
0x00000000...728357827
0.00000025 HYPE
13807572025-03-21 2:03:00313 days ago1742522580
0x00000000...728357827
 Contract Creation0 HYPE
13792382025-03-21 1:14:00313 days ago1742519640
0x00000000...728357827
0.0000975 HYPE
13792382025-03-21 1:14:00313 days ago1742519640
0x00000000...728357827
0.0000025 HYPE
13792382025-03-21 1:14:00313 days ago1742519640
0x00000000...728357827
 Contract Creation0 HYPE
13791762025-03-21 1:12:00313 days ago1742519520
0x00000000...728357827
0.00000097 HYPE
13791762025-03-21 1:12:00313 days ago1742519520
0x00000000...728357827
0.00000002 HYPE
13791762025-03-21 1:12:00313 days ago1742519520
0x00000000...728357827
 Contract Creation0 HYPE
13724182025-03-20 21:34:00313 days ago1742506440
0x00000000...728357827
0.0000975 HYPE
13724182025-03-20 21:34:00313 days ago1742506440
0x00000000...728357827
0.0000025 HYPE
13724182025-03-20 21:34:00313 days ago1742506440
0x00000000...728357827
 Contract Creation0 HYPE
13719532025-03-20 21:19:00313 days ago1742505540
0x00000000...728357827
0.0000975 HYPE
13719532025-03-20 21:19:00313 days ago1742505540
0x00000000...728357827
0.0000025 HYPE
13719532025-03-20 21:19:00313 days ago1742505540
0x00000000...728357827
 Contract Creation0 HYPE
5489962025-03-02 10:47:12331 days ago1740912432
0x00000000...728357827
 Contract Creation0 HYPE
5488532025-03-02 10:42:36331 days ago1740912156
0x00000000...728357827
 Contract Creation0 HYPE
5478162025-03-02 10:09:08331 days ago1740910148
0x00000000...728357827
 Contract Creation0 HYPE
5478082025-03-02 10:08:54331 days ago1740910134
0x00000000...728357827
 Contract Creation0 HYPE
5263422025-03-01 22:36:26332 days ago1740868586
0x00000000...728357827
 Contract Creation0 HYPE
5262162025-03-01 22:32:22332 days ago1740868342
0x00000000...728357827
 Contract Creation0 HYPE
View All Internal Transactions
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BangerV3

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {TickMath} from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol";
import {INet} from "../INet.sol";
import {IStorage} from "../IStorage.sol";
import {IInscribedDrops} from "../IInscribedDrops.sol";

import {INonfungiblePositionManager, IUniswapV3Factory, ILockerFactory, ILocker, ExactInputSingleParams, ISwapRouter} from "../interface.sol";
import {Bytes32AddressLib} from "../Bytes32AddressLib.sol";
import {LibString} from "../LibString.sol";
import {Base64} from "../Base64.sol";
import {BangerV3Token} from "./BangerV3Token.sol";

contract BangerV3 is IERC1155Receiver, Ownable {
    using TickMath for int24;
    using Bytes32AddressLib for bytes32;
    using LibString for address;
    using LibString for uint256;

    error Deprecated();

    event TokenCreated(
        address tokenAddress,
        uint256 lpNftId,
        address deployer,
        uint256 fid,
        string name,
        string symbol,
        uint256 supply,
        address lockerAddress,
        string extraStringData,
        bool isMsgSenderOwner
    );

    address public taxCollector =
        address(0x2a67566660714AfaD6eB80A8a64eb7431B121F3c);
    uint64 public defaultLockingPeriod = 33275115461;
    uint8 public taxRate = 25; // 25 / 1000 -> 2.5 %
    uint8 public lpFeesCut = 50; // 5 / 100 -> 5%
    uint8 public protocolCut = 30; // 3 / 100 -> 3%
    uint24 public fee = 10_000;
    ILockerFactory public liquidityLocker =
        ILockerFactory(address(0x00000000a4944d1E707f869356fF4B48e527370f));

    address public weth = address(0x4200000000000000000000000000000000000006);
    IUniswapV3Factory public uniswapV3Factory =
        IUniswapV3Factory(address(0x33128a8fC17869897dcE68Ed026d694621f6FDfD));
    INonfungiblePositionManager public positionManager =
        INonfungiblePositionManager(
            address(0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1)
        );
    address public swapRouter =
        address(0x2626664c2603336E57B271c5C0b26F421741e481);

    address internal constant netAddress =
        address(0x00000000B24D62781dB359b07880a105cD0b64e6);
    address internal constant netStorageAddress =
        address(0x00000000DB40fcB9f4466330982372e27Fd7Bbf5);
    address internal constant inscribedDropsAddress =
        address(0x000000001c147E48f5aaD3C69e8E4d2305523c62);

    bool public deprecated;
    bool public bundleFeeSwitch = true;

    constructor()
        Ownable(address(0xcC4193B652FcB25347Bb44a88d2b09B52585643F))
    {}

    function deployToken(
        uint256 _supply,
        int24 _initialTick,
        bytes32 _salt,
        address _deployer,
        uint256 _fid,
        uint256 mintPrice,
        uint256 mintEndTimestamp,
        uint256 maxMintSupply,
        string calldata _name,
        string calldata _symbol,
        string calldata _image,
        string calldata _animation,
        string calldata _extraStringData,
        bytes calldata _extraBytesData
    ) external payable returns (BangerV3Token token, uint256 tokenId) {
        if (deprecated) revert Deprecated();

        int24 tickSpacing = uniswapV3Factory.feeAmountTickSpacing(fee);

        require(
            tickSpacing != 0 && _initialTick % tickSpacing == 0,
            "Invalid tick"
        );

        token = new BangerV3Token{
            salt: keccak256(abi.encode(_deployer, _salt))
        }(
            _name,
            _symbol,
            _supply,
            _deployer,
            _fid,
            _image,
            _animation,
            _extraStringData,
            _extraBytesData
        );

        // Makes sure that the token address is less than the WETH address. This is so that the token
        // is first in the pool. Just makes things consistent.
        require(address(token) < weth, "Invalid salt");

        uint160 sqrtPriceX96 = _initialTick.getSqrtRatioAtTick();
        address pool = uniswapV3Factory.createPool(address(token), weth, fee);
        IUniswapV3Factory(pool).initialize(sqrtPriceX96);

        INonfungiblePositionManager.MintParams
            memory params = INonfungiblePositionManager.MintParams(
                address(token),
                weth,
                fee,
                _initialTick,
                maxUsableTick(tickSpacing),
                _supply,
                0,
                0,
                0,
                address(this),
                block.timestamp
            );

        token.approve(address(positionManager), _supply);
        (tokenId, , , ) = positionManager.mint(params);

        address lockerAddress = liquidityLocker.deploy(
            address(positionManager),
            _deployer,
            defaultLockingPeriod,
            tokenId,
            lpFeesCut
        );

        positionManager.safeTransferFrom(address(this), lockerAddress, tokenId);

        ILocker(lockerAddress).initializer(tokenId);

        if (msg.value > 0) {
            uint256 remainingFundsToBuyTokens = msg.value;
            if (bundleFeeSwitch) {
                uint256 protocolFees = (msg.value * taxRate) / 1000;
                remainingFundsToBuyTokens = msg.value - protocolFees;

                (bool success, ) = payable(taxCollector).call{
                    value: protocolFees
                }("");

                if (!success) {
                    revert("Failed to send protocol fees");
                }
            }

            ExactInputSingleParams memory swapParams = ExactInputSingleParams({
                tokenIn: weth, // The token we are exchanging from (ETH wrapped as WETH)
                tokenOut: address(token), // The token we are exchanging to
                fee: fee, // The pool fee
                recipient: _deployer, // The recipient address
                amountIn: remainingFundsToBuyTokens, // The amount of ETH (WETH) to be swapped
                amountOutMinimum: 0, // Minimum amount to receive
                sqrtPriceLimitX96: 0 // No price limit
            });

            // The call to `exactInputSingle` executes the swap.
            ISwapRouter(swapRouter).exactInputSingle{
                value: remainingFundsToBuyTokens
            }(swapParams);
        }

        // Send Net message
        uint256 msgIdx = INet(netAddress).getTotalMessagesCount();
        INet(netAddress).sendMessageViaApp(
            msg.sender,
            string.concat(
                "Deployed ",
                _name,
                " (",
                _symbol,
                ") to ",
                address(token).toHexStringChecksumed(),
                " (pool: ",
                pool.toHexStringChecksumed(),
                ")"
            ),
            address(token).toHexStringChecksumed(),
            // Use encode rather than encodePacked so that the data can be easily decoded
            abi.encode(
                msgIdx,
                pool,
                token,
                lockerAddress,
                tokenId,
                _deployer,
                _fid,
                _extraStringData,
                _extraBytesData
            )
        );

        // Create drop
        uint256 dropIdx = IInscribedDrops(inscribedDropsAddress).totalDrops();
        IInscribedDrops(inscribedDropsAddress).inscribe(
            mintPrice,
            maxMintSupply,
            mintEndTimestamp,
            // No mint limit per wallet because this contract is the minter
            // of all tokens and so mint limit would not work with that.
            0,
            getUri(
                _name,
                _symbol,
                _image,
                _animation,
                _extraStringData,
                address(token)
            )
        );

        // Transfer the first minted token to _deployer, which supports if a bot deploys tokens on
        // behalf of a user
        IInscribedDrops(inscribedDropsAddress).safeTransferFrom(
            address(this),
            _deployer,
            dropIdx,
            1,
            ""
        );

        // Map the token address to relevant data
        IStorage(netStorageAddress).put(
            bytes32(uint256(uint160(address(token)))),
            "",
            // Use encode rather than encodePacked so that the data can be easily decoded
            abi.encode(
                msgIdx,
                dropIdx,
                inscribedDropsAddress,
                pool,
                lockerAddress,
                tokenId
            )
        );

        // Map the drop idx to the message idx and token address
        IStorage(netStorageAddress).put(
            bytes32(dropIdx),
            "",
            // Use encode rather than encodePacked so that the data can be easily decoded
            abi.encode(dropIdx, token, pool)
        );

        emit TokenCreated(
            address(token),
            tokenId,
            _deployer,
            _fid,
            _name,
            _symbol,
            _supply,
            lockerAddress,
            _extraStringData,
            msg.sender == owner()
        );
    }

    function getUri(
        string calldata name,
        string calldata symbol,
        string calldata image,
        string calldata animation,
        string calldata extraStringData,
        address token
    ) internal pure returns (string memory) {
        return
            string.concat(
                '{"name":"',
                name,
                " (",
                symbol,
                ")",
                '","description":"Memecoin-NFT created via Net. Memecoin is located at ',
                token.toHexStringChecksumed(),
                "\\n\\n",
                extraStringData,
                '","image":"',
                bytes(image).length > 0
                    ? image
                    : string.concat(
                        "data:image/svg+xml;base64,",
                        Base64.encode(
                            bytes(
                                string.concat(
                                    '<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" style="background-color:#000"><text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" style="font-family:monospace;font-size:24px;fill:#0f7">',
                                    name,
                                    "</text></svg>"
                                )
                            )
                        )
                    ),
                '","animation_url":"',
                animation,
                '"}'
            );
    }

    function mint(address token, uint256 quantity) external payable {
        // Get the message index and drop index
        (, bytes memory tokenData) = IStorage(netStorageAddress).get(
            bytes32(uint256(uint160(token))),
            address(this)
        );
        // We know tokenData consists of uint256, uint256, address, address, address, uint256 so we can unpack them
        (, uint256 dropIdx, address readDropsAddress, address pool, , ) = abi
            .decode(
                tokenData,
                (uint256, uint256, address, address, address, uint256)
            );

        // Mint NFTs
        // NOTE: we use readDropsAddress to future-proof this contract in case the inscribedDropsAddress changes
        IInscribedDrops(readDropsAddress).mint{value: msg.value}(
            dropIdx,
            quantity
        );

        // Transfer NFTs to msg.sender
        IInscribedDrops(readDropsAddress).safeTransferFrom(
            address(this),
            msg.sender,
            dropIdx,
            quantity,
            ""
        );

        // Send mint message
        INet(netAddress).sendMessageViaApp(
            msg.sender,
            "mint",
            address(token).toHexStringChecksumed(),
            // Use encode rather than encodePacked so that the data can be easily decoded
            abi.encode(quantity, dropIdx, token, pool)
        );

        // Use ETH to purchase tokens if value is greater than 0
        if (msg.value > 0) {
            uint256 amountIn = msg.value -
                (IInscribedDrops(inscribedDropsAddress).feeBps() * msg.value) /
                10_000;
            ExactInputSingleParams memory swapParams = ExactInputSingleParams({
                tokenIn: weth, // The token we are exchanging from (ETH wrapped as WETH)
                tokenOut: address(token), // The token we are exchanging to
                fee: fee, // The pool fee
                recipient: msg.sender, // The recipient address
                amountIn: amountIn, // The amount of ETH (WETH) to be swapped
                amountOutMinimum: 0, // Minimum amount of DAI to receive
                sqrtPriceLimitX96: 0 // No price limit
            });

            // The call to `exactInputSingle` executes the swap.
            ISwapRouter(swapRouter).exactInputSingle{value: amountIn}(
                swapParams
            );
        }
    }

    function initialSwapTokens(address token, uint24 _fee) public payable {
        ExactInputSingleParams memory swapParams = ExactInputSingleParams({
            tokenIn: weth, // The token we are exchanging from (ETH wrapped as WETH)
            tokenOut: address(token), // The token we are exchanging to
            fee: _fee, // The pool fee
            recipient: msg.sender, // The recipient address
            amountIn: msg.value, // The amount of ETH (WETH) to be swapped
            amountOutMinimum: 0, // Minimum amount of DAI to receive
            sqrtPriceLimitX96: 0 // No price limit
        });

        // The call to `exactInputSingle` executes the swap.
        ISwapRouter(swapRouter).exactInputSingle{value: msg.value}(swapParams);
    }

    function predictToken(
        address deployer,
        uint256 fid,
        string calldata name,
        string calldata symbol,
        string calldata image,
        string calldata animation,
        string calldata extraStringData,
        bytes calldata extraBytesData,
        uint256 supply,
        bytes32 salt
    ) public view returns (address) {
        bytes32 create2Salt = keccak256(abi.encode(deployer, salt));
        return
            keccak256(
                abi.encodePacked(
                    bytes1(0xFF),
                    address(this),
                    create2Salt,
                    keccak256(
                        abi.encodePacked(
                            type(BangerV3Token).creationCode,
                            abi.encode(
                                name,
                                symbol,
                                supply,
                                deployer,
                                fid,
                                image,
                                animation,
                                extraStringData,
                                extraBytesData
                            )
                        )
                    )
                )
            ).fromLast20Bytes();
    }

    function generateSalt(
        address deployer,
        uint256 fid,
        string calldata name,
        string calldata symbol,
        string calldata image,
        string calldata animation,
        string calldata extraStringData,
        bytes calldata extraBytesData,
        uint256 supply
    ) external view returns (bytes32 salt, address token) {
        for (uint256 i; ; i++) {
            salt = bytes32(i);
            token = predictToken(
                deployer,
                fid,
                name,
                symbol,
                image,
                animation,
                extraStringData,
                extraBytesData,
                supply,
                salt
            );
            if (token < weth && token.code.length == 0) {
                break;
            }
        }
    }

    function toggleBundleFeeSwitch(bool _enabled) external onlyOwner {
        bundleFeeSwitch = _enabled;
    }

    function setDeprecated(bool _deprecated) external onlyOwner {
        deprecated = _deprecated;
    }

    function updateTaxCollector(address newCollector) external onlyOwner {
        taxCollector = newCollector;
    }

    function updateLiquidityLocker(address newLocker) external onlyOwner {
        liquidityLocker = ILockerFactory(newLocker);
    }

    function updatePositionManager(
        address newPositionManager
    ) external onlyOwner {
        positionManager = INonfungiblePositionManager(newPositionManager);
    }

    function updateUniV3Factory(address newFactory) external onlyOwner {
        uniswapV3Factory = IUniswapV3Factory(newFactory);
    }

    function updateSwapRouter(address newRouter) external onlyOwner {
        swapRouter = newRouter;
    }

    function updateWeth(address newWeth) external onlyOwner {
        weth = newWeth;
    }

    function updateDefaultLockingPeriod(uint64 newPeriod) external onlyOwner {
        defaultLockingPeriod = newPeriod;
    }

    function updateProtocolFees(uint8 newFee) external onlyOwner {
        lpFeesCut = newFee;
    }

    function updateTaxRate(uint8 newRate) external onlyOwner {
        taxRate = newRate;
    }

    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }

    function supportsInterface(
        bytes4 interfaceId
    ) external view returns (bool) {
        return interfaceId == type(IERC1155Receiver).interfaceId;
    }

    fallback() external payable {}

    receive() external payable {}
}

/// @notice Given a tickSpacing, compute the maximum usable tick
function maxUsableTick(int24 tickSpacing) pure returns (int24) {
    unchecked {
        return (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Library for converting between addresses and bytes32 values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)
library Bytes32AddressLib {
    function fromLast20Bytes(
        bytes32 bytesValue
    ) internal pure returns (address) {
        return address(uint160(uint256(bytesValue)));
    }

    function fillLast12Bytes(
        address addressValue
    ) internal pure returns (bytes32) {
        return bytes32(bytes20(addressValue));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

/// @title InscribedDrops
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice NFT mints created by inscribing token uris and mint configurations in Net messages.
interface IInscribedDrops {
    function totalDrops() external view returns (uint256);

    function feeBps() external view returns (uint256);

    function name() external pure returns (string memory);

    function symbol() external pure returns (string memory);

    function inscribe(
        uint256 mintPrice,
        uint256 maxSupply,
        uint256 mintEndTimestamp,
        uint256 maxMintsPerWallet,
        string calldata tokenUri
    ) external;

    function setFeeBps(uint256 newFeeBps) external;

    function mint(uint256 id, uint256 quantity) external payable;

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

interface INet {
    struct Message {
        address app;
        address sender;
        uint256 timestamp;
        bytes data;
        string text;
        string topic;
    }

    function sendMessageViaApp(
        address sender,
        string calldata text,
        string calldata topic,
        bytes calldata extraData
    ) external;

    function sendMessage(
        string calldata text,
        string calldata topic,
        bytes calldata extraData
    ) external;

    // **************
    // Fetch Messages
    // **************

    // Fetch message indexes

    function getMessageIdxForApp(
        uint256 idx,
        address app
    ) external view returns (uint256);

    function getMessageIdxForAppUser(
        uint256 idx,
        address app,
        address user
    ) external view returns (uint256);

    function getMessageIdxForAppTopic(
        uint256 idx,
        address app,
        string calldata topic
    ) external view returns (uint256);

    function getMessageIdxForAppUserTopic(
        uint256 idx,
        address app,
        address user,
        string calldata topic
    ) external view returns (uint256);

    // Fetch single message

    function getMessage(uint256 idx) external view returns (Message memory);

    function getMessageForApp(
        uint256 idx,
        address app
    ) external view returns (Message memory);

    function getMessageForAppUser(
        uint256 idx,
        address app,
        address user
    ) external view returns (Message memory);

    function getMessageForAppTopic(
        uint256 idx,
        address app,
        string calldata topic
    ) external view returns (Message memory);

    function getMessageForAppUserTopic(
        uint256 idx,
        address app,
        address user,
        string calldata topic
    ) external view returns (Message memory);

    // Fetch multiple messages

    function getMessagesInRange(
        uint256 startIdx,
        uint256 endIdx
    ) external view returns (Message[] memory);

    function getMessagesInRangeForApp(
        uint256 startIdx,
        uint256 endIdx,
        address app
    ) external view returns (Message[] memory);

    function getMessagesInRangeForAppUser(
        uint256 startIdx,
        uint256 endIdx,
        address app,
        address user
    ) external view returns (Message[] memory);

    function getMessagesInRangeForAppTopic(
        uint256 startIdx,
        uint256 endIdx,
        address app,
        string calldata topic
    ) external view returns (Message[] memory);

    function getMessagesInRangeForAppUserTopic(
        uint256 startIdx,
        uint256 endIdx,
        address app,
        address user,
        string calldata topic
    ) external view returns (Message[] memory);

    // **************
    // Message counts
    // **************

    function getTotalMessagesCount() external view returns (uint256);

    function getTotalMessagesForAppCount(
        address app
    ) external view returns (uint256);

    function getTotalMessagesForAppUserCount(
        address app,
        address user
    ) external view returns (uint256);

    function getTotalMessagesForAppTopicCount(
        address app,
        string calldata topic
    ) external view returns (uint256);

    function getTotalMessagesForAppUserTopicCount(
        address app,
        address user,
        string calldata topic
    ) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 .0;

/// @title Storage
/// @author Aspyn Palatnick (aspyn.eth, stuckinaboot.eth)
/// @notice Data storage with persistent history using Net
interface IStorage {
    event Stored(bytes32 indexed key, address indexed operator);

    struct BulkPutParams {
        bytes32 key;
        string text;
        bytes value;
    }

    struct BulkGetParams {
        bytes32 key;
        address operator;
    }

    struct BulkGetValueAtIndexParams {
        bytes32 key;
        address operator;
        uint256 idx;
    }

    struct BulkGetResult {
        string text;
        bytes value;
    }

    /// @notice Store value for a given key
    /// @param key key
    /// @param text text
    /// @param value value
    function put(
        bytes32 key,
        string calldata text,
        bytes calldata value
    ) external;

    /// @notice Store values for given params
    /// @param params params
    function bulkPut(BulkPutParams[] calldata params) external;

    /// @notice Get value for a particular key and operator
    /// @param key key
    /// @param operator user that stored key
    /// @return (text, data) stored value for the particular key-operator pair
    function get(
        bytes32 key,
        address operator
    ) external view returns (string memory, bytes memory);

    /// @notice Bulk get values for particular parameters
    /// @param params parameters
    /// @return results for stored values for the particular parameters
    function bulkGet(
        BulkGetParams[] calldata params
    ) external view returns (BulkGetResult[] memory results);

    /// @notice Get value at index for a particular key and operator
    /// @param key key
    /// @param operator user that stored key
    /// @param idx index
    /// @return (text, data) stored value at index for the particular key-operator pair
    function getValueAtIndex(
        bytes32 key,
        address operator,
        uint256 idx
    ) external view returns (string memory, bytes memory);

    /// @notice Bulk get value at index for particular parameters
    /// @param params parameters
    /// @return results for stored values for the particular parameters
    function bulkGetValueAtIndex(
        BulkGetValueAtIndexParams[] calldata params
    ) external view returns (BulkGetResult[] memory results);

    /// @notice Get total number of writes to a particular key for a given operator
    /// @param key key
    /// @param operator user that stored key
    /// @return total total writes count
    function getTotalWrites(
        bytes32 key,
        address operator
    ) external view returns (uint256);

    /// @notice Bulk get total writes for particular parameters
    /// @param params parameters
    /// @return results for total writes for the particular parameters
    function bulkGetTotalWrites(
        BulkGetParams[] calldata params
    ) external view returns (uint256[] memory results);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The `length` of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = type(uint256).max;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     DECIMAL OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits.
            str := add(mload(0x40), 0x80)
            // Update the free memory pointer to allocate.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            let w := not(0) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {
                let temp := value
            } 1 {

            } {
                str := add(str, w) // `sub(str, 1)`.
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                if iszero(temp) {
                    break
                }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) {
            return toString(uint256(value));
        }
        unchecked {
            str = toString(uint256(-value));
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   HEXADECIMAL OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(
        uint256 value,
        uint256 length
    ) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexStringNoPrefix(
        uint256 value,
        uint256 length
    ) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let start := sub(str, add(length, length))
            let w := not(1) // Tsk.
            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {

            } 1 {

            } {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(str, start)) {
                    break
                }
            }

            if temp {
                // Store the function selector of `HexLengthInsufficient()`.
                mstore(0x00, 0x2194895a)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(
        uint256 value
    ) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(
        uint256 value
    ) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            str := add(mload(0x40), 0x80)
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let w := not(1) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {
                let temp := value
            } 1 {

            } {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) {
                    break
                }
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksumed(
        address value
    ) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            for {
                let i := 0
            } 1 {

            } {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                if eq(i, 20) {
                    break
                }
            }
            mstore(
                o,
                xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))
            )
            o := add(o, 0x20)
            mstore(
                o,
                xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))
            )
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(
        address value
    ) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(
        address value
    ) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)

            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            str := add(str, 2)
            mstore(str, 40)

            let o := add(str, 0x20)
            mstore(add(o, 40), 0)

            value := shl(96, value)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {
                let i := 0
            } 1 {

            } {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                if eq(i, 20) {
                    break
                }
            }
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexString(
        bytes memory raw
    ) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(
        bytes memory raw
    ) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(raw)
            str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(str, add(length, length)) // Store the length of the output.

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let o := add(str, 0x20)
            let end := add(raw, length)

            for {

            } iszero(eq(raw, end)) {

            } {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, and(add(o, 31), not(31))) // Allocate the memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(
                    0x20,
                    0x0202020202020202020202020202020202020202020202020303030304040506
                )
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for {
                    result := 1
                } 1 {
                    result := add(result, 1)
                } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    if iszero(lt(o, end)) {
                        break
                    }
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance and bytecode compactness, all indices of the following operations
    // are byte (ASCII) offsets, not UTF character offsets.

    /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
    function replace(
        string memory subject,
        string memory search,
        string memory replacement
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                for {

                } 1 {

                } {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) {
                                    break
                                }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for {
                            let o := 0
                        } 1 {

                        } {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            if iszero(lt(o, replacementLength)) {
                                break
                            }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) {
                                break
                            }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) {
                        break
                    }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            for {

            } lt(subject, subjectEnd) {

            } {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            // Zeroize the slot after the string.
            let last := add(add(result, 0x20), k)
            mstore(last, 0)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
            // Store the length of the result.
            mstore(result, k)
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let subjectLength := mload(subject)
            } 1 {

            } {
                if iszero(mload(search)) {
                    if iszero(gt(from, subjectLength)) {
                        result := from
                        break
                    }
                    result := subjectLength
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)

                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let end := add(
                    sub(add(subjectStart, subjectLength), searchLength),
                    1
                )

                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(add(search, 0x20))

                if iszero(and(lt(subject, end), lt(from, subjectLength))) {
                    break
                }

                if iszero(lt(searchLength, 32)) {
                    for {
                        let h := keccak256(add(search, 0x20), searchLength)
                    } 1 {

                    } {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) {
                            break
                        }
                    }
                    break
                }
                for {

                } 1 {

                } {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, end)) {
                        break
                    }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(
        string memory subject,
        string memory search
    ) internal pure returns (uint256 result) {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {

            } 1 {

            } {
                result := not(0) // Initialize to `NOT_FOUND`.
                let searchLength := mload(search)
                if gt(searchLength, mload(subject)) {
                    break
                }
                let w := result

                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) {
                    from := fromMax
                }

                let end := add(add(subject, 0x20), w)
                subject := add(add(subject, 0x20), from)
                if iszero(gt(subject, end)) {
                    break
                }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                for {
                    let h := keccak256(add(search, 0x20), searchLength)
                } 1 {

                } {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(end, 1))
                        break
                    }
                    subject := add(subject, w) // `sub(subject, 1)`.
                    if iszero(gt(subject, end)) {
                        break
                    }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(
        string memory subject,
        string memory search
    ) internal pure returns (uint256 result) {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(
        string memory subject,
        string memory search
    ) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(
                    keccak256(add(subject, 0x20), searchLength),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(
        string memory subject,
        string memory search
    ) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(
                            add(subject, 0x20),
                            mul(withinRange, sub(subjectLength, searchLength))
                        ),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(
        string memory subject,
        uint256 times
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                for {

                } 1 {

                } {
                    // Copy the `subject` one word at a time.
                    for {
                        let o := 0
                    } 1 {

                    } {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        if iszero(lt(o, subjectLength)) {
                            break
                        }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    if iszero(times) {
                        break
                    }
                }
                // Zeroize the slot after the string.
                mstore(output, 0)
                // Store the length.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(
        string memory subject,
        uint256 start,
        uint256 end
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) {
                end := subjectLength
            }
            if iszero(gt(subjectLength, start)) {
                start := subjectLength
            }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                let w := not(31)
                // Copy the `subject` one word at a time, backwards.
                for {
                    let o := and(add(resultLength, 31), w)
                } 1 {

                } {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) {
                        break
                    }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), w)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(
        string memory subject,
        uint256 start
    ) internal pure returns (string memory result) {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(
        string memory subject,
        string memory search
    ) internal pure returns (uint256[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(
                    sub(add(subject, subjectLength), searchLength),
                    1
                )
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                for {

                } 1 {

                } {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) {
                                    break
                                }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) {
                                break
                            }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) {
                        break
                    }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(
        string memory subject,
        string memory delimiter
    ) internal pure returns (string[] memory result) {
        uint256[] memory indices = indicesOf(subject, delimiter);
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(31)
            let indexPtr := add(indices, 0x20)
            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
            mstore(add(indicesEnd, w), mload(subject))
            mstore(indices, add(mload(indices), 1))
            let prevIndex := 0
            for {

            } 1 {

            } {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let elementLength := sub(index, prevIndex)
                    mstore(element, elementLength)
                    // Copy the `subject` one word at a time, backwards.
                    for {
                        let o := and(add(elementLength, 31), w)
                    } 1 {

                    } {
                        mstore(
                            add(element, o),
                            mload(add(add(subject, prevIndex), o))
                        )
                        o := add(o, w) // `sub(o, 0x20)`.
                        if iszero(o) {
                            break
                        }
                    }
                    // Zeroize the slot after the string.
                    mstore(add(add(element, 0x20), elementLength), 0)
                    // Allocate memory for the length and the bytes,
                    // rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(elementLength, 63), w)))
                    // Store the `element` into the array.
                    mstore(indexPtr, element)
                }
                prevIndex := add(index, mload(delimiter))
                indexPtr := add(indexPtr, 0x20)
                if iszero(lt(indexPtr, indicesEnd)) {
                    break
                }
            }
            result := indices
            if iszero(mload(delimiter)) {
                result := add(indices, 0x20)
                mstore(result, sub(mload(indices), 2))
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(
        string memory a,
        string memory b
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(31)
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            for {
                let o := and(add(mload(a), 32), w)
            } 1 {

            } {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) {
                    break
                }
            }
            let bLength := mload(b)
            let output := add(result, mload(a))
            // Copy `b` one word at a time, backwards.
            for {
                let o := and(add(bLength, 32), w)
            } 1 {

            } {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) {
                    break
                }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), w))
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    function toCase(
        string memory subject,
        bool toUpper
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 67108863)
                let w := not(0)
                for {
                    let o := length
                } 1 {

                } {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    if iszero(o) {
                        break
                    }
                }
                // Restore the result.
                result := mload(0x40)
                // Stores the string length.
                mstore(result, length)
                // Zeroize the slot after the string.
                let last := add(add(result, 0x20), length)
                mstore(last, 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, and(add(last, 31), not(31)))
            }
        }
    }

    /// @dev Returns a lowercased copy of the string.
    function lower(
        string memory subject
    ) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    function upper(
        string memory subject
    ) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(
        string memory s
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store the bytes of the packed offsets and strides into the scratch space.
                // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                mstore(0x1f, 0x900094)
                mstore(0x08, 0xc0000000a6ab)
                // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                mstore(
                    0x00,
                    shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)
                )
            } iszero(eq(s, end)) {

            } {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(result, c)
                    result := add(result, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 31)))
                result := add(result, shr(5, t))
            }
            let last := result
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Restore the result to the start of the free memory.
            result := mload(0x40)
            // Store the length of the result.
            mstore(result, sub(last, add(result, 0x20)))
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(
        string memory s
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store "\\u0000" in scratch space.
                // Store "0123456789abcdef" in scratch space.
                // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
                // into the scratch space.
                mstore(
                    0x15,
                    0x5c75303030303031323334353637383961626364656662746e006672
                )
                // Bitmask for detecting `["\"","\\"]`.
                let e := or(shl(0x22, 1), shl(0x5c, 1))
            } iszero(eq(s, end)) {

            } {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) {
                        // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c)
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) {
                    // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            let last := result
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Restore the result to the start of the free memory.
            result := mload(0x40)
            // Store the length of the result.
            mstore(result, sub(last, add(result, 0x20)))
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(
        string memory a,
        string memory b
    ) internal pure returns (bool result) {
        assembly {
            result := eq(
                keccak256(add(a, 0x20), mload(a)),
                keccak256(add(b, 0x20), mload(b))
            )
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes.
                mload(add(a, 0x1f)),
                // `length != 0 && length < 32`. Abuses underflow.
                // Assumes that the length is valid and within the block gas limit.
                lt(sub(mload(a), 1), 0x1f)
            )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behaviour is undefined.
    function unpackOne(
        bytes32 packed
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(
        string memory a,
        string memory b
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes of `a` and `b`.
                or(
                    shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
                    mload(sub(add(b, 0x1e), aLength))
                ),
                // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                // Assumes that the lengths are valid and within the block gas limit.
                lt(sub(add(aLength, mload(b)), 1), 0x1e)
            )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behaviour is undefined.
    function unpackTwo(
        bytes32 packed
    ) internal pure returns (string memory resultA, string memory resultB) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(
                add(resultB, 0x1f),
                mload(add(add(resultA, 0x20), mload(resultA)))
            )
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Assumes that the string does not start from the scratch space.
            let retStart := sub(a, 0x20)
            let retSize := add(mload(a), 0x40)
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(retStart, retSize), 0)
            // Store the return offset.
            mstore(retStart, 0x20)
            // End the transaction, returning the string.
            return(retStart, retSize)
        }
    }
}

File 7 of 18 : BangerV3Token.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract BangerV3Token is ERC20 {
    address public deployer;
    uint256 public fid;
    string public image;
    string public animation;
    string public extraStringData;
    bytes public extraBytesData;

    constructor(
        string memory name_,
        string memory symbol_,
        uint256 maxSupply_,
        address deployer_,
        uint256 fid_,
        string memory image_,
        string memory animation_,
        string memory extraStringData_,
        bytes memory extraBytesData_
    ) ERC20(name_, symbol_) {
        deployer = deployer_;
        fid = fid_;
        image = image_;
        animation = animation_;
        extraStringData = extraStringData_;
        extraBytesData = extraBytesData_;
        _mint(msg.sender, maxSupply_);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

interface INonfungiblePositionManager {
    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function mint(
        MintParams calldata params
    )
        external
        payable
        returns (
            uint256 tokenId,
            uint128 liquidity,
            uint256 amount0,
            uint256 amount1
        );

    function createAndInitializePoolIfNecessary(
        address token0,
        address token1,
        uint24 fee,
        uint160 sqrtPriceX96
    ) external payable returns (address pool);

    function collect(
        CollectParams calldata params
    ) external payable returns (uint256 amount0, uint256 amount1);

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;
}

interface IUniswapV3Factory {
    function initialize(uint160 sqrtPriceX96) external;

    function createPool(
        address tokenA,
        address tokenB,
        uint24 fee
    ) external returns (address pool);

    function feeAmountTickSpacing(uint24 fee) external view returns (int24);
}

interface ILockerFactory {
    function deploy(
        address token,
        address beneficiary,
        uint64 durationSeconds,
        uint256 tokenId,
        uint256 fees
    ) external payable returns (address);
}

interface ILocker {
    function initializer(uint256 tokenId) external;
}

struct ExactInputSingleParams {
    address tokenIn;
    address tokenOut;
    uint24 fee;
    address recipient;
    uint256 amountIn;
    uint256 amountOutMinimum;
    uint160 sqrtPriceLimitX96;
}

interface ISwapRouter {
    function exactInputSingle(
        ExactInputSingleParams calldata params
    ) external payable returns (uint256 amountOut);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
        uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
        require(absTick <= uint256(int256(MAX_TICK)), 'T');

        uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
        if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
        if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
        if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
        if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
        if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
        if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
        if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
        if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
        if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
        if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
        if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
        if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
        if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
        if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
        if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
        if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
        if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
        if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;

        if (tick > 0) ratio = type(uint256).max / ratio;

        // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
        // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
        // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
        sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
    }

    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        // second inequality must be < because the price can never reach the price at the max tick
        require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R');
        uint256 ratio = uint256(sqrtPriceX96) << 32;

        uint256 r = ratio;
        uint256 msb = 0;

        assembly {
            let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(5, gt(r, 0xFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(4, gt(r, 0xFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(3, gt(r, 0xFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(2, gt(r, 0xF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(1, gt(r, 0x3))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := gt(r, 0x1)
            msb := or(msb, f)
        }

        if (msb >= 128) r = ratio >> (msb - 127);
        else r = ratio << (127 - msb);

        int256 log_2 = (int256(msb) - 128) << 64;

        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(63, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(62, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(61, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(60, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(59, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(58, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(57, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(56, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(55, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(54, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(53, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(52, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(51, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(50, f))
        }

        int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

        int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
        int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

        tick = tickLow == tickHi
            ? tickLow
            : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96
                ? tickHi
                : tickLow;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library to encode strings in Base64.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
/// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>.
library Base64 {
    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// See: https://datatracker.ietf.org/doc/html/rfc4648
    /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
    /// @param noPadding Whether to strip away the padding.
    function encode(
        bytes memory data,
        bool fileSafe,
        bool noPadding
    ) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                // Multiply by 4/3 rounded up.
                // The `shl(2, ...)` is equivalent to multiplying by 4.
                let encodedLength := shl(2, div(add(dataLength, 2), 3))

                // Set `result` to point to the start of the free memory.
                result := mload(0x40)

                // Store the table into the scratch space.
                // Offsetted by -1 byte so that the `mload` will load the character.
                // We will rewrite the free memory pointer at `0x40` later with
                // the allocated size.
                // The magic constant 0x0670 will turn "-_" into "+/".
                mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                mstore(
                    0x3f,
                    xor(
                        "ghijklmnopqrstuvwxyz0123456789-_",
                        mul(iszero(fileSafe), 0x0670)
                    )
                )

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, encodedLength)

                let dataEnd := add(add(0x20, data), dataLength)
                let dataEndValue := mload(dataEnd) // Cache the value at the `dataEnd` slot.
                mstore(dataEnd, 0x00) // Zeroize the `dataEnd` slot to clear dirty bits.

                // Run over the input, 3 bytes at a time.
                for {

                } 1 {

                } {
                    data := add(data, 3) // Advance 3 bytes.
                    let input := mload(data)

                    // Write 4 bytes. Optimized for fewer stack operations.
                    mstore8(0, mload(and(shr(18, input), 0x3F)))
                    mstore8(1, mload(and(shr(12, input), 0x3F)))
                    mstore8(2, mload(and(shr(6, input), 0x3F)))
                    mstore8(3, mload(and(input, 0x3F)))
                    mstore(ptr, mload(0x00))

                    ptr := add(ptr, 4) // Advance 4 bytes.
                    if iszero(lt(ptr, end)) {
                        break
                    }
                }
                mstore(dataEnd, dataEndValue) // Restore the cached value at `dataEnd`.
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                // Equivalent to `o = [0, 2, 1][dataLength % 3]`.
                let o := div(2, mod(dataLength, 3))
                // Offset `ptr` and pad with '='. We can simply write over the end.
                mstore(sub(ptr, o), shl(240, 0x3d3d))
                // Set `o` to zero if there is padding.
                o := mul(iszero(iszero(noPadding)), o)
                mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
                mstore(result, sub(encodedLength, o)) // Store the length.
            }
        }
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, false, false)`.
    function encode(
        bytes memory data
    ) internal pure returns (string memory result) {
        result = encode(data, false, false);
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, fileSafe, false)`.
    function encode(
        bytes memory data,
        bool fileSafe
    ) internal pure returns (string memory result) {
        result = encode(data, fileSafe, false);
    }

    /// @dev Decodes base64 encoded `data`.
    ///
    /// Supports:
    /// - RFC 4648 (both standard and file-safe mode).
    /// - RFC 3501 (63: ',').
    ///
    /// Does not support:
    /// - Line breaks.
    ///
    /// Note: For performance reasons,
    /// this function will NOT revert on invalid `data` inputs.
    /// Outputs for invalid inputs will simply be undefined behaviour.
    /// It is the user's responsibility to ensure that the `data`
    /// is a valid base64 encoded string.
    function decode(
        string memory data
    ) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                let decodedLength := mul(shr(2, dataLength), 3)

                for {

                } 1 {

                } {
                    // If padded.
                    if iszero(and(dataLength, 3)) {
                        let t := xor(mload(add(data, dataLength)), 0x3d3d)
                        // forgefmt: disable-next-item
                        decodedLength := sub(
                            decodedLength,
                            add(iszero(byte(30, t)), iszero(byte(31, t)))
                        )
                        break
                    }
                    // If non-padded.
                    decodedLength := add(
                        decodedLength,
                        sub(and(dataLength, 3), 1)
                    )
                    break
                }
                result := mload(0x40)

                // Write the length of the bytes.
                mstore(result, decodedLength)

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, decodedLength)

                // Load the table into the scratch space.
                // Constants are optimized for smaller bytecode with zero gas overhead.
                // `m` also doubles as the mask of the upper 6 bits.
                let
                    m
                := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                mstore(0x5b, m)
                mstore(
                    0x3b,
                    0x04080c1014181c2024282c3034383c4044484c5054585c6064
                )
                mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)

                for {

                } 1 {

                } {
                    // Read 4 bytes.
                    data := add(data, 4)
                    let input := mload(data)

                    // Write 3 bytes.
                    // forgefmt: disable-next-item
                    mstore(
                        ptr,
                        or(
                            and(m, mload(byte(28, input))),
                            shr(
                                6,
                                or(
                                    and(m, mload(byte(29, input))),
                                    shr(
                                        6,
                                        or(
                                            and(m, mload(byte(30, input))),
                                            shr(6, mload(byte(31, input)))
                                        )
                                    )
                                )
                            )
                        )
                    )
                    ptr := add(ptr, 3)
                    if iszero(lt(ptr, end)) {
                        break
                    }
                }
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                mstore(end, 0) // Zeroize the slot after the bytes.
                mstore(0x60, 0) // Restore the zero slot.
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

/**
 * @dev Interface that must be implemented by smart contracts in order to receive
 * ERC-1155 token transfers.
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

Settings
{
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "appendCBOR": true,
    "bytecodeHash": "ipfs",
    "useLiteralContent": false
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "lib/v3-core/=lib/v3-core/",
    "lib/openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "src/SocialDexDeployer.sol/=src/SocialDexDeployer.sol/",
    "src/interface.sol/=src/interface.sol/",
    "src/Bytes32AddressLib.sol/=src/Bytes32AddressLib.sol/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "v3-core/=lib/v3-core/",
    "v3-periphery/=lib/v3-periphery/contracts/"
  ],
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Deprecated","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpNftId","type":"uint256"},{"indexed":false,"internalType":"address","name":"deployer","type":"address"},{"indexed":false,"internalType":"uint256","name":"fid","type":"uint256"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint256","name":"supply","type":"uint256"},{"indexed":false,"internalType":"address","name":"lockerAddress","type":"address"},{"indexed":false,"internalType":"string","name":"extraStringData","type":"string"},{"indexed":false,"internalType":"bool","name":"isMsgSenderOwner","type":"bool"}],"name":"TokenCreated","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"bundleFeeSwitch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultLockingPeriod","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_supply","type":"uint256"},{"internalType":"int24","name":"_initialTick","type":"int24"},{"internalType":"bytes32","name":"_salt","type":"bytes32"},{"internalType":"address","name":"_deployer","type":"address"},{"internalType":"uint256","name":"_fid","type":"uint256"},{"internalType":"uint256","name":"mintPrice","type":"uint256"},{"internalType":"uint256","name":"mintEndTimestamp","type":"uint256"},{"internalType":"uint256","name":"maxMintSupply","type":"uint256"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"string","name":"_image","type":"string"},{"internalType":"string","name":"_animation","type":"string"},{"internalType":"string","name":"_extraStringData","type":"string"},{"internalType":"bytes","name":"_extraBytesData","type":"bytes"}],"name":"deployToken","outputs":[{"internalType":"contract BangerV3Token","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"deprecated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"deployer","type":"address"},{"internalType":"uint256","name":"fid","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"image","type":"string"},{"internalType":"string","name":"animation","type":"string"},{"internalType":"string","name":"extraStringData","type":"string"},{"internalType":"bytes","name":"extraBytesData","type":"bytes"},{"internalType":"uint256","name":"supply","type":"uint256"}],"name":"generateSalt","outputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint24","name":"_fee","type":"uint24"}],"name":"initialSwapTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"liquidityLocker","outputs":[{"internalType":"contract ILockerFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpFeesCut","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"contract INonfungiblePositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"deployer","type":"address"},{"internalType":"uint256","name":"fid","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"image","type":"string"},{"internalType":"string","name":"animation","type":"string"},{"internalType":"string","name":"extraStringData","type":"string"},{"internalType":"bytes","name":"extraBytesData","type":"bytes"},{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"predictToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolCut","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_deprecated","type":"bool"}],"name":"setDeprecated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxRate","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"toggleBundleFeeSwitch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapV3Factory","outputs":[{"internalType":"contract IUniswapV3Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"newPeriod","type":"uint64"}],"name":"updateDefaultLockingPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newLocker","type":"address"}],"name":"updateLiquidityLocker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPositionManager","type":"address"}],"name":"updatePositionManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"newFee","type":"uint8"}],"name":"updateProtocolFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRouter","type":"address"}],"name":"updateSwapRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newCollector","type":"address"}],"name":"updateTaxCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"newRate","type":"uint8"}],"name":"updateTaxRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFactory","type":"address"}],"name":"updateUniV3Factory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWeth","type":"address"}],"name":"updateWeth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

6080806040523461016e576000805473cc4193b652fcb25347bb44a88d2b09b52585643f6001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3600180547e1e321900000007bf59f7c52a67566660714afad6eb80a8a64eb7431b121f3c7fff00000000000000000000000000000000000000000000000000000000000000909116179055600280546001600160b81b03191672a4944d1e707f869356ff4b48e527370f002710179055600380546001600160a01b0319908116734200000000000000000000000000000000000006179091556004805482167333128a8fc17869897dce68ed026d694621f6fdfd179055600580549091167303a520b32c04bf3beef7beb72e919cf822ed34f117905560068054600161ff0160a01b0319167501002626664c2603336e57b271c5c0b26f421741e48117905561470990816101748239f35b600080fdfe61056080604052600436101561001b575b50361561001957005b005b600061012052610120513560e01c90816301ffc9a714612e2c575080630e136b1914612e0457806310f3577e14612d515780631962d94714612d0b5780631b5ad80114612cc55780632bdd20ef14612c7f57806337ad470114610f405780633aa10f1f14610ee25780633fc8cef314610eb757806340c10f1914610a6c5780634de2fae714610a2257806357aa0201146109fc5780635b549182146109d15780636881d8cb1461098b578063715018a614610929578063771a3a1d14610903578063791b98bc146108d85780637ebdf9ab146108b05780638da5cb5b146108835780639759164a146108545780639dbb8c9f14610803578063b0fad6f0146107b9578063b98b677f14610773578063bc197c81146106db578063bea1dcf8146106b0578063c31c9c0714610685578063cb1e82ee14610576578063d406d7511461054a578063d848dee714610500578063ddca3f43146104db578063de04f42e146104b5578063e42efd541461046b578063f23a6e6114610415578063f2fde38b1461037f5763f408ad2a146101b15738610010565b3461037857610120366003190112610378576101cb612e62565b610420526044356001600160401b038111610378576101ee903690600401612e8e565b61046052610480526064356001600160401b03811161037857610215903690600401612e8e565b6104a052610440526084356001600160401b0381116103785761023c903690600401612e8e565b6103205260a4356001600160401b0381116103785761025f903690600401612e8e565b610380526103605260c4356001600160401b03811161037857610286903690600401612e8e565b6103c0526103a05260e4356001600160401b038111610378576102ad903690600401612e8e565b610400526103e052610120516003546001600160a01b0316610340525b8061030c8261010435610400516103e0516103c0516103a0516103805161036051610320518c6104a0516104405161046051610480516024356104205161318b565b610340519091906001600160a01b038316108061036f575b610355575050600019811461033b576001016102ca565b634e487b7160e01b61012051526011600452602461012051fd5b604080519182526001600160a01b03929092166020820152f35b50813b15610324565b6101205180fd5b3461037857602036600319011261037857610398612e62565b6103a06132b3565b6001600160a01b031680156103f9576101205180546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36101205180f35b631e4fbdf760e01b610120515261012051600452602461012051fd5b346103785760a03660031901126103785761042e612e62565b50610437612e78565b506084356001600160401b03811161037857610457903690600401612e8e565b505060405163f23a6e6160e01b8152602090f35b346103785760203660031901126103785760043560ff81168103610378576104916132b3565b6001805460ff60e01b191660e09290921b60ff60e01b169190911790556101205180f35b34610378576101205136600319011261037857602060ff60015460f01c16604051908152f35b34610378576101205136600319011261037857602062ffffff60025416604051908152f35b3461037857602036600319011261037857600435801515809103610378576105266132b3565b6006805460ff60a01b191660a09290921b60ff60a01b169190911790556101205180f35b3461037857610120513660031901126103785760206001600160401b0360015460a01c16604051908152f35b346103785761014036600319011261037857610590612e62565b6044356001600160401b038111610378576105af903690600401612e8e565b906064356001600160401b038111610378576105cf903690600401612e8e565b93906084356001600160401b038111610378576105f0903690600401612e8e565b60a4959195356001600160401b03811161037857610612903690600401612e8e565b60c4929192356001600160401b03811161037857610634903690600401612e8e565b93909260e435996001600160401b038b116103785760209b61065d6106739c3690600401612e8e565b999098610124359c610104359c6024359061318b565b6040516001600160a01b039091168152f35b346103785761012051366003190112610378576006546040516001600160a01b039091168152602090f35b346103785761012051366003190112610378576001546040516001600160a01b039091168152602090f35b346103785760a0366003190112610378576106f4612e62565b506106fd612e78565b506044356001600160401b0381116103785761071d903690600401612ebb565b50506064356001600160401b0381116103785761073e903690600401612ebb565b50506084356001600160401b0381116103785761075f903690600401612e8e565b505060405163bc197c8160e01b8152602090f35b346103785760203660031901126103785761078c612e62565b6107946132b3565b60018060a01b03166001600160601b0360a01b60065416176006556101205161012051f35b3461037857602036600319011261037857600435801515809103610378576107df6132b3565b6006805460ff60a81b191660a89290921b60ff60a81b169190911790556101205180f35b346103785760203660031901126103785761081c612e62565b6108246132b3565b600280546301000000600160b81b03191660189290921b6301000000600160b81b03169190911790556101205180f35b3461037857610120513660031901126103785760025460405160189190911c6001600160a01b03168152602090f35b3461037857610120513660031901126103785761012051546040516001600160a01b039091168152602090f35b34610378576101205136600319011261037857602060ff60065460a81c166040519015158152f35b346103785761012051366003190112610378576005546040516001600160a01b039091168152602090f35b34610378576101205136600319011261037857602060ff60015460e01c16604051908152f35b346103785761012051366003190112610378576109446132b3565b6101205180546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a36101205180f35b34610378576020366003190112610378576109a4612e62565b6109ac6132b3565b60018060a01b03166001600160601b0360a01b60055416176005556101205161012051f35b346103785761012051366003190112610378576004546040516001600160a01b039091168152602090f35b34610378576101205136600319011261037857602060ff60015460e81c16604051908152f35b346103785760203660031901126103785760043560ff8116810361037857610a486132b3565b6001805460ff60e81b191660e89290921b60ff60e81b169190911790556101205180f35b604036600319011261037857610a80612e62565b60405163041804f760e31b8152610120516001600160a01b0383166004830181905230602480850191909152909392903591816044816fdb40fcb9f4466330982372e27fd7bbf55afa908115610d9b576101205191610e33575b5060c08180518101031261037857604081015192610afa60608301613177565b610b1260a0610b0b60808601613177565b9401613177565b506001600160a01b0316803b1561037857604051630d9778e560e11b8152856004820152846024820152610120518160448134865af18015610d9b57610e18575b50803b156103785760405190637921219560e11b825230600483015233602483015285604483015284606483015260a060848301526101205160a48301528160c481610120519361012051905af18015610d9b57610dfd575b50610bb6906132dc565b92604051926020840152604083015283606083015260018060a01b0316608082015260808152610be760a082612f1c565b6fb24d62781db359b07880a105cd0b64e63b1561037857610c49610c5f9260405193849283926308521f2760e21b84523360048501526080602485015260046084850152631b5a5b9d60e21b60a485015260c0604485015260c48401906130f6565b82810360031901606484015261012051946130f6565b0381610120516fb24d62781db359b07880a105cd0b64e65af18015610d9b57610de2575b5034610c91575b6101205180f35b6040516324a9d85360e01b81526020816004816f1c147e48f5aad3c69e8e4d2305523c625afa908115610d9b576101205191610da9575b50610d5c610ce6612710610cdf6020943490613082565b04346130ab565b600354600254604051929562ffffff909116916001600160a01b0316610d0b84612eeb565b83528483015260408201523360608201528360808201526101205160a08201526101205160c082015260018060a01b0360065416906040519485809481936304e45aaf60e01b835260048301612f4c565b03925af18015610d9b5715610c8a575b610d8d9060203d602011610d94575b610d858183612f1c565b810190612f3d565b5080610c8a565b503d610d7b565b6040513d61012051823e3d90fd5b90506020813d602011610dda575b81610dc460209383612f1c565b81010312610dd55751610d5c610cc8565b600080fd5b3d9150610db7565b61012051610def91612f1c565b610120516103785781610c83565b61012051610e0a91612f1c565b610120516103785785610bac565b61012051610e2591612f1c565b610120516103785786610b53565b90503d8061012051833e610e478183612f1c565b8101906040818303126103785780516001600160401b03811161037857810182601f820112156103785782816020610e8193519101613140565b506020810151906001600160401b03821161037857019080601f83011215610378578151610eb192602001613140565b84610ada565b346103785761012051366003190112610378576003546040516001600160a01b039091168152602090f35b34610378576020366003190112610378576004356001600160401b038116810361037857610f0e6132b3565b6001805467ffffffffffffffff60a01b191660a09290921b67ffffffffffffffff60a01b169190911790556101205180f35b6101c036600319011261037857602435610520526105205160020b6105205103610378576064356101408190526001600160a01b038116900361037857610104356001600160401b03811161037857610f9d903690600401612e8e565b6101605261018052610124356001600160401b03811161037857610fc5903690600401612e8e565b6102a0526102c052610144356001600160401b03811161037857610fed903690600401612e8e565b60e05261054052610164356001600160401b03811161037857611014903690600401612e8e565b60c052610184356001600160401b03811161037857611037903690600401612e8e565b6104e0526104c0526101a4356001600160401b0381116103785761105f903690600401612e8e565b906102205260ff60065460a01c16612c6a5760018060a01b0360045416602062ffffff600254166024604051809481936322afcccb60e01b835260048301525afa908115610d9b576101205191612c2e575b508060020b151580612c0f575b15612bdb5760408051610140516001600160a01b031660208201908152604435928201929092526110fc81606081015b03601f198101835282612f1c565b519020604051806112cc8101106001600160401b036112cc83011117612552576112cc61340882398061116785610220516104e0516104c05160c0518b60e05161054051608435610140516004356102a0518d6102c05190610160519061018051906112cc01612fcb565b039061012051f5610500526105005115610d9b57600354610500516001600160a01b039182169116811115612ba757610120516105205160020b1215612b9b576105205160020b600160ff1b1461033b576105205160020b61012051035b620d89e891828211612b72576001821615612b60576001600160881b036ffffcb933bd6fad37aa2d162d1a5940015b169160028116612b15575b60048116612aca575b60088116612a7f575b60108116612a34575b602081166129e9575b6040811661299e575b60808116612953575b6101008116612908575b61020081166128bd575b6104008116612872575b6108008116612827575b61100081166127dc575b6120008116612791575b6140008116612746575b61800081166126fb575b6201000081166126b0575b620200008116612668575b620400008116612623575b62080000166125e4575b610120516105205160020b136125d3575b602082901c9163ffffffff166125cb5761012051905b60ff82168301831161033b576004805460025460405163a167129560e01b8152610500516001600160a01b0390811694820194909452602481019490945262ffffff16604484015261012051602092849260649284929091165af18015610d9b576101205161010052612599575b50610100516001600160a01b03163b156103785760405163f637731d60e01b815260ff919091169091016001600160a01b03908116600483015261012051610100519091839160249183918591165af18015610d9b57612586575b506003546001600160a01b0316610300526002805462ffffff169083900b1561256c576040516102e08190526001600160401b036101608201908111911117612552576101606102e0510160405260018060a01b0361050051166102e051526103005160206102e051015260406102e05101526105205160020b60606102e05101528160020b90050260020b60806102e051015260043560a06102e05101526101205160c06102e05101526101205160e06102e0510152610120516101006102e0510152306101206102e0510152426101406102e051015260018060a01b03600554166040519063095ea7b360e01b8252600482015260043560248201526020816044816101205160018060a01b0361050051165af18015610d9b57612518575b5060018060a01b03600554166102805260405161026052634418b22b60e11b610260515260018060a01b036102e0515116600461026051015260018060a01b0360206102e051015116602461026051015262ffffff60406102e051015116604461026051015260606102e051015160020b606461026051015260806102e051015160020b608461026051015260a06102e051015160a461026051015260c06102e051015160c461026051015260e06102e051015160e46102605101526101006102e051015161010461026051015260018060a01b036101206102e0510151166101246102605101526101406102e05101516101446102605101526080610260516101646102605161012051610280515af1610240526102405115610d9b576101205161020052610240516124bb575b602060018060a01b0360025460181c1660ff60018060a01b0360055416600154906040516101e052634e54db0960e01b6101e0515260046101e051015260018060a01b03610140511660246101e05101526001600160401b038160a01c1660446101e05101526102005160646101e051015260e81c1660846101e051015260a46101e051916101e0519061012051905af16101c0526101c05115610d9b57610120516101a0526101c051612481575b6005546001600160a01b0316803b1561037857604051632142170760e11b81523060048201526101a0516001600160a01b031660248201526102005160448201526101205190918290606490829084905af18015610d9b57612466575b506101a0516001600160a01b03163b15610378576040516367a4d1c360e01b8152610200516004820152610120516101a0518290602490829084906001600160a01b03165af18015610d9b5761244b575b50346122c8575b60405163b8070c7360e01b8152906020826004816fb24d62781db359b07880a105cd0b64e65afa60a05260a05115610d9b57610120519160a051612294575b506118796001600860056117b0838060a01b0361050051166132dc565b60026117be610100516132dc565b916040516080526802232b83637bcb2b2160bd1b602060805101526101605161018051602960805101376029610160516080510161040560f31b828201526102a0516102c051602b8301376102a051010164014903a37960dd1b838201526118308251809360206007850191016130d3565b010167010143837b7b61d160c51b83820152611856825180936020600d850191016130d3565b0101602960f81b82820152608051900301601e1981016080515201608051612f1c565b610500516110ee9061192e90611897906001600160a01b03166132dc565b9260405192839186602084015260018060a01b036101005116604084015260018060a01b036105005116606084015260018060a01b036101a0511660808401526102005160a084015260018060a01b03610140511660c084015260843560e084015261012061010084015261191761014084016104e0516104c051612faa565b90601f198483030161012085015261022051612faa565b6fb24d62781db359b07880a105cd0b64e63b1561037857610c4961198c9260405193849283926308521f2760e21b84523360048501526080602485015261197a608485016080516130f6565b848103600319016044860152906130f6565b0381610120516fb24d62781db359b07880a105cd0b64e65af18015610d9b57612279575b50604051633ee60bcf60e01b8152916020836004816f1c147e48f5aad3c69e8e4d2305523c625afa928315610d9b576101205193612245575b5061050051611a00906001600160a01b03166132dc565b60e05115611f7657611a1360e0516130b8565b611a206040519182612f1c565b60e05181523660e051610540510111610378576013611bae92600b6002956004879560e05161054051602083013761012051602060e051830101525b6047604051998a96683d913730b6b2911d1160b91b6020890152610160516101805160298a0137602961016051890161040560f31b828201526102a0516102c051602b8301376102a0510101602960f81b838201527f222c226465736372697074696f6e223a224d656d65636f696e2d4e465420637260038201527f656174656420766961204e65742e204d656d65636f696e206973206c6f636174602382015265032b21030ba160d51b6043820152611b208251809360206049850191016130d3565b0101632e372e3760e11b828201526104e0516104c051604b8301376104e05101016a11161134b6b0b3b2911d1160a91b83820152611b68825180936020600f850191016130d3565b0101907211161130b734b6b0ba34b7b72fbab936111d1160691b8383015260c05190601e83013760c051010161227d60f01b838201520301601d19810184520182612f1c565b6f1c147e48f5aad3c69e8e4d2305523c623b156103785760405190630dd2f7a160e41b825260a435600483015260e435602483015260c435604483015261012051606483015260a060848301528180611c0f610120519360a48301906130f6565b0381610120516f1c147e48f5aad3c69e8e4d2305523c625af18015610d9b57611f5b575b506f1c147e48f5aad3c69e8e4d2305523c623b1561037857604051637921219560e11b8152306004820152610140516001600160a01b03166024820152604481018390526001606482015260a06084820152610120805160a4830152518160c481836f1c147e48f5aad3c69e8e4d2305523c625af18015610d9b57611f40575b5060408051602081019290925281018290526f1c147e48f5aad3c69e8e4d2305523c626060820152610100516001600160a01b0390811660808301526101a0511660a08201526102005160c0808301919091528152611d1360e082612f1c565b6fdb40fcb9f4466330982372e27fd7bbf53b15610378576040519063f9ed11d960e01b82528180611d57610120519360018060a01b0361050051166004840161311b565b0381610120516fdb40fcb9f4466330982372e27fd7bbf55af18015610d9b57611f25575b506040519080602083015260018060a01b036105005116604083015260018060a01b036101005116606083015260608252611db7608083612f1c565b6fdb40fcb9f4466330982372e27fd7bbf53b1561037857611df1916040518093819263f9ed11d960e01b835261012051946004840161311b565b0381610120516fdb40fcb9f4466330982372e27fd7bbf55af18015610d9b57611f0a575b7f7c8762543264172a17e130518fcc375c045781639d522d793332fc5c76a5ffe560018060a01b036101205154163314604051809160018060a01b036105005116825261020051602083015260018060a01b036101405116604083015260843560608301526101406080830152611ee2611eb2611e9d61014085016101605161018051612faa565b84810360a08601526102a0516102c051612faa565b60043560c08501526101a0516001600160a01b031660e08501528381036101008501526104e0516104c051612faa565b906101208301520390a160408051610500516001600160a01b03168152610200516020820152f35b61012051611f1791612f1c565b610120516103785780611e15565b61012051611f3291612f1c565b610120516103785781611d7b565b61012051611f4d91612f1c565b610120516103785782611cb3565b61012051611f6891612f1c565b610120516103785782611c33565b6040517f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323060208201527f30302f737667222077696474683d2235303022206865696768743d223530302260408201527f207374796c653d226261636b67726f756e642d636f6c6f723a23303030223e3c60608201527f7465787420783d223530252220793d223530252220746578742d616e63686f7260808201527f3d226d6964646c652220646f6d696e616e742d626173656c696e653d226d696460a08201527f646c6522207374796c653d22666f6e742d66616d696c793a6d6f6e6f7370616360c08201527f653b666f6e742d73697a653a323470783b66696c6c3a23306637223e0000000060e0820152610160516101805160fc8301376120c3600d60fc836101605181016c1e17ba32bc3a1f1e17b9bb339f60991b838201520301601219810184520182612f1c565b8051606091808261213a575b5050506013611bae92600b60029560048795612135603a60405180937f646174613a696d6167652f7376672b786d6c3b6261736536342c000000000000602083015261212481518092602086860191016130d3565b81010301601f198101835282612f1c565b611a5c565b9193909250604051937f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106707f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f526020850160046003602084870101519661012051602086890101525b0191603f8351818160121c165161012051538181600c1c1651600153818160061c1651600253165160035361012051518152019060206003600285010460021b880101821015612200576004906003906121ab565b509281016020019390935260036002848101829004811b80870160409081019052613d3d60f01b9290950690048303526101205190915290825290601382600b6120cf565b9092506020813d602011612271575b8161226160209383612f1c565b81010312610dd5575191836119e9565b3d9150612254565b6101205161228691612f1c565b6101205161037857826119b0565b9091506020813d6020116122c0575b816122b060209383612f1c565b81010312610dd557519083611793565b3d91506122a3565b3460ff60065460a81c16612397575b6003546002546040516020926123639262ffffff16906001600160a01b03166122ff83612eeb565b8252610500516001600160a01b0390811685840152604080840192909252610140518116606084015260808301869052610120805160a08501525160c084015260065491516304e45aaf60e01b815295869492909116928492839160048301612f4c565b03925af18015610d9b57612378575b50611754565b6123909060203d602011610d9457610d858183612f1c565b5082612372565b506001546103e86123ae60ff8360e01c1634613082565b046123b981346130ab565b61012051909290918291829182916001600160a01b03165af13d15612446573d6123e2816130b8565b906123f06040519283612f1c565b8152610120513d916020013e5b6122d75760405162461bcd60e51b815260206004820152601c60248201527f4661696c656420746f2073656e642070726f746f636f6c2066656573000000006044820152606490fd5b6123fd565b6101205161245891612f1c565b61012051610378578261174d565b6101205161247391612f1c565b6101205161037857826116fc565b60203d6020116124b4575b8061249d6124ab926101e051612f1c565b6101e051016101e051613063565b6101a05261169f565b503d61248c565b60803d608011612511575b806124d660809261026051612f1c565b61026051908101031261037857610260515160206102605101516fffffffffffffffffffffffffffffffff81160361037857610200526115f0565b503d6124c6565b6020813d60201161254a575b8161253160209383612f1c565b81010312610378575180151581146114c1576101205180fd5b3d9150612524565b634e487b7160e01b61012051526041600452602461012051fd5b634e487b7160e01b61012051526012600452602461012051fd5b6101205161259391612f1c565b846113a0565b6125ba9060203d6020116125c4575b6125b28183612f1c565b810190613063565b6101005286611345565b503d6125a8565b6001906112d7565b90801561256c5760001904906112c1565b90806b048a170391f7dc42444e8fa28102046b048a170391f7dc42444e8fa2148115171561033b576b048a170391f7dc42444e8fa20260801c906112b0565b91806d2216e584f5fa1ea926041bedfe988102046d2216e584f5fa1ea926041bedfe98148115171561033b576d2216e584f5fa1ea926041bedfe980260801c916112a6565b91806e5d6af8dedb81196699c329225ee6048102046e5d6af8dedb81196699c329225ee604148115171561033b576e5d6af8dedb81196699c329225ee6040260801c9161129b565b91806f09aa508b5b7a84e1c677de54f3e99bc98102046f09aa508b5b7a84e1c677de54f3e99bc9148115171561033b576f09aa508b5b7a84e1c677de54f3e99bc90260801c91611290565b91806f31be135f97d08fd981231505542fcfa68102046f31be135f97d08fd981231505542fcfa6148115171561033b576f31be135f97d08fd981231505542fcfa60260801c91611285565b91806f70d869a156d2a1b890bb3df62baf32f78102046f70d869a156d2a1b890bb3df62baf32f7148115171561033b576f70d869a156d2a1b890bb3df62baf32f70260801c9161127b565b91806fa9f746462d870fdf8a65dc1f90e061e58102046fa9f746462d870fdf8a65dc1f90e061e5148115171561033b576fa9f746462d870fdf8a65dc1f90e061e50260801c91611271565b91806fd097f3bdfd2022b8845ad8f792aa58258102046fd097f3bdfd2022b8845ad8f792aa5825148115171561033b576fd097f3bdfd2022b8845ad8f792aa58250260801c91611267565b91806fe7159475a2c29b7443b29c7fa6e889d98102046fe7159475a2c29b7443b29c7fa6e889d9148115171561033b576fe7159475a2c29b7443b29c7fa6e889d90260801c9161125d565b91806ff3392b0822b70005940c7a398e4b70f38102046ff3392b0822b70005940c7a398e4b70f3148115171561033b576ff3392b0822b70005940c7a398e4b70f30260801c91611253565b91806ff987a7253ac413176f2b074cf7815e548102046ff987a7253ac413176f2b074cf7815e54148115171561033b576ff987a7253ac413176f2b074cf7815e540260801c91611249565b91806ffcbe86c7900a88aedcffc83b479aa3a48102046ffcbe86c7900a88aedcffc83b479aa3a4148115171561033b576ffcbe86c7900a88aedcffc83b479aa3a40260801c9161123f565b91806ffe5dee046a99a2a811c461f1969c30538102046ffe5dee046a99a2a811c461f1969c3053148115171561033b576ffe5dee046a99a2a811c461f1969c30530260801c91611235565b91806fff2ea16466c96a3843ec78b326b528618102046fff2ea16466c96a3843ec78b326b52861148115171561033b576fff2ea16466c96a3843ec78b326b528610260801c9161122c565b91806fff973b41fa98c081472e6896dfb254c08102046fff973b41fa98c081472e6896dfb254c0148115171561033b576fff973b41fa98c081472e6896dfb254c00260801c91611223565b91806fffcb9843d60f6159c9db58835c9266448102046fffcb9843d60f6159c9db58835c926644148115171561033b576fffcb9843d60f6159c9db58835c9266440260801c9161121a565b91806fffe5caca7e10e4e61c3624eaa0941cd08102046fffe5caca7e10e4e61c3624eaa0941cd0148115171561033b576fffe5caca7e10e4e61c3624eaa0941cd00260801c91611211565b91806ffff2e50f5f656932ef12357cf3c7fdcc8102046ffff2e50f5f656932ef12357cf3c7fdcc148115171561033b576ffff2e50f5f656932ef12357cf3c7fdcc0260801c91611208565b91806ffff97272373d413259a46990580e213a8102046ffff97272373d413259a46990580e213a148115171561033b576ffff97272373d413259a46990580e213a0260801c916111ff565b6001600160881b03600160801b6111f4565b60405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606490fd5b6105205160020b6111c5565b60405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081cd85b1d60a21b6044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b496e76616c6964207469636b60a01b6044820152606490fd5b508060020b1561256c578060020b6105205160020b0760020b156110be565b90506020813d602011612c62575b81612c4960209383612f1c565b8101031261037857518060020b810361037857836110b1565b3d9150612c3c565b6331cee75f60e21b6101205152600461012051fd5b3461037857602036600319011261037857612c98612e62565b612ca06132b3565b60018060a01b03166001600160601b0360a01b60045416176004556101205161012051f35b3461037857602036600319011261037857612cde612e62565b612ce66132b3565b60018060a01b03166001600160601b0360a01b60035416176003556101205161012051f35b3461037857602036600319011261037857612d24612e62565b612d2c6132b3565b60018060a01b03166001600160601b0360a01b60015416176001556101205161012051f35b604036600319011261037857612d65612e62565b60243562ffffff811680910361037857600354604051612ded9360209391926001600160a01b0316612d9684612eeb565b835260018060a01b03168383015260408201523360608201523460808201526101205160a08201526101205160c082015260018060a01b036006541660405180809581946304e45aaf60e01b835260048301612f4c565b039134905af18015610d9b57610d6c576101205180f35b34610378576101205136600319011261037857602060ff60065460a01c166040519015158152f35b34610378576020366003190112610378576004359063ffffffff60e01b821680920361037857602091630271189760e51b148152f35b600435906001600160a01b0382168203610dd557565b602435906001600160a01b0382168203610dd557565b9181601f84011215610dd5578235916001600160401b038311610dd55760208381860195010111610dd557565b9181601f84011215610dd5578235916001600160401b038311610dd5576020808501948460051b010111610dd557565b60e081019081106001600160401b03821117612f0657604052565b634e487b7160e01b600052604160045260246000fd5b90601f801991011681019081106001600160401b03821117612f0657604052565b90816020910312610dd5575190565b81516001600160a01b03908116825260208084015182169083015260408084015162ffffff16908301526060808401518216908301526080808401519083015260a0808401519083015260c092830151169181019190915260e00190565b908060209392818452848401376000828201840152601f01601f1916010190565b9d9b9996906130609f9d99938f90976130106130519e9961303599986130026080966130439f996101208852610120880191612faa565b918583036020870152612faa565b60408301979097526001600160a01b0316606082015201528c830360a08e0152612faa565b9189830360c08b0152612faa565b9186830360e0880152612faa565b92610100818503910152612faa565b90565b90816020910312610dd557516001600160a01b0381168103610dd55790565b8181029291811591840414171561309557565b634e487b7160e01b600052601160045260246000fd5b9190820391821161309557565b6001600160401b038111612f0657601f01601f191660200190565b60005b8381106130e65750506000910152565b81810151838201526020016130d6565b9060209161310f815180928185528580860191016130d3565b601f01601f1916010190565b60809061306093928152606060208201526000606082015281604082015201906130f6565b9092919261314d816130b8565b9161315b6040519384612f1c565b829482845282820111610dd55760206131759301906130d3565b565b51906001600160a01b0382168203610dd557565b90919a97949d9e9c9996939c9b9895929b6040518060208101926131c590858560209093929193604081019460018060a01b031681520152565b03601f19810182526131d79082612f1c565b5190209e6040519e8f6112cc6020016131f09082612f1c565b6112cc81526112cc9061340890602001396040519e8f9e8f6020019e6132159f612fcb565b03601f19810182526132279082612f1c565b60405180916020820193848151602081930191613243926130d3565b82018151918260208301916020019161325b926130d3565b010380825261326d9060200182612f1c565b5190209060405191602083019160ff60f81b83523060601b602185015260358401526055830152605582526132a3607583612f1c565b905190206001600160a01b031690565b6000546001600160a01b031633036132c757565b63118cdaa760e01b6000523360045260246000fd5b9060405191608083016040526f30313233343536373839616263646566600f526002830190602882526000604a85015260601b600160005b8080018601602284831a600f811651602384015360041c519101530190601482146133425790600190613314565b50506130786002825101915282528160228101907f88888888888888888888888888888888888888888888888888888888888888806028832016600160005b601160f31b83821a02818001520190601482146133a15790600190613381565b505060429180517f2020202020202020202020202020202020202020202020202020202020202020816000511660011c161890520180517f2020202020202020202020202020202020202020202020202020202020202020816020511660011c1618905256fe60806040523461085b576112cc8038038061001981610860565b9283398101906101208183031261085b5780516001600160401b03811161085b57826100469183016108df565b60208201516001600160401b03811161085b57836100659184016108df565b604083015160608401519091906001600160a01b0381169081900361085b57608085015160a08601519091906001600160401b03811161085b57876100ab9188016108df565b60c08701519093906001600160401b03811161085b57886100cd9189016108df565b60e08801519098906001600160401b03811161085b57816100ef918a016108df565b61010089015190986001600160401b03821161085b57019080601f8301121561085b57815161012092602001610885565b865190966001600160401b0382116104b15761013d6003546108fc565b601f811161080c575b50602090601f83116001146107a3576101789291600091836103e7575b50508160011b916000199060031b1c19161790565b6003555b8051906001600160401b0382116104b1576101986004546108fc565b601f8111610754575b50602090601f83116001146106eb576101d29291600091836103e75750508160011b916000199060031b1c19161790565b6004555b600580546001600160a01b0319169190911790556006558051906001600160401b0382116104b1576102096007546108fc565b601f811161069c575b50602090601f8311600114610633576102439291600091836103e75750508160011b916000199060031b1c19161790565b6007555b83516001600160401b0381116104b1576102626008546108fc565b601f81116105e4575b50602094601f821160011461057d5761029f9293949582916000926103e75750508160011b916000199060031b1c19161790565b6008555b82516001600160401b0381116104b1576102be6009546108fc565b601f811161052e575b506020601f82116001146104c75781906102f99394956000926103e75750508160011b916000199060031b1c19161790565b6009555b81516001600160401b0381116104b157610318600a546108fc565b601f8111610457575b50602092601f82116001146103f257610353929382916000926103e75750508160011b916000199060031b1c19161790565b600a555b33156103d1576002548181018091116103bb57600255600033815280602052604081208281540190556040519182527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a360405161097e908161094e8239f35b634e487b7160e01b600052601160045260246000fd5b63ec442f0560e01b600052600060045260246000fd5b015190503880610163565b601f19821693600a600052806000209160005b86811061043f5750836001959610610426575b505050811b01600a55610357565b015160001960f88460031b161c19169055388080610418565b91926020600181928685015181550194019201610405565b600a6000526104a1907fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8601f840160051c810191602085106104a7575b601f0160051c0190610936565b38610321565b9091508190610494565b634e487b7160e01b600052604160045260246000fd5b601f198216906009600052806000209160005b818110610516575095836001959697106104fd575b505050811b016009556102fd565b015160001960f88460031b161c191690553880806104ef565b9192602060018192868b0151815501940192016104da565b6009600052610577907f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af601f840160051c810191602085106104a757601f0160051c0190610936565b386102c7565b601f198216956008600052806000209160005b8881106105cc575083600195969798106105b3575b505050811b016008556102a3565b015160001960f88460031b161c191690553880806105a5565b91926020600181928685015181550194019201610590565b600860005261062d907ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3601f840160051c810191602085106104a757601f0160051c0190610936565b3861026b565b90601f198316916007600052816000209260005b818110610684575090846001959493921061066b575b505050811b01600755610247565b015160001960f88460031b161c1916905538808061065d565b92936020600181928786015181550195019301610647565b60076000526106e5907fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688601f850160051c810191602086106104a757601f0160051c0190610936565b38610212565b90601f198316916004600052816000209260005b81811061073c5750908460019594939210610723575b505050811b016004556101d6565b015160001960f88460031b161c19169055388080610715565b929360206001819287860151815501950193016106ff565b600460005261079d907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f850160051c810191602086106104a757601f0160051c0190610936565b386101a1565b90601f198316916003600052816000209260005b8181106107f457509084600195949392106107db575b505050811b0160035561017c565b015160001960f88460031b161c191690553880806107cd565b929360206001819287860151815501950193016107b7565b6003600052610855907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f850160051c810191602086106104a757601f0160051c0190610936565b38610146565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176104b157604052565b9192916001600160401b0382116104b1576108a9601f8301601f1916602001610860565b938285528282011161085b5760005b8281106108cc575050602060009184010152565b80602091830151828288010152016108b8565b9080601f8301121561085b5781516108f992602001610885565b90565b90600182811c9216801561092c575b602083101461091657565b634e487b7160e01b600052602260045260246000fd5b91607f169161090b565b818110610941575050565b6000815560010161093656fe6080604052600436101561001257600080fd5b60003560e01c806306fdde03146106fc578063095ea7b31461067657806318160ddd1461065857806323b872dd1461056b578063313ce5671461054f5780633257b4f31461053157806370a08231146104f75780638c662b7f1461044f57806395d89b41146103a7578063977fb1cc146102ff578063a9059cbb146102ce578063d5f39488146102a5578063dd62ed3e14610254578063e8a5edce146101ab5763f3ccaac0146100c157600080fd5b346101a65760003660031901126101a65760405160006007546100e381610811565b80845290600181169081156101825750600114610123575b61011f8361010b8185038261084b565b6040519182916020835260208301906107a4565b0390f35b600760009081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688939250905b8082106101685750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610150565b60ff191660208086019190915291151560051b8401909101915061010b90506100fb565b600080fd5b346101a65760003660031901126101a6576040516000600a546101cd81610811565b808452906001811690811561018257506001146101f45761011f8361010b8185038261084b565b919050600a6000527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8916000905b80821061023a5750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610222565b346101a65760403660031901126101a65761026d6107e5565b6102756107fb565b6001600160a01b039182166000908152600160209081526040808320949093168252928352819020549051908152f35b346101a65760003660031901126101a6576005546040516001600160a01b039091168152602090f35b346101a65760403660031901126101a6576102f46102ea6107e5565b6024359033610883565b602060405160018152f35b346101a65760003660031901126101a657604051600060085461032181610811565b808452906001811690811561018257506001146103485761011f8361010b8185038261084b565b600860009081527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3939250905b80821061038d5750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610375565b346101a65760003660031901126101a65760405160006004546103c981610811565b808452906001811690811561018257506001146103f05761011f8361010b8185038261084b565b600460009081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b8082106104355750909150810160200161010b6100fb565b91926001816020925483858801015201910190929161041d565b346101a65760003660031901126101a657604051600060095461047181610811565b808452906001811690811561018257506001146104985761011f8361010b8185038261084b565b600960009081527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af939250905b8082106104dd5750909150810160200161010b6100fb565b9192600181602092548385880101520191019092916104c5565b346101a65760203660031901126101a6576001600160a01b036105186107e5565b1660005260006020526020604060002054604051908152f35b346101a65760003660031901126101a6576020600654604051908152f35b346101a65760003660031901126101a657602060405160128152f35b346101a65760603660031901126101a6576105846107e5565b61058c6107fb565b6001600160a01b03821660008181526001602081815260408084203385529091529091205491936044359392909181016105cc575b506102f49350610883565b83811061063b57841561062557331561060f576102f4946000526001602052604060002060018060a01b03331660005260205283604060002091039055846105c1565b634a1406b160e11b600052600060045260246000fd5b63e602df0560e01b600052600060045260246000fd5b8390637dc7a0d960e11b6000523360045260245260445260646000fd5b346101a65760003660031901126101a6576020600254604051908152f35b346101a65760403660031901126101a65761068f6107e5565b602435903315610625576001600160a01b031690811561060f57336000526001602052604060002082600052602052806040600020556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b346101a65760003660031901126101a657604051600060035461071e81610811565b808452906001811690811561018257506001146107455761011f8361010b8185038261084b565b600360009081527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b939250905b80821061078a5750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610772565b919082519283825260005b8481106107d0575050826000602080949584010152601f8019910116010190565b806020809284010151828286010152016107af565b600435906001600160a01b03821682036101a657565b602435906001600160a01b03821682036101a657565b90600182811c92168015610841575b602083101461082b57565b634e487b7160e01b600052602260045260246000fd5b91607f1691610820565b90601f8019910116810190811067ffffffffffffffff82111761086d57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0316908115610932576001600160a01b031691821561091c5760008281528060205260408120548281106109025791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b916064928463391434e360e21b8452600452602452604452fd5b63ec442f0560e01b600052600060045260246000fd5b634b637e8f60e11b600052600060045260246000fdfea26469706673582212206c58257e9404c1e2494d4f719acb3b836cb49a49d92f4b5610d7074fbe24923364736f6c634300081a0033a264697066735822122041824f4abf3181b8b2a5453c96c8b2b4ad717e33543abb9ddf31ec180a02e07d64736f6c634300081a0033

Deployed Bytecode

0x61056080604052600436101561001b575b50361561001957005b005b600061012052610120513560e01c90816301ffc9a714612e2c575080630e136b1914612e0457806310f3577e14612d515780631962d94714612d0b5780631b5ad80114612cc55780632bdd20ef14612c7f57806337ad470114610f405780633aa10f1f14610ee25780633fc8cef314610eb757806340c10f1914610a6c5780634de2fae714610a2257806357aa0201146109fc5780635b549182146109d15780636881d8cb1461098b578063715018a614610929578063771a3a1d14610903578063791b98bc146108d85780637ebdf9ab146108b05780638da5cb5b146108835780639759164a146108545780639dbb8c9f14610803578063b0fad6f0146107b9578063b98b677f14610773578063bc197c81146106db578063bea1dcf8146106b0578063c31c9c0714610685578063cb1e82ee14610576578063d406d7511461054a578063d848dee714610500578063ddca3f43146104db578063de04f42e146104b5578063e42efd541461046b578063f23a6e6114610415578063f2fde38b1461037f5763f408ad2a146101b15738610010565b3461037857610120366003190112610378576101cb612e62565b610420526044356001600160401b038111610378576101ee903690600401612e8e565b61046052610480526064356001600160401b03811161037857610215903690600401612e8e565b6104a052610440526084356001600160401b0381116103785761023c903690600401612e8e565b6103205260a4356001600160401b0381116103785761025f903690600401612e8e565b610380526103605260c4356001600160401b03811161037857610286903690600401612e8e565b6103c0526103a05260e4356001600160401b038111610378576102ad903690600401612e8e565b610400526103e052610120516003546001600160a01b0316610340525b8061030c8261010435610400516103e0516103c0516103a0516103805161036051610320518c6104a0516104405161046051610480516024356104205161318b565b610340519091906001600160a01b038316108061036f575b610355575050600019811461033b576001016102ca565b634e487b7160e01b61012051526011600452602461012051fd5b604080519182526001600160a01b03929092166020820152f35b50813b15610324565b6101205180fd5b3461037857602036600319011261037857610398612e62565b6103a06132b3565b6001600160a01b031680156103f9576101205180546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36101205180f35b631e4fbdf760e01b610120515261012051600452602461012051fd5b346103785760a03660031901126103785761042e612e62565b50610437612e78565b506084356001600160401b03811161037857610457903690600401612e8e565b505060405163f23a6e6160e01b8152602090f35b346103785760203660031901126103785760043560ff81168103610378576104916132b3565b6001805460ff60e01b191660e09290921b60ff60e01b169190911790556101205180f35b34610378576101205136600319011261037857602060ff60015460f01c16604051908152f35b34610378576101205136600319011261037857602062ffffff60025416604051908152f35b3461037857602036600319011261037857600435801515809103610378576105266132b3565b6006805460ff60a01b191660a09290921b60ff60a01b169190911790556101205180f35b3461037857610120513660031901126103785760206001600160401b0360015460a01c16604051908152f35b346103785761014036600319011261037857610590612e62565b6044356001600160401b038111610378576105af903690600401612e8e565b906064356001600160401b038111610378576105cf903690600401612e8e565b93906084356001600160401b038111610378576105f0903690600401612e8e565b60a4959195356001600160401b03811161037857610612903690600401612e8e565b60c4929192356001600160401b03811161037857610634903690600401612e8e565b93909260e435996001600160401b038b116103785760209b61065d6106739c3690600401612e8e565b999098610124359c610104359c6024359061318b565b6040516001600160a01b039091168152f35b346103785761012051366003190112610378576006546040516001600160a01b039091168152602090f35b346103785761012051366003190112610378576001546040516001600160a01b039091168152602090f35b346103785760a0366003190112610378576106f4612e62565b506106fd612e78565b506044356001600160401b0381116103785761071d903690600401612ebb565b50506064356001600160401b0381116103785761073e903690600401612ebb565b50506084356001600160401b0381116103785761075f903690600401612e8e565b505060405163bc197c8160e01b8152602090f35b346103785760203660031901126103785761078c612e62565b6107946132b3565b60018060a01b03166001600160601b0360a01b60065416176006556101205161012051f35b3461037857602036600319011261037857600435801515809103610378576107df6132b3565b6006805460ff60a81b191660a89290921b60ff60a81b169190911790556101205180f35b346103785760203660031901126103785761081c612e62565b6108246132b3565b600280546301000000600160b81b03191660189290921b6301000000600160b81b03169190911790556101205180f35b3461037857610120513660031901126103785760025460405160189190911c6001600160a01b03168152602090f35b3461037857610120513660031901126103785761012051546040516001600160a01b039091168152602090f35b34610378576101205136600319011261037857602060ff60065460a81c166040519015158152f35b346103785761012051366003190112610378576005546040516001600160a01b039091168152602090f35b34610378576101205136600319011261037857602060ff60015460e01c16604051908152f35b346103785761012051366003190112610378576109446132b3565b6101205180546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a36101205180f35b34610378576020366003190112610378576109a4612e62565b6109ac6132b3565b60018060a01b03166001600160601b0360a01b60055416176005556101205161012051f35b346103785761012051366003190112610378576004546040516001600160a01b039091168152602090f35b34610378576101205136600319011261037857602060ff60015460e81c16604051908152f35b346103785760203660031901126103785760043560ff8116810361037857610a486132b3565b6001805460ff60e81b191660e89290921b60ff60e81b169190911790556101205180f35b604036600319011261037857610a80612e62565b60405163041804f760e31b8152610120516001600160a01b0383166004830181905230602480850191909152909392903591816044816fdb40fcb9f4466330982372e27fd7bbf55afa908115610d9b576101205191610e33575b5060c08180518101031261037857604081015192610afa60608301613177565b610b1260a0610b0b60808601613177565b9401613177565b506001600160a01b0316803b1561037857604051630d9778e560e11b8152856004820152846024820152610120518160448134865af18015610d9b57610e18575b50803b156103785760405190637921219560e11b825230600483015233602483015285604483015284606483015260a060848301526101205160a48301528160c481610120519361012051905af18015610d9b57610dfd575b50610bb6906132dc565b92604051926020840152604083015283606083015260018060a01b0316608082015260808152610be760a082612f1c565b6fb24d62781db359b07880a105cd0b64e63b1561037857610c49610c5f9260405193849283926308521f2760e21b84523360048501526080602485015260046084850152631b5a5b9d60e21b60a485015260c0604485015260c48401906130f6565b82810360031901606484015261012051946130f6565b0381610120516fb24d62781db359b07880a105cd0b64e65af18015610d9b57610de2575b5034610c91575b6101205180f35b6040516324a9d85360e01b81526020816004816f1c147e48f5aad3c69e8e4d2305523c625afa908115610d9b576101205191610da9575b50610d5c610ce6612710610cdf6020943490613082565b04346130ab565b600354600254604051929562ffffff909116916001600160a01b0316610d0b84612eeb565b83528483015260408201523360608201528360808201526101205160a08201526101205160c082015260018060a01b0360065416906040519485809481936304e45aaf60e01b835260048301612f4c565b03925af18015610d9b5715610c8a575b610d8d9060203d602011610d94575b610d858183612f1c565b810190612f3d565b5080610c8a565b503d610d7b565b6040513d61012051823e3d90fd5b90506020813d602011610dda575b81610dc460209383612f1c565b81010312610dd55751610d5c610cc8565b600080fd5b3d9150610db7565b61012051610def91612f1c565b610120516103785781610c83565b61012051610e0a91612f1c565b610120516103785785610bac565b61012051610e2591612f1c565b610120516103785786610b53565b90503d8061012051833e610e478183612f1c565b8101906040818303126103785780516001600160401b03811161037857810182601f820112156103785782816020610e8193519101613140565b506020810151906001600160401b03821161037857019080601f83011215610378578151610eb192602001613140565b84610ada565b346103785761012051366003190112610378576003546040516001600160a01b039091168152602090f35b34610378576020366003190112610378576004356001600160401b038116810361037857610f0e6132b3565b6001805467ffffffffffffffff60a01b191660a09290921b67ffffffffffffffff60a01b169190911790556101205180f35b6101c036600319011261037857602435610520526105205160020b6105205103610378576064356101408190526001600160a01b038116900361037857610104356001600160401b03811161037857610f9d903690600401612e8e565b6101605261018052610124356001600160401b03811161037857610fc5903690600401612e8e565b6102a0526102c052610144356001600160401b03811161037857610fed903690600401612e8e565b60e05261054052610164356001600160401b03811161037857611014903690600401612e8e565b60c052610184356001600160401b03811161037857611037903690600401612e8e565b6104e0526104c0526101a4356001600160401b0381116103785761105f903690600401612e8e565b906102205260ff60065460a01c16612c6a5760018060a01b0360045416602062ffffff600254166024604051809481936322afcccb60e01b835260048301525afa908115610d9b576101205191612c2e575b508060020b151580612c0f575b15612bdb5760408051610140516001600160a01b031660208201908152604435928201929092526110fc81606081015b03601f198101835282612f1c565b519020604051806112cc8101106001600160401b036112cc83011117612552576112cc61340882398061116785610220516104e0516104c05160c0518b60e05161054051608435610140516004356102a0518d6102c05190610160519061018051906112cc01612fcb565b039061012051f5610500526105005115610d9b57600354610500516001600160a01b039182169116811115612ba757610120516105205160020b1215612b9b576105205160020b600160ff1b1461033b576105205160020b61012051035b620d89e891828211612b72576001821615612b60576001600160881b036ffffcb933bd6fad37aa2d162d1a5940015b169160028116612b15575b60048116612aca575b60088116612a7f575b60108116612a34575b602081166129e9575b6040811661299e575b60808116612953575b6101008116612908575b61020081166128bd575b6104008116612872575b6108008116612827575b61100081166127dc575b6120008116612791575b6140008116612746575b61800081166126fb575b6201000081166126b0575b620200008116612668575b620400008116612623575b62080000166125e4575b610120516105205160020b136125d3575b602082901c9163ffffffff166125cb5761012051905b60ff82168301831161033b576004805460025460405163a167129560e01b8152610500516001600160a01b0390811694820194909452602481019490945262ffffff16604484015261012051602092849260649284929091165af18015610d9b576101205161010052612599575b50610100516001600160a01b03163b156103785760405163f637731d60e01b815260ff919091169091016001600160a01b03908116600483015261012051610100519091839160249183918591165af18015610d9b57612586575b506003546001600160a01b0316610300526002805462ffffff169083900b1561256c576040516102e08190526001600160401b036101608201908111911117612552576101606102e0510160405260018060a01b0361050051166102e051526103005160206102e051015260406102e05101526105205160020b60606102e05101528160020b90050260020b60806102e051015260043560a06102e05101526101205160c06102e05101526101205160e06102e0510152610120516101006102e0510152306101206102e0510152426101406102e051015260018060a01b03600554166040519063095ea7b360e01b8252600482015260043560248201526020816044816101205160018060a01b0361050051165af18015610d9b57612518575b5060018060a01b03600554166102805260405161026052634418b22b60e11b610260515260018060a01b036102e0515116600461026051015260018060a01b0360206102e051015116602461026051015262ffffff60406102e051015116604461026051015260606102e051015160020b606461026051015260806102e051015160020b608461026051015260a06102e051015160a461026051015260c06102e051015160c461026051015260e06102e051015160e46102605101526101006102e051015161010461026051015260018060a01b036101206102e0510151166101246102605101526101406102e05101516101446102605101526080610260516101646102605161012051610280515af1610240526102405115610d9b576101205161020052610240516124bb575b602060018060a01b0360025460181c1660ff60018060a01b0360055416600154906040516101e052634e54db0960e01b6101e0515260046101e051015260018060a01b03610140511660246101e05101526001600160401b038160a01c1660446101e05101526102005160646101e051015260e81c1660846101e051015260a46101e051916101e0519061012051905af16101c0526101c05115610d9b57610120516101a0526101c051612481575b6005546001600160a01b0316803b1561037857604051632142170760e11b81523060048201526101a0516001600160a01b031660248201526102005160448201526101205190918290606490829084905af18015610d9b57612466575b506101a0516001600160a01b03163b15610378576040516367a4d1c360e01b8152610200516004820152610120516101a0518290602490829084906001600160a01b03165af18015610d9b5761244b575b50346122c8575b60405163b8070c7360e01b8152906020826004816fb24d62781db359b07880a105cd0b64e65afa60a05260a05115610d9b57610120519160a051612294575b506118796001600860056117b0838060a01b0361050051166132dc565b60026117be610100516132dc565b916040516080526802232b83637bcb2b2160bd1b602060805101526101605161018051602960805101376029610160516080510161040560f31b828201526102a0516102c051602b8301376102a051010164014903a37960dd1b838201526118308251809360206007850191016130d3565b010167010143837b7b61d160c51b83820152611856825180936020600d850191016130d3565b0101602960f81b82820152608051900301601e1981016080515201608051612f1c565b610500516110ee9061192e90611897906001600160a01b03166132dc565b9260405192839186602084015260018060a01b036101005116604084015260018060a01b036105005116606084015260018060a01b036101a0511660808401526102005160a084015260018060a01b03610140511660c084015260843560e084015261012061010084015261191761014084016104e0516104c051612faa565b90601f198483030161012085015261022051612faa565b6fb24d62781db359b07880a105cd0b64e63b1561037857610c4961198c9260405193849283926308521f2760e21b84523360048501526080602485015261197a608485016080516130f6565b848103600319016044860152906130f6565b0381610120516fb24d62781db359b07880a105cd0b64e65af18015610d9b57612279575b50604051633ee60bcf60e01b8152916020836004816f1c147e48f5aad3c69e8e4d2305523c625afa928315610d9b576101205193612245575b5061050051611a00906001600160a01b03166132dc565b60e05115611f7657611a1360e0516130b8565b611a206040519182612f1c565b60e05181523660e051610540510111610378576013611bae92600b6002956004879560e05161054051602083013761012051602060e051830101525b6047604051998a96683d913730b6b2911d1160b91b6020890152610160516101805160298a0137602961016051890161040560f31b828201526102a0516102c051602b8301376102a0510101602960f81b838201527f222c226465736372697074696f6e223a224d656d65636f696e2d4e465420637260038201527f656174656420766961204e65742e204d656d65636f696e206973206c6f636174602382015265032b21030ba160d51b6043820152611b208251809360206049850191016130d3565b0101632e372e3760e11b828201526104e0516104c051604b8301376104e05101016a11161134b6b0b3b2911d1160a91b83820152611b68825180936020600f850191016130d3565b0101907211161130b734b6b0ba34b7b72fbab936111d1160691b8383015260c05190601e83013760c051010161227d60f01b838201520301601d19810184520182612f1c565b6f1c147e48f5aad3c69e8e4d2305523c623b156103785760405190630dd2f7a160e41b825260a435600483015260e435602483015260c435604483015261012051606483015260a060848301528180611c0f610120519360a48301906130f6565b0381610120516f1c147e48f5aad3c69e8e4d2305523c625af18015610d9b57611f5b575b506f1c147e48f5aad3c69e8e4d2305523c623b1561037857604051637921219560e11b8152306004820152610140516001600160a01b03166024820152604481018390526001606482015260a06084820152610120805160a4830152518160c481836f1c147e48f5aad3c69e8e4d2305523c625af18015610d9b57611f40575b5060408051602081019290925281018290526f1c147e48f5aad3c69e8e4d2305523c626060820152610100516001600160a01b0390811660808301526101a0511660a08201526102005160c0808301919091528152611d1360e082612f1c565b6fdb40fcb9f4466330982372e27fd7bbf53b15610378576040519063f9ed11d960e01b82528180611d57610120519360018060a01b0361050051166004840161311b565b0381610120516fdb40fcb9f4466330982372e27fd7bbf55af18015610d9b57611f25575b506040519080602083015260018060a01b036105005116604083015260018060a01b036101005116606083015260608252611db7608083612f1c565b6fdb40fcb9f4466330982372e27fd7bbf53b1561037857611df1916040518093819263f9ed11d960e01b835261012051946004840161311b565b0381610120516fdb40fcb9f4466330982372e27fd7bbf55af18015610d9b57611f0a575b7f7c8762543264172a17e130518fcc375c045781639d522d793332fc5c76a5ffe560018060a01b036101205154163314604051809160018060a01b036105005116825261020051602083015260018060a01b036101405116604083015260843560608301526101406080830152611ee2611eb2611e9d61014085016101605161018051612faa565b84810360a08601526102a0516102c051612faa565b60043560c08501526101a0516001600160a01b031660e08501528381036101008501526104e0516104c051612faa565b906101208301520390a160408051610500516001600160a01b03168152610200516020820152f35b61012051611f1791612f1c565b610120516103785780611e15565b61012051611f3291612f1c565b610120516103785781611d7b565b61012051611f4d91612f1c565b610120516103785782611cb3565b61012051611f6891612f1c565b610120516103785782611c33565b6040517f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323060208201527f30302f737667222077696474683d2235303022206865696768743d223530302260408201527f207374796c653d226261636b67726f756e642d636f6c6f723a23303030223e3c60608201527f7465787420783d223530252220793d223530252220746578742d616e63686f7260808201527f3d226d6964646c652220646f6d696e616e742d626173656c696e653d226d696460a08201527f646c6522207374796c653d22666f6e742d66616d696c793a6d6f6e6f7370616360c08201527f653b666f6e742d73697a653a323470783b66696c6c3a23306637223e0000000060e0820152610160516101805160fc8301376120c3600d60fc836101605181016c1e17ba32bc3a1f1e17b9bb339f60991b838201520301601219810184520182612f1c565b8051606091808261213a575b5050506013611bae92600b60029560048795612135603a60405180937f646174613a696d6167652f7376672b786d6c3b6261736536342c000000000000602083015261212481518092602086860191016130d3565b81010301601f198101835282612f1c565b611a5c565b9193909250604051937f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106707f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f526020850160046003602084870101519661012051602086890101525b0191603f8351818160121c165161012051538181600c1c1651600153818160061c1651600253165160035361012051518152019060206003600285010460021b880101821015612200576004906003906121ab565b509281016020019390935260036002848101829004811b80870160409081019052613d3d60f01b9290950690048303526101205190915290825290601382600b6120cf565b9092506020813d602011612271575b8161226160209383612f1c565b81010312610dd5575191836119e9565b3d9150612254565b6101205161228691612f1c565b6101205161037857826119b0565b9091506020813d6020116122c0575b816122b060209383612f1c565b81010312610dd557519083611793565b3d91506122a3565b3460ff60065460a81c16612397575b6003546002546040516020926123639262ffffff16906001600160a01b03166122ff83612eeb565b8252610500516001600160a01b0390811685840152604080840192909252610140518116606084015260808301869052610120805160a08501525160c084015260065491516304e45aaf60e01b815295869492909116928492839160048301612f4c565b03925af18015610d9b57612378575b50611754565b6123909060203d602011610d9457610d858183612f1c565b5082612372565b506001546103e86123ae60ff8360e01c1634613082565b046123b981346130ab565b61012051909290918291829182916001600160a01b03165af13d15612446573d6123e2816130b8565b906123f06040519283612f1c565b8152610120513d916020013e5b6122d75760405162461bcd60e51b815260206004820152601c60248201527f4661696c656420746f2073656e642070726f746f636f6c2066656573000000006044820152606490fd5b6123fd565b6101205161245891612f1c565b61012051610378578261174d565b6101205161247391612f1c565b6101205161037857826116fc565b60203d6020116124b4575b8061249d6124ab926101e051612f1c565b6101e051016101e051613063565b6101a05261169f565b503d61248c565b60803d608011612511575b806124d660809261026051612f1c565b61026051908101031261037857610260515160206102605101516fffffffffffffffffffffffffffffffff81160361037857610200526115f0565b503d6124c6565b6020813d60201161254a575b8161253160209383612f1c565b81010312610378575180151581146114c1576101205180fd5b3d9150612524565b634e487b7160e01b61012051526041600452602461012051fd5b634e487b7160e01b61012051526012600452602461012051fd5b6101205161259391612f1c565b846113a0565b6125ba9060203d6020116125c4575b6125b28183612f1c565b810190613063565b6101005286611345565b503d6125a8565b6001906112d7565b90801561256c5760001904906112c1565b90806b048a170391f7dc42444e8fa28102046b048a170391f7dc42444e8fa2148115171561033b576b048a170391f7dc42444e8fa20260801c906112b0565b91806d2216e584f5fa1ea926041bedfe988102046d2216e584f5fa1ea926041bedfe98148115171561033b576d2216e584f5fa1ea926041bedfe980260801c916112a6565b91806e5d6af8dedb81196699c329225ee6048102046e5d6af8dedb81196699c329225ee604148115171561033b576e5d6af8dedb81196699c329225ee6040260801c9161129b565b91806f09aa508b5b7a84e1c677de54f3e99bc98102046f09aa508b5b7a84e1c677de54f3e99bc9148115171561033b576f09aa508b5b7a84e1c677de54f3e99bc90260801c91611290565b91806f31be135f97d08fd981231505542fcfa68102046f31be135f97d08fd981231505542fcfa6148115171561033b576f31be135f97d08fd981231505542fcfa60260801c91611285565b91806f70d869a156d2a1b890bb3df62baf32f78102046f70d869a156d2a1b890bb3df62baf32f7148115171561033b576f70d869a156d2a1b890bb3df62baf32f70260801c9161127b565b91806fa9f746462d870fdf8a65dc1f90e061e58102046fa9f746462d870fdf8a65dc1f90e061e5148115171561033b576fa9f746462d870fdf8a65dc1f90e061e50260801c91611271565b91806fd097f3bdfd2022b8845ad8f792aa58258102046fd097f3bdfd2022b8845ad8f792aa5825148115171561033b576fd097f3bdfd2022b8845ad8f792aa58250260801c91611267565b91806fe7159475a2c29b7443b29c7fa6e889d98102046fe7159475a2c29b7443b29c7fa6e889d9148115171561033b576fe7159475a2c29b7443b29c7fa6e889d90260801c9161125d565b91806ff3392b0822b70005940c7a398e4b70f38102046ff3392b0822b70005940c7a398e4b70f3148115171561033b576ff3392b0822b70005940c7a398e4b70f30260801c91611253565b91806ff987a7253ac413176f2b074cf7815e548102046ff987a7253ac413176f2b074cf7815e54148115171561033b576ff987a7253ac413176f2b074cf7815e540260801c91611249565b91806ffcbe86c7900a88aedcffc83b479aa3a48102046ffcbe86c7900a88aedcffc83b479aa3a4148115171561033b576ffcbe86c7900a88aedcffc83b479aa3a40260801c9161123f565b91806ffe5dee046a99a2a811c461f1969c30538102046ffe5dee046a99a2a811c461f1969c3053148115171561033b576ffe5dee046a99a2a811c461f1969c30530260801c91611235565b91806fff2ea16466c96a3843ec78b326b528618102046fff2ea16466c96a3843ec78b326b52861148115171561033b576fff2ea16466c96a3843ec78b326b528610260801c9161122c565b91806fff973b41fa98c081472e6896dfb254c08102046fff973b41fa98c081472e6896dfb254c0148115171561033b576fff973b41fa98c081472e6896dfb254c00260801c91611223565b91806fffcb9843d60f6159c9db58835c9266448102046fffcb9843d60f6159c9db58835c926644148115171561033b576fffcb9843d60f6159c9db58835c9266440260801c9161121a565b91806fffe5caca7e10e4e61c3624eaa0941cd08102046fffe5caca7e10e4e61c3624eaa0941cd0148115171561033b576fffe5caca7e10e4e61c3624eaa0941cd00260801c91611211565b91806ffff2e50f5f656932ef12357cf3c7fdcc8102046ffff2e50f5f656932ef12357cf3c7fdcc148115171561033b576ffff2e50f5f656932ef12357cf3c7fdcc0260801c91611208565b91806ffff97272373d413259a46990580e213a8102046ffff97272373d413259a46990580e213a148115171561033b576ffff97272373d413259a46990580e213a0260801c916111ff565b6001600160881b03600160801b6111f4565b60405162461bcd60e51b81526020600482015260016024820152601560fa1b6044820152606490fd5b6105205160020b6111c5565b60405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081cd85b1d60a21b6044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b496e76616c6964207469636b60a01b6044820152606490fd5b508060020b1561256c578060020b6105205160020b0760020b156110be565b90506020813d602011612c62575b81612c4960209383612f1c565b8101031261037857518060020b810361037857836110b1565b3d9150612c3c565b6331cee75f60e21b6101205152600461012051fd5b3461037857602036600319011261037857612c98612e62565b612ca06132b3565b60018060a01b03166001600160601b0360a01b60045416176004556101205161012051f35b3461037857602036600319011261037857612cde612e62565b612ce66132b3565b60018060a01b03166001600160601b0360a01b60035416176003556101205161012051f35b3461037857602036600319011261037857612d24612e62565b612d2c6132b3565b60018060a01b03166001600160601b0360a01b60015416176001556101205161012051f35b604036600319011261037857612d65612e62565b60243562ffffff811680910361037857600354604051612ded9360209391926001600160a01b0316612d9684612eeb565b835260018060a01b03168383015260408201523360608201523460808201526101205160a08201526101205160c082015260018060a01b036006541660405180809581946304e45aaf60e01b835260048301612f4c565b039134905af18015610d9b57610d6c576101205180f35b34610378576101205136600319011261037857602060ff60065460a01c166040519015158152f35b34610378576020366003190112610378576004359063ffffffff60e01b821680920361037857602091630271189760e51b148152f35b600435906001600160a01b0382168203610dd557565b602435906001600160a01b0382168203610dd557565b9181601f84011215610dd5578235916001600160401b038311610dd55760208381860195010111610dd557565b9181601f84011215610dd5578235916001600160401b038311610dd5576020808501948460051b010111610dd557565b60e081019081106001600160401b03821117612f0657604052565b634e487b7160e01b600052604160045260246000fd5b90601f801991011681019081106001600160401b03821117612f0657604052565b90816020910312610dd5575190565b81516001600160a01b03908116825260208084015182169083015260408084015162ffffff16908301526060808401518216908301526080808401519083015260a0808401519083015260c092830151169181019190915260e00190565b908060209392818452848401376000828201840152601f01601f1916010190565b9d9b9996906130609f9d99938f90976130106130519e9961303599986130026080966130439f996101208852610120880191612faa565b918583036020870152612faa565b60408301979097526001600160a01b0316606082015201528c830360a08e0152612faa565b9189830360c08b0152612faa565b9186830360e0880152612faa565b92610100818503910152612faa565b90565b90816020910312610dd557516001600160a01b0381168103610dd55790565b8181029291811591840414171561309557565b634e487b7160e01b600052601160045260246000fd5b9190820391821161309557565b6001600160401b038111612f0657601f01601f191660200190565b60005b8381106130e65750506000910152565b81810151838201526020016130d6565b9060209161310f815180928185528580860191016130d3565b601f01601f1916010190565b60809061306093928152606060208201526000606082015281604082015201906130f6565b9092919261314d816130b8565b9161315b6040519384612f1c565b829482845282820111610dd55760206131759301906130d3565b565b51906001600160a01b0382168203610dd557565b90919a97949d9e9c9996939c9b9895929b6040518060208101926131c590858560209093929193604081019460018060a01b031681520152565b03601f19810182526131d79082612f1c565b5190209e6040519e8f6112cc6020016131f09082612f1c565b6112cc81526112cc9061340890602001396040519e8f9e8f6020019e6132159f612fcb565b03601f19810182526132279082612f1c565b60405180916020820193848151602081930191613243926130d3565b82018151918260208301916020019161325b926130d3565b010380825261326d9060200182612f1c565b5190209060405191602083019160ff60f81b83523060601b602185015260358401526055830152605582526132a3607583612f1c565b905190206001600160a01b031690565b6000546001600160a01b031633036132c757565b63118cdaa760e01b6000523360045260246000fd5b9060405191608083016040526f30313233343536373839616263646566600f526002830190602882526000604a85015260601b600160005b8080018601602284831a600f811651602384015360041c519101530190601482146133425790600190613314565b50506130786002825101915282528160228101907f88888888888888888888888888888888888888888888888888888888888888806028832016600160005b601160f31b83821a02818001520190601482146133a15790600190613381565b505060429180517f2020202020202020202020202020202020202020202020202020202020202020816000511660011c161890520180517f2020202020202020202020202020202020202020202020202020202020202020816020511660011c1618905256fe60806040523461085b576112cc8038038061001981610860565b9283398101906101208183031261085b5780516001600160401b03811161085b57826100469183016108df565b60208201516001600160401b03811161085b57836100659184016108df565b604083015160608401519091906001600160a01b0381169081900361085b57608085015160a08601519091906001600160401b03811161085b57876100ab9188016108df565b60c08701519093906001600160401b03811161085b57886100cd9189016108df565b60e08801519098906001600160401b03811161085b57816100ef918a016108df565b61010089015190986001600160401b03821161085b57019080601f8301121561085b57815161012092602001610885565b865190966001600160401b0382116104b15761013d6003546108fc565b601f811161080c575b50602090601f83116001146107a3576101789291600091836103e7575b50508160011b916000199060031b1c19161790565b6003555b8051906001600160401b0382116104b1576101986004546108fc565b601f8111610754575b50602090601f83116001146106eb576101d29291600091836103e75750508160011b916000199060031b1c19161790565b6004555b600580546001600160a01b0319169190911790556006558051906001600160401b0382116104b1576102096007546108fc565b601f811161069c575b50602090601f8311600114610633576102439291600091836103e75750508160011b916000199060031b1c19161790565b6007555b83516001600160401b0381116104b1576102626008546108fc565b601f81116105e4575b50602094601f821160011461057d5761029f9293949582916000926103e75750508160011b916000199060031b1c19161790565b6008555b82516001600160401b0381116104b1576102be6009546108fc565b601f811161052e575b506020601f82116001146104c75781906102f99394956000926103e75750508160011b916000199060031b1c19161790565b6009555b81516001600160401b0381116104b157610318600a546108fc565b601f8111610457575b50602092601f82116001146103f257610353929382916000926103e75750508160011b916000199060031b1c19161790565b600a555b33156103d1576002548181018091116103bb57600255600033815280602052604081208281540190556040519182527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a360405161097e908161094e8239f35b634e487b7160e01b600052601160045260246000fd5b63ec442f0560e01b600052600060045260246000fd5b015190503880610163565b601f19821693600a600052806000209160005b86811061043f5750836001959610610426575b505050811b01600a55610357565b015160001960f88460031b161c19169055388080610418565b91926020600181928685015181550194019201610405565b600a6000526104a1907fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8601f840160051c810191602085106104a7575b601f0160051c0190610936565b38610321565b9091508190610494565b634e487b7160e01b600052604160045260246000fd5b601f198216906009600052806000209160005b818110610516575095836001959697106104fd575b505050811b016009556102fd565b015160001960f88460031b161c191690553880806104ef565b9192602060018192868b0151815501940192016104da565b6009600052610577907f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af601f840160051c810191602085106104a757601f0160051c0190610936565b386102c7565b601f198216956008600052806000209160005b8881106105cc575083600195969798106105b3575b505050811b016008556102a3565b015160001960f88460031b161c191690553880806105a5565b91926020600181928685015181550194019201610590565b600860005261062d907ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3601f840160051c810191602085106104a757601f0160051c0190610936565b3861026b565b90601f198316916007600052816000209260005b818110610684575090846001959493921061066b575b505050811b01600755610247565b015160001960f88460031b161c1916905538808061065d565b92936020600181928786015181550195019301610647565b60076000526106e5907fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688601f850160051c810191602086106104a757601f0160051c0190610936565b38610212565b90601f198316916004600052816000209260005b81811061073c5750908460019594939210610723575b505050811b016004556101d6565b015160001960f88460031b161c19169055388080610715565b929360206001819287860151815501950193016106ff565b600460005261079d907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f850160051c810191602086106104a757601f0160051c0190610936565b386101a1565b90601f198316916003600052816000209260005b8181106107f457509084600195949392106107db575b505050811b0160035561017c565b015160001960f88460031b161c191690553880806107cd565b929360206001819287860151815501950193016107b7565b6003600052610855907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f850160051c810191602086106104a757601f0160051c0190610936565b38610146565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176104b157604052565b9192916001600160401b0382116104b1576108a9601f8301601f1916602001610860565b938285528282011161085b5760005b8281106108cc575050602060009184010152565b80602091830151828288010152016108b8565b9080601f8301121561085b5781516108f992602001610885565b90565b90600182811c9216801561092c575b602083101461091657565b634e487b7160e01b600052602260045260246000fd5b91607f169161090b565b818110610941575050565b6000815560010161093656fe6080604052600436101561001257600080fd5b60003560e01c806306fdde03146106fc578063095ea7b31461067657806318160ddd1461065857806323b872dd1461056b578063313ce5671461054f5780633257b4f31461053157806370a08231146104f75780638c662b7f1461044f57806395d89b41146103a7578063977fb1cc146102ff578063a9059cbb146102ce578063d5f39488146102a5578063dd62ed3e14610254578063e8a5edce146101ab5763f3ccaac0146100c157600080fd5b346101a65760003660031901126101a65760405160006007546100e381610811565b80845290600181169081156101825750600114610123575b61011f8361010b8185038261084b565b6040519182916020835260208301906107a4565b0390f35b600760009081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688939250905b8082106101685750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610150565b60ff191660208086019190915291151560051b8401909101915061010b90506100fb565b600080fd5b346101a65760003660031901126101a6576040516000600a546101cd81610811565b808452906001811690811561018257506001146101f45761011f8361010b8185038261084b565b919050600a6000527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8916000905b80821061023a5750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610222565b346101a65760403660031901126101a65761026d6107e5565b6102756107fb565b6001600160a01b039182166000908152600160209081526040808320949093168252928352819020549051908152f35b346101a65760003660031901126101a6576005546040516001600160a01b039091168152602090f35b346101a65760403660031901126101a6576102f46102ea6107e5565b6024359033610883565b602060405160018152f35b346101a65760003660031901126101a657604051600060085461032181610811565b808452906001811690811561018257506001146103485761011f8361010b8185038261084b565b600860009081527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3939250905b80821061038d5750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610375565b346101a65760003660031901126101a65760405160006004546103c981610811565b808452906001811690811561018257506001146103f05761011f8361010b8185038261084b565b600460009081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b8082106104355750909150810160200161010b6100fb565b91926001816020925483858801015201910190929161041d565b346101a65760003660031901126101a657604051600060095461047181610811565b808452906001811690811561018257506001146104985761011f8361010b8185038261084b565b600960009081527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af939250905b8082106104dd5750909150810160200161010b6100fb565b9192600181602092548385880101520191019092916104c5565b346101a65760203660031901126101a6576001600160a01b036105186107e5565b1660005260006020526020604060002054604051908152f35b346101a65760003660031901126101a6576020600654604051908152f35b346101a65760003660031901126101a657602060405160128152f35b346101a65760603660031901126101a6576105846107e5565b61058c6107fb565b6001600160a01b03821660008181526001602081815260408084203385529091529091205491936044359392909181016105cc575b506102f49350610883565b83811061063b57841561062557331561060f576102f4946000526001602052604060002060018060a01b03331660005260205283604060002091039055846105c1565b634a1406b160e11b600052600060045260246000fd5b63e602df0560e01b600052600060045260246000fd5b8390637dc7a0d960e11b6000523360045260245260445260646000fd5b346101a65760003660031901126101a6576020600254604051908152f35b346101a65760403660031901126101a65761068f6107e5565b602435903315610625576001600160a01b031690811561060f57336000526001602052604060002082600052602052806040600020556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b346101a65760003660031901126101a657604051600060035461071e81610811565b808452906001811690811561018257506001146107455761011f8361010b8185038261084b565b600360009081527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b939250905b80821061078a5750909150810160200161010b6100fb565b919260018160209254838588010152019101909291610772565b919082519283825260005b8481106107d0575050826000602080949584010152601f8019910116010190565b806020809284010151828286010152016107af565b600435906001600160a01b03821682036101a657565b602435906001600160a01b03821682036101a657565b90600182811c92168015610841575b602083101461082b57565b634e487b7160e01b600052602260045260246000fd5b91607f1691610820565b90601f8019910116810190811067ffffffffffffffff82111761086d57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0316908115610932576001600160a01b031691821561091c5760008281528060205260408120548281106109025791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b916064928463391434e360e21b8452600452602452604452fd5b63ec442f0560e01b600052600060045260246000fd5b634b637e8f60e11b600052600060045260246000fdfea26469706673582212206c58257e9404c1e2494d4f719acb3b836cb49a49d92f4b5610d7074fbe24923364736f6c634300081a0033a264697066735822122041824f4abf3181b8b2a5453c96c8b2b4ad717e33543abb9ddf31ec180a02e07d64736f6c634300081a0033

Block Transaction Gas Used Reward
view all blocks ##produced##

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.