BangerV4

0x00000000cdab5161815cd4005fac11ac3a796f63

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
17,727 bytes
ABI entries
43
17 read · 18 write
License
none

Contract information

Address
0x00000000cdab5161815cd4005fac11ac3a796f63
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x5be10b444f…e82a625ad0

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (17)

bundleFeeSwitch()bool
defaultLockingPeriod()uint64
deprecated()bool
fee()uint24
generateSalt(address, uint256, string, string, string, string, address, string, uint256)bytes32, address
liquidityLocker()address
lpFeesCut()uint8
owner()address
positionManager()address
predictToken(address, uint256, string, string, string, string, address, string, uint256, bytes32)address
protocolCut()uint8
supportsInterface(bytes4)bool
swapRouter()address
taxCollector()address
taxRate()uint8
uniswapV3Factory()address
weth()address

Events (2)

OwnershipTransferredTokenCreated

ABI

[
  {
    "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": "address",
        "name": "_metadataAddress",
        "type": "address"
      },
      {
        "internalType": "string",
        "name": "_extraStringData",
        "type": "string"
      }
    ],
    "name": "deployToken",
    "outputs": [
      {
        "internalType": "contract BangerV4Token",
        "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": "address",
        "name": "metadataAddress",
        "type": "address"
      },
      {
        "internalType": "string",
        "name": "extraStringData",
        "type": "string"
      },
      {
        "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 ILockerFactoryV3",
        "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": "address",
        "name": "metadataAddress",
        "type": "address"
      },
      {
        "internalType": "string",
        "name": "extraStringData",
        "type": "string"
      },
      {
        "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"
  }
]

Source code

// 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 {IInscribedDropsDynamic} from "../IInscribedDropsDynamic.sol";

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

contract BangerV4 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;
    ILockerFactoryV3 public liquidityLocker =
        ILockerFactoryV3(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(0x0000004c6cc2cA10Cf4d67C8902659085D31e1Dc);

    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,
        address _metadataAddress,
        string calldata _extraStringData
    ) external payable returns (BangerV4Token token, uint256 tokenId) {
        if (deprecated) revert Deprecated();

        int24 tickSpacing = uniswapV3Factory.feeAmountTickSpacing(fee);

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

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

        // 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,
            address(positionManager)
        );

        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
            )
        );

        // Create drop
        uint256 dropIdx = IInscribedDropsDynamic(inscribedDropsAddress)
            .totalDrops();
        IInscribedDropsDynamic(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,
            _metadataAddress != address(0)
                ? ""
                : getUri(
                    _name,
                    _symbol,
                    _image,
                    _animation,
                    _extraStringData,
                    address(token)
                ),
            _metadataAddress
        );

        // Transfer the first minted token to _deployer, which supports if a bot deploys tokens on
        // behalf of a user
        IInscribedDropsDynamic(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 predictToken(
        address deployer,
        uint256 fid,
        string calldata name,
        string calldata symbol,
        string calldata image,
        string calldata animation,
        address metadataAddress,
        string calldata extraStringData,
        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(BangerV4Token).creationCode,
                            abi.encode(
                                name,
                                symbol,
                                supply,
                                deployer,
                                fid,
                                image,
                                animation,
                                metadataAddress,
                                extraStringData
                            )
                        )
                    )
                )
            ).fromLast20Bytes();
    }

    function generateSalt(
        address deployer,
        uint256 fid,
        string calldata name,
        string calldata symbol,
        string calldata image,
        string calldata animation,
        address metadataAddress,
        string calldata extraStringData,
        uint256 supply
    ) external view returns (bytes32 salt, address token) {
        for (uint256 i; ; i++) {
            salt = bytes32(i);
            token = predictToken(
                deployer,
                fid,
                name,
                symbol,
                image,
                animation,
                metadataAddress,
                extraStringData,
                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 = ILockerFactoryV3(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 {}

    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
        IInscribedDropsDynamic(readDropsAddress).mint{value: msg.value}(
            dropIdx,
            quantity
        );

        // Transfer NFTs to msg.sender
        IInscribedDropsDynamic(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 -
                (IInscribedDropsDynamic(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) external payable {
        ExactInputSingleParams memory swapParams = ExactInputSingleParams({
            tokenIn: weth, // The token we are exchanging from (ETH wrapped as WETH)
            tokenOut: 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 to receive
            sqrtPriceLimitX96: 0 // No price limit
        });

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

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