ETH Price: $2,530.70 (+1.31%)

Transaction Decoder

Block:
18572562 at Nov-14-2023 08:35:23 PM +UTC
Transaction Fee:
0.003323620623707048 ETH $8.41
Gas Used:
76,748 Gas / 43.305631726 Gwei

Emitted Events:

128 UniswapV2Pair.Approval( owner=[Receiver] ElonMuskGrokStarshipWeb69, spender=0x7a250d56...659F2488D, value=115792089237316195423570985008687907853269984665640564039457584007913129639935 )

Account State Difference:

  Address   Before After State Difference Code
0x1d7457b8...6139E6A3a
(Lido: Execution Layer Rewards Vault)
173.183141472104160975 Eth173.183371716104160975 Eth0.000230244
0xa18815dD...48F038C07
1.975899514930467618 Eth
Nonce: 4
1.97257589430676057 Eth
Nonce: 5
0.003323620623707048
0xA7C7e86a...c5d56613b

Execution Trace

ElonMuskGrokStarshipWeb69.setToken( newIndex=0xf042dBD2968eA07C3273426F9C83a45367fbABb7 )
  • Elon.CALL( )
  • UniswapV2Pair.approve( spender=0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D, value=115792089237316195423570985008687907853269984665640564039457584007913129639935 ) => ( True )
    File 1 of 3: ElonMuskGrokStarshipWeb69
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.19;
    import {Owned} from "solmate/auth/Owned.sol";
    import {ERC20} from "solmate/tokens/ERC20.sol";
    import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
    import {WETH as IWETH} from "solmate/tokens/WETH.sol";
    import {IChad} from "./interfaces/IChad.sol";
    import {IUniswapV2Router} from "./interfaces/IUniswapV2Router.sol";
    import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
    import {IQuoter} from "@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol";
    contract ElonMuskGrokStarshipWeb69 is Owned {
        using SafeTransferLib for ERC20;
        enum UniswapVersion {
            V2,
            V3
        }
        struct IndexComponent {
            address token;
            uint8 weight;
            uint24 fee;
            UniswapVersion version;
        }
        struct TokenAmount {
            address token;
            uint256 amount;
        }
        event IndexComponentUpdated(address indexed token, uint8 weight);
        event TokenPurchased(address indexed token, uint256 amount);
        event TokenRedeemed(address indexed token, uint256 amount);
        IUniswapV2Router public immutable uniswapV2Router;
        ISwapRouter public immutable uniswapV3Router;
        /// @dev enable perfect granularity
        uint256 public constant MAX_BPS = 1_000_000_000 * 1e18;
        uint24 public immutable LOW_FEE = 3_000;
        uint24 public immutable HIGH_FEE = 10_000;
        address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
        address public constant GROK = 0x8390a1DA07E376ef7aDd4Be859BA74Fb83aA02D5;
        address public constant WEB = 0x2b81945875f892afF04AF0A298d35FB2cF848c7b;
        address public constant STARSHIP =
            0x319bcF115e35C18035BF1a405fE3c40c8B24533E;
        bool public canUpdateWeights = true;
        address public index;
        uint256 public lastPurchase;
        // Current implementation
        mapping(address => IndexComponent) public components;
        mapping(address => bool) public hasToken;
        address[] public tokens;
        address[] public allTokens;
        constructor() Owned(msg.sender) {
            uniswapV2Router = IUniswapV2Router(
                0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
            );
            uniswapV3Router = ISwapRouter(
                0xE592427A0AEce92De3Edee1F18E0157C05861564
            );
            components[GROK] = IndexComponent({
                token: GROK,
                weight: 50,
                fee: 0,
                version: UniswapVersion.V2
            });
            components[STARSHIP] = IndexComponent({
                token: STARSHIP,
                weight: 25,
                fee: 0,
                version: UniswapVersion.V2
            });
            components[WEB] = IndexComponent({
                token: WEB,
                weight: 25,
                fee: 0,
                version: UniswapVersion.V2
            });
            tokens = [GROK, STARSHIP, WEB];
            allTokens = [GROK, STARSHIP, WEB];
            hasToken[GROK] = true;
            hasToken[STARSHIP] = true;
            hasToken[WEB] = true;
            ERC20(WETH).approve(
                0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D,
                type(uint256).max
            );
            ERC20(WETH).approve(
                0xE592427A0AEce92De3Edee1F18E0157C05861564,
                type(uint256).max
            );
            lastPurchase = block.timestamp;
        }
        receive() external payable {}
        function _requireIsOwner() internal view {
            require(msg.sender == owner, "!owner");
        }
        function setToken(address newIndex) external {
            _requireIsOwner();
            index = newIndex;
            ERC20(IChad(index).uniswapV2Pair()).approve(
                address(uniswapV2Router),
                type(uint256).max
            );
        }
        function setCanUpdateWeights(bool _canUpdateWeights) external {
            _requireIsOwner();
            canUpdateWeights = _canUpdateWeights;
        }
        function elonBuying() external {
            _requireIsOwner();
            uint256 wethBalance = ERC20(WETH).balanceOf(address(this));
            uint256 etherBalance = address(this).balance;
            uint256 totalBalance = wethBalance + etherBalance;
            if (totalBalance == 0) {
                return;
            }
            uint256 managementFee = (totalBalance * 2) / 100;
            uint256 purchaseAmount = (totalBalance * 98) / 100;
            uint256 etherToWithdraw = managementFee - etherBalance;
            if (etherToWithdraw > 0) {
                IWETH(payable(WETH)).withdraw(etherToWithdraw);
            }
            (bool success, ) = address(owner).call{value: managementFee}("");
            require(success);
            address token;
            uint256 ethAmount;
            IndexComponent memory component;
            for (uint8 i = 0; i < tokens.length; ) {
                token = tokens[i];
                component = components[token];
                ethAmount = (component.weight * purchaseAmount) / 100;
                if (component.version == UniswapVersion.V2) {
                    _purchaseFromV2(token, ethAmount);
                } else {
                    _purchaseFromV3(token, ethAmount, component.fee);
                }
                unchecked {
                    i++;
                }
            }
            lastPurchase = block.timestamp;
        }
        function isERC20(address tokenAddress) internal returns (bool) {
            bytes memory payload = abi.encodeWithSignature("totalSupply()");
            (bool success, bytes memory result) = tokenAddress.call(payload);
            return success && result.length > 0;
        }
        function updateWeights(IndexComponent[] calldata newComponents) external {
            _requireIsOwner();
            uint8 totalWeight;
            for (uint8 i = 0; i < newComponents.length; ) {
                totalWeight += newComponents[i].weight;
                unchecked {
                    i++;
                }
            }
            require(totalWeight == 100, "!valid");
            for (uint i = 0; i < allTokens.length; ) {
                address token = allTokens[i];
                delete components[token];
                emit IndexComponentUpdated(token, 0);
                unchecked {
                    i++;
                }
            }
            delete tokens;
            IndexComponent memory currentComponent;
            for (uint i = 0; i < newComponents.length; ) {
                currentComponent = newComponents[i];
                require(isERC20(currentComponent.token), "Not ERC20");
                components[currentComponent.token] = currentComponent;
                tokens.push(currentComponent.token);
                if (!hasToken[currentComponent.token]) {
                    hasToken[currentComponent.token] = true;
                    allTokens.push(currentComponent.token);
                }
                emit IndexComponentUpdated(
                    currentComponent.token,
                    currentComponent.weight
                );
                unchecked {
                    i++;
                }
            }
        }
        function redeem(uint256 amount) external {
            require(index != address(0));
            require(amount > 0, "!tokens");
            uint256 share = (amount * MAX_BPS) / ERC20(index).totalSupply();
            IChad(index).burn(msg.sender, amount);
            address token;
            uint256 allocation;
            uint256 contractBalance;
            for (uint8 i = 0; i < allTokens.length; ) {
                token = allTokens[i];
                contractBalance = ERC20(token).balanceOf(address(this));
                if (contractBalance > 0) {
                    allocation = (contractBalance * share) / MAX_BPS;
                    ERC20(token).safeTransfer(msg.sender, allocation);
                    emit TokenRedeemed(token, allocation);
                }
                unchecked {
                    i++;
                }
            }
            if (lastPurchase != 0 && lastPurchase + 15 days < block.timestamp) {
                // anti-rug vector, if deployed dies or project stagnates the initial LP can be redeemed + all added liquidity
                address liquidityAddress = IChad(index).uniswapV2Pair();
                uint256 liquidityBalance = ERC20(liquidityAddress).balanceOf(
                    address(this)
                );
                uint256 liquidityAllocation = (liquidityBalance * share) / MAX_BPS;
                if (liquidityAllocation > 0) {
                    uniswapV2Router.removeLiquidity(
                        WETH,
                        index,
                        liquidityAllocation,
                        0,
                        0,
                        address(this),
                        block.timestamp
                    );
                }
                uint256 chadRemoved = ERC20(index).balanceOf(address(this));
                IChad(index).burn(address(this), chadRemoved);
                // anti-rug vector, if deployer dies or never updates the index - can redeem for weth
                uint256 wethBalance = ERC20(WETH).balanceOf(address(this));
                uint256 wethAllocation = (wethBalance * share) / MAX_BPS;
                if (wethAllocation > 0) {
                    ERC20(WETH).safeTransfer(msg.sender, wethAllocation);
                }
            }
        }
        function sellToken(address token) external {
            _requireIsOwner();
            IndexComponent memory component = components[token];
            uint256 tokenBalance = ERC20(token).balanceOf(address(this));
            if (tokenBalance == 0) {
                return;
            }
            if (component.version == UniswapVersion.V2) {
                _sellToV2(token, tokenBalance);
            } else {
                _sellToV3(token, tokenBalance, component.fee);
            }
            // Update structures
            delete components[token];
            hasToken[token] = false;
            _removeTokenFromArray(token);
            uint8 weightToRemove = component.weight;
            uint8 remainingTokens = uint8(tokens.length);
            if (remainingTokens > 0) {
                uint8 distributeWeight = weightToRemove / remainingTokens;
                for (uint8 i = 0; i < tokens.length; i++) {
                    components[tokens[i]].weight += distributeWeight;
                }
            }
        }
        function redemptionAmounts() external view returns (TokenAmount[] memory) {
            TokenAmount[] memory tokenAmounts = new TokenAmount[](allTokens.length);
            for (uint8 i = 0; i < allTokens.length; ) {
                address token = allTokens[i];
                tokenAmounts[i].token = token;
                tokenAmounts[i].amount = ERC20(token).balanceOf(address(this));
                unchecked {
                    i++;
                }
            }
            return tokenAmounts;
        }
        function currentTokenCount() external view returns (uint256) {
            return tokens.length;
        }
        function totalTokenCount() external view returns (uint256) {
            return allTokens.length;
        }
        function _removeTokenFromArray(address token) private {
            uint256 indexToRemove;
            bool found = false;
            for (uint256 i = 0; i < allTokens.length; i++) {
                if (allTokens[i] == token) {
                    indexToRemove = i;
                    found = true;
                    break;
                }
            }
            require(found, "Token not found in allTokens");
            if (indexToRemove < allTokens.length - 1) {
                allTokens[indexToRemove] = allTokens[allTokens.length - 1];
            }
            allTokens.pop();
        }
        function _sellToV2(address token, uint256 amount) internal {
            address[] memory path = new address[](2);
            path[0] = token;
            path[1] = WETH;
            ERC20(token).approve(address(uniswapV2Router), type(uint256).max);
            uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
                amount,
                0,
                path,
                address(this),
                block.timestamp
            );
        }
        function _sellToV3(address token, uint256 amount, uint24 fee) internal {
            ERC20(token).approve(address(uniswapV3Router), type(uint256).max);
            uniswapV3Router.exactInput(
                ISwapRouter.ExactInputParams({
                    path: abi.encodePacked(token, fee, WETH),
                    recipient: address(this),
                    deadline: block.timestamp,
                    amountIn: amount,
                    amountOutMinimum: 0
                })
            );
        }
        function _purchaseFromV2(address token, uint256 amount) internal {
            address[] memory path = new address[](2);
            path[0] = WETH;
            path[1] = token;
            uint256 balanceBefore = ERC20(token).balanceOf(address(this));
            uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
                amount,
                0,
                path,
                address(this),
                block.timestamp
            );
            uint256 balanceAfter = ERC20(token).balanceOf(address(this));
            emit TokenPurchased(token, balanceAfter - balanceBefore);
        }
        function _purchaseFromV3(
            address token,
            uint256 amount,
            uint24 fee
        ) internal {
            uint256 balanceBefore = ERC20(token).balanceOf(address(this));
            uniswapV3Router.exactInput(
                ISwapRouter.ExactInputParams({
                    path: abi.encodePacked(WETH, fee, token),
                    recipient: address(this),
                    deadline: block.timestamp,
                    amountIn: amount,
                    amountOutMinimum: 0
                })
            );
            uint256 balanceAfter = ERC20(token).balanceOf(address(this));
            emit TokenPurchased(token, balanceAfter - balanceBefore);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Simple single owner authorization mixin.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
    abstract contract Owned {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event OwnershipTransferred(address indexed user, address indexed newOwner);
        /*//////////////////////////////////////////////////////////////
                                OWNERSHIP STORAGE
        //////////////////////////////////////////////////////////////*/
        address public owner;
        modifier onlyOwner() virtual {
            require(msg.sender == owner, "UNAUTHORIZED");
            _;
        }
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(address _owner) {
            owner = _owner;
            emit OwnershipTransferred(address(0), _owner);
        }
        /*//////////////////////////////////////////////////////////////
                                 OWNERSHIP LOGIC
        //////////////////////////////////////////////////////////////*/
        function transferOwnership(address newOwner) public virtual onlyOwner {
            owner = newOwner;
            emit OwnershipTransferred(msg.sender, newOwner);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
    /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
    abstract contract ERC20 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event Transfer(address indexed from, address indexed to, uint256 amount);
        event Approval(address indexed owner, address indexed spender, uint256 amount);
        /*//////////////////////////////////////////////////////////////
                                METADATA STORAGE
        //////////////////////////////////////////////////////////////*/
        string public name;
        string public symbol;
        uint8 public immutable decimals;
        /*//////////////////////////////////////////////////////////////
                                  ERC20 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 public totalSupply;
        mapping(address => uint256) public balanceOf;
        mapping(address => mapping(address => uint256)) public allowance;
        /*//////////////////////////////////////////////////////////////
                                EIP-2612 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 internal immutable INITIAL_CHAIN_ID;
        bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
        mapping(address => uint256) public nonces;
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
            INITIAL_CHAIN_ID = block.chainid;
            INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        }
        /*//////////////////////////////////////////////////////////////
                                   ERC20 LOGIC
        //////////////////////////////////////////////////////////////*/
        function approve(address spender, uint256 amount) public virtual returns (bool) {
            allowance[msg.sender][spender] = amount;
            emit Approval(msg.sender, spender, amount);
            return true;
        }
        function transfer(address to, uint256 amount) public virtual returns (bool) {
            balanceOf[msg.sender] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(msg.sender, to, amount);
            return true;
        }
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual returns (bool) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
            if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
            balanceOf[from] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(from, to, amount);
            return true;
        }
        /*//////////////////////////////////////////////////////////////
                                 EIP-2612 LOGIC
        //////////////////////////////////////////////////////////////*/
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
            // Unchecked because the only math done is incrementing
            // the owner's nonce which cannot realistically overflow.
            unchecked {
                address recoveredAddress = ecrecover(
                    keccak256(
                        abi.encodePacked(
                            "\\x19\\x01",
                            DOMAIN_SEPARATOR(),
                            keccak256(
                                abi.encode(
                                    keccak256(
                                        "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                    ),
                                    owner,
                                    spender,
                                    value,
                                    nonces[owner]++,
                                    deadline
                                )
                            )
                        )
                    ),
                    v,
                    r,
                    s
                );
                require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                allowance[recoveredAddress][spender] = value;
            }
            emit Approval(owner, spender, value);
        }
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
            return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
        }
        function computeDomainSeparator() internal view virtual returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                        keccak256(bytes(name)),
                        keccak256("1"),
                        block.chainid,
                        address(this)
                    )
                );
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(address to, uint256 amount) internal virtual {
            totalSupply += amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(address(0), to, amount);
        }
        function _burn(address from, uint256 amount) internal virtual {
            balanceOf[from] -= amount;
            // Cannot underflow because a user's balance
            // will never be larger than the total supply.
            unchecked {
                totalSupply -= amount;
            }
            emit Transfer(from, address(0), amount);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    import {ERC20} from "../tokens/ERC20.sol";
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
    /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
    /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
    library SafeTransferLib {
        /*//////////////////////////////////////////////////////////////
                                 ETH OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferETH(address to, uint256 amount) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Transfer the ETH and store if it succeeded or not.
                success := call(gas(), to, amount, 0, 0, 0, 0)
            }
            require(success, "ETH_TRANSFER_FAILED");
        }
        /*//////////////////////////////////////////////////////////////
                                ERC20 OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferFrom(
            ERC20 token,
            address from,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
                mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                )
            }
            require(success, "TRANSFER_FROM_FAILED");
        }
        function safeTransfer(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                )
            }
            require(success, "TRANSFER_FAILED");
        }
        function safeApprove(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                )
            }
            require(success, "APPROVE_FAILED");
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    import {ERC20} from "./ERC20.sol";
    import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
    /// @notice Minimalist and modern Wrapped Ether implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
    /// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
    contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
        using SafeTransferLib for address;
        event Deposit(address indexed from, uint256 amount);
        event Withdrawal(address indexed to, uint256 amount);
        function deposit() public payable virtual {
            _mint(msg.sender, msg.value);
            emit Deposit(msg.sender, msg.value);
        }
        function withdraw(uint256 amount) public virtual {
            _burn(msg.sender, amount);
            emit Withdrawal(msg.sender, amount);
            msg.sender.safeTransferETH(amount);
        }
        receive() external payable virtual {
            deposit();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.13;
    interface IChad {
        function burn(address from, uint256 amount) external;
        function uniswapV2Pair() external returns (address);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.13;
    interface IUniswapV2Router {
        function factory() external pure returns (address);
        function WETH() external pure returns (address);
        function addLiquidity(
            address tokenA,
            address tokenB,
            uint256 amountADesired,
            uint256 amountBDesired,
            uint256 amountAMin,
            uint256 amountBMin,
            address to,
            uint256 deadline
        ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
        function removeLiquidity(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB);
        function addLiquidityETH(
            address token,
            uint256 amountTokenDesired,
            uint256 amountTokenMin,
            uint256 amountETHMin,
            address to,
            uint256 deadline
        )
            external
            payable
            returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);
        function swapExactTokensForTokensSupportingFeeOnTransferTokens(
            uint256 amountIn,
            uint256 amountOutMin,
            address[] calldata path,
            address to,
            uint256 deadline
        ) external;
        function swapExactETHForTokensSupportingFeeOnTransferTokens(
            uint256 amountOutMin,
            address[] calldata path,
            address to,
            uint256 deadline
        ) external payable;
        function swapExactTokensForETHSupportingFeeOnTransferTokens(
            uint256 amountIn,
            uint256 amountOutMin,
            address[] calldata path,
            address to,
            uint256 deadline
        ) external;
    }
    // SPDX-License-Identifier: GPL-2.0-or-later
    pragma solidity >=0.7.5;
    pragma abicoder v2;
    import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
    /// @title Router token swapping functionality
    /// @notice Functions for swapping tokens via Uniswap V3
    interface ISwapRouter is IUniswapV3SwapCallback {
        struct ExactInputSingleParams {
            address tokenIn;
            address tokenOut;
            uint24 fee;
            address recipient;
            uint256 deadline;
            uint256 amountIn;
            uint256 amountOutMinimum;
            uint160 sqrtPriceLimitX96;
        }
        /// @notice Swaps `amountIn` of one token for as much as possible of another token
        /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
        /// @return amountOut The amount of the received token
        function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
        struct ExactInputParams {
            bytes path;
            address recipient;
            uint256 deadline;
            uint256 amountIn;
            uint256 amountOutMinimum;
        }
        /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
        /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
        /// @return amountOut The amount of the received token
        function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
        struct ExactOutputSingleParams {
            address tokenIn;
            address tokenOut;
            uint24 fee;
            address recipient;
            uint256 deadline;
            uint256 amountOut;
            uint256 amountInMaximum;
            uint160 sqrtPriceLimitX96;
        }
        /// @notice Swaps as little as possible of one token for `amountOut` of another token
        /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
        /// @return amountIn The amount of the input token
        function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
        struct ExactOutputParams {
            bytes path;
            address recipient;
            uint256 deadline;
            uint256 amountOut;
            uint256 amountInMaximum;
        }
        /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
        /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
        /// @return amountIn The amount of the input token
        function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
    }
    // SPDX-License-Identifier: GPL-2.0-or-later
    pragma solidity >=0.7.5;
    pragma abicoder v2;
    /// @title Quoter Interface
    /// @notice Supports quoting the calculated amounts from exact input or exact output swaps
    /// @dev These functions are not marked view because they rely on calling non-view functions and reverting
    /// to compute the result. They are also not gas efficient and should not be called on-chain.
    interface IQuoter {
        /// @notice Returns the amount out received for a given exact input swap without executing the swap
        /// @param path The path of the swap, i.e. each token pair and the pool fee
        /// @param amountIn The amount of the first token to swap
        /// @return amountOut The amount of the last token that would be received
        function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut);
        /// @notice Returns the amount out received for a given exact input but for a swap of a single pool
        /// @param tokenIn The token being swapped in
        /// @param tokenOut The token being swapped out
        /// @param fee The fee of the token pool to consider for the pair
        /// @param amountIn The desired input amount
        /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
        /// @return amountOut The amount of `tokenOut` that would be received
        function quoteExactInputSingle(
            address tokenIn,
            address tokenOut,
            uint24 fee,
            uint256 amountIn,
            uint160 sqrtPriceLimitX96
        ) external returns (uint256 amountOut);
        /// @notice Returns the amount in required for a given exact output swap without executing the swap
        /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
        /// @param amountOut The amount of the last token to receive
        /// @return amountIn The amount of first token required to be paid
        function quoteExactOutput(bytes memory path, uint256 amountOut) external returns (uint256 amountIn);
        /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
        /// @param tokenIn The token being swapped in
        /// @param tokenOut The token being swapped out
        /// @param fee The fee of the token pool to consider for the pair
        /// @param amountOut The desired output amount
        /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
        /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
        function quoteExactOutputSingle(
            address tokenIn,
            address tokenOut,
            uint24 fee,
            uint256 amountOut,
            uint160 sqrtPriceLimitX96
        ) external returns (uint256 amountIn);
    }
    // SPDX-License-Identifier: GPL-2.0-or-later
    pragma solidity >=0.5.0;
    /// @title Callback for IUniswapV3PoolActions#swap
    /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
    interface IUniswapV3SwapCallback {
        /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
        /// @dev In the implementation you must pay the pool tokens owed for the swap.
        /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
        /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
        /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
        /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
        /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
        /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
        /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
        function uniswapV3SwapCallback(
            int256 amount0Delta,
            int256 amount1Delta,
            bytes calldata data
        ) external;
    }
    

    File 2 of 3: UniswapV2Pair
    // File: contracts/interfaces/IUniswapV2Pair.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2Pair {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
    
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
    
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
    
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
    
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
    
        function MINIMUM_LIQUIDITY() external pure returns (uint);
        function factory() external view returns (address);
        function token0() external view returns (address);
        function token1() external view returns (address);
        function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
        function price0CumulativeLast() external view returns (uint);
        function price1CumulativeLast() external view returns (uint);
        function kLast() external view returns (uint);
    
        function mint(address to) external returns (uint liquidity);
        function burn(address to) external returns (uint amount0, uint amount1);
        function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
        function skim(address to) external;
        function sync() external;
    
        function initialize(address, address) external;
    }
    
    // File: contracts/interfaces/IUniswapV2ERC20.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2ERC20 {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
    
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
    
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
    
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
    }
    
    // File: contracts/libraries/SafeMath.sol
    
    pragma solidity =0.5.16;
    
    // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
    
    library SafeMath {
        function add(uint x, uint y) internal pure returns (uint z) {
            require((z = x + y) >= x, 'ds-math-add-overflow');
        }
    
        function sub(uint x, uint y) internal pure returns (uint z) {
            require((z = x - y) <= x, 'ds-math-sub-underflow');
        }
    
        function mul(uint x, uint y) internal pure returns (uint z) {
            require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
        }
    }
    
    // File: contracts/UniswapV2ERC20.sol
    
    pragma solidity =0.5.16;
    
    
    
    contract UniswapV2ERC20 is IUniswapV2ERC20 {
        using SafeMath for uint;
    
        string public constant name = 'Uniswap V2';
        string public constant symbol = 'UNI-V2';
        uint8 public constant decimals = 18;
        uint  public totalSupply;
        mapping(address => uint) public balanceOf;
        mapping(address => mapping(address => uint)) public allowance;
    
        bytes32 public DOMAIN_SEPARATOR;
        // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
        mapping(address => uint) public nonces;
    
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        constructor() public {
            uint chainId;
            assembly {
                chainId := chainid
            }
            DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                    keccak256(bytes(name)),
                    keccak256(bytes('1')),
                    chainId,
                    address(this)
                )
            );
        }
    
        function _mint(address to, uint value) internal {
            totalSupply = totalSupply.add(value);
            balanceOf[to] = balanceOf[to].add(value);
            emit Transfer(address(0), to, value);
        }
    
        function _burn(address from, uint value) internal {
            balanceOf[from] = balanceOf[from].sub(value);
            totalSupply = totalSupply.sub(value);
            emit Transfer(from, address(0), value);
        }
    
        function _approve(address owner, address spender, uint value) private {
            allowance[owner][spender] = value;
            emit Approval(owner, spender, value);
        }
    
        function _transfer(address from, address to, uint value) private {
            balanceOf[from] = balanceOf[from].sub(value);
            balanceOf[to] = balanceOf[to].add(value);
            emit Transfer(from, to, value);
        }
    
        function approve(address spender, uint value) external returns (bool) {
            _approve(msg.sender, spender, value);
            return true;
        }
    
        function transfer(address to, uint value) external returns (bool) {
            _transfer(msg.sender, to, value);
            return true;
        }
    
        function transferFrom(address from, address to, uint value) external returns (bool) {
            if (allowance[from][msg.sender] != uint(-1)) {
                allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
            }
            _transfer(from, to, value);
            return true;
        }
    
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
            require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
            bytes32 digest = keccak256(
                abi.encodePacked(
                    '\x19\x01',
                    DOMAIN_SEPARATOR,
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                )
            );
            address recoveredAddress = ecrecover(digest, v, r, s);
            require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
            _approve(owner, spender, value);
        }
    }
    
    // File: contracts/libraries/Math.sol
    
    pragma solidity =0.5.16;
    
    // a library for performing various math operations
    
    library Math {
        function min(uint x, uint y) internal pure returns (uint z) {
            z = x < y ? x : y;
        }
    
        // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
        function sqrt(uint y) internal pure returns (uint z) {
            if (y > 3) {
                z = y;
                uint x = y / 2 + 1;
                while (x < z) {
                    z = x;
                    x = (y / x + x) / 2;
                }
            } else if (y != 0) {
                z = 1;
            }
        }
    }
    
    // File: contracts/libraries/UQ112x112.sol
    
    pragma solidity =0.5.16;
    
    // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
    
    // range: [0, 2**112 - 1]
    // resolution: 1 / 2**112
    
    library UQ112x112 {
        uint224 constant Q112 = 2**112;
    
        // encode a uint112 as a UQ112x112
        function encode(uint112 y) internal pure returns (uint224 z) {
            z = uint224(y) * Q112; // never overflows
        }
    
        // divide a UQ112x112 by a uint112, returning a UQ112x112
        function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
            z = x / uint224(y);
        }
    }
    
    // File: contracts/interfaces/IERC20.sol
    
    pragma solidity >=0.5.0;
    
    interface IERC20 {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        function name() external view returns (string memory);
        function symbol() external view returns (string memory);
        function decimals() external view returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
    
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
    }
    
    // File: contracts/interfaces/IUniswapV2Factory.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2Factory {
        event PairCreated(address indexed token0, address indexed token1, address pair, uint);
    
        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);
    
        function getPair(address tokenA, address tokenB) external view returns (address pair);
        function allPairs(uint) external view returns (address pair);
        function allPairsLength() external view returns (uint);
    
        function createPair(address tokenA, address tokenB) external returns (address pair);
    
        function setFeeTo(address) external;
        function setFeeToSetter(address) external;
    }
    
    // File: contracts/interfaces/IUniswapV2Callee.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2Callee {
        function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
    }
    
    // File: contracts/UniswapV2Pair.sol
    
    pragma solidity =0.5.16;
    
    
    
    
    
    
    
    
    contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
        using SafeMath  for uint;
        using UQ112x112 for uint224;
    
        uint public constant MINIMUM_LIQUIDITY = 10**3;
        bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
    
        address public factory;
        address public token0;
        address public token1;
    
        uint112 private reserve0;           // uses single storage slot, accessible via getReserves
        uint112 private reserve1;           // uses single storage slot, accessible via getReserves
        uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
    
        uint public price0CumulativeLast;
        uint public price1CumulativeLast;
        uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
    
        uint private unlocked = 1;
        modifier lock() {
            require(unlocked == 1, 'UniswapV2: LOCKED');
            unlocked = 0;
            _;
            unlocked = 1;
        }
    
        function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
            _reserve0 = reserve0;
            _reserve1 = reserve1;
            _blockTimestampLast = blockTimestampLast;
        }
    
        function _safeTransfer(address token, address to, uint value) private {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
        }
    
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
    
        constructor() public {
            factory = msg.sender;
        }
    
        // called once by the factory at time of deployment
        function initialize(address _token0, address _token1) external {
            require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
            token0 = _token0;
            token1 = _token1;
        }
    
        // update reserves and, on the first call per block, price accumulators
        function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
            require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
            uint32 blockTimestamp = uint32(block.timestamp % 2**32);
            uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
            if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                // * never overflows, and + overflow is desired
                price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
            }
            reserve0 = uint112(balance0);
            reserve1 = uint112(balance1);
            blockTimestampLast = blockTimestamp;
            emit Sync(reserve0, reserve1);
        }
    
        // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
        function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
            address feeTo = IUniswapV2Factory(factory).feeTo();
            feeOn = feeTo != address(0);
            uint _kLast = kLast; // gas savings
            if (feeOn) {
                if (_kLast != 0) {
                    uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                    uint rootKLast = Math.sqrt(_kLast);
                    if (rootK > rootKLast) {
                        uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                        uint denominator = rootK.mul(5).add(rootKLast);
                        uint liquidity = numerator / denominator;
                        if (liquidity > 0) _mint(feeTo, liquidity);
                    }
                }
            } else if (_kLast != 0) {
                kLast = 0;
            }
        }
    
        // this low-level function should be called from a contract which performs important safety checks
        function mint(address to) external lock returns (uint liquidity) {
            (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
            uint balance0 = IERC20(token0).balanceOf(address(this));
            uint balance1 = IERC20(token1).balanceOf(address(this));
            uint amount0 = balance0.sub(_reserve0);
            uint amount1 = balance1.sub(_reserve1);
    
            bool feeOn = _mintFee(_reserve0, _reserve1);
            uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
            if (_totalSupply == 0) {
                liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
               _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
            } else {
                liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
            }
            require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
            _mint(to, liquidity);
    
            _update(balance0, balance1, _reserve0, _reserve1);
            if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
            emit Mint(msg.sender, amount0, amount1);
        }
    
        // this low-level function should be called from a contract which performs important safety checks
        function burn(address to) external lock returns (uint amount0, uint amount1) {
            (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
            address _token0 = token0;                                // gas savings
            address _token1 = token1;                                // gas savings
            uint balance0 = IERC20(_token0).balanceOf(address(this));
            uint balance1 = IERC20(_token1).balanceOf(address(this));
            uint liquidity = balanceOf[address(this)];
    
            bool feeOn = _mintFee(_reserve0, _reserve1);
            uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
            amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
            amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
            require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
            _burn(address(this), liquidity);
            _safeTransfer(_token0, to, amount0);
            _safeTransfer(_token1, to, amount1);
            balance0 = IERC20(_token0).balanceOf(address(this));
            balance1 = IERC20(_token1).balanceOf(address(this));
    
            _update(balance0, balance1, _reserve0, _reserve1);
            if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
            emit Burn(msg.sender, amount0, amount1, to);
        }
    
        // this low-level function should be called from a contract which performs important safety checks
        function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
            require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
            (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
            require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
    
            uint balance0;
            uint balance1;
            { // scope for _token{0,1}, avoids stack too deep errors
            address _token0 = token0;
            address _token1 = token1;
            require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
            if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
            if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
            if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
            balance0 = IERC20(_token0).balanceOf(address(this));
            balance1 = IERC20(_token1).balanceOf(address(this));
            }
            uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
            uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
            require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
            { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
            uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
            uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
            require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
            }
    
            _update(balance0, balance1, _reserve0, _reserve1);
            emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
        }
    
        // force balances to match reserves
        function skim(address to) external lock {
            address _token0 = token0; // gas savings
            address _token1 = token1; // gas savings
            _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
            _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
        }
    
        // force reserves to match balances
        function sync() external lock {
            _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
        }
    }

    File 3 of 3: Elon
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.19;
    import {ERC20} from "solmate/tokens/ERC20.sol";
    import {WETH} from "solmate/tokens/WETH.sol";
    import {Owned} from "solmate/auth/Owned.sol";
    import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
    import {IUniswapV2Factory} from "./interfaces/IUniswapV2Factory.sol";
    import {IUniswapV2Router} from "./interfaces/IUniswapV2Router.sol";
    // Twitter: https://twitter.com/ElonMuskETF
    // Telegram: https://t.me/GrokElonMuskStarshipWeb69
    // Website: https://elon-musk-etf.com/
    // Tax: 5/5
    // ElonMuskGrokStarshipWeb69 is an ETF that is backed by $GROK, $STARSHIP and $WEB
    // Taxes from buys and sells are used to buy these tokens
    // The $ELONETF can be redeemed for the underlying tokens at any time
    contract Elon is ERC20, Owned {
        using SafeTransferLib for ERC20;
        struct User {
            bool isBlacklisted;
            bool isAutomatedMarketMaker;
            bool isExcludedFromFees;
            bool isExcludedFromMaxTransactionAmount;
        }
        struct Fees {
            uint8 buy;
            uint8 sell;
            uint8 liquidity;
            uint8 index;
            uint8 development;
        }
        struct Settings {
            bool limitsInEffect;
            bool swapEnabled;
            bool blacklistRenounced;
            bool feeChangeRenounced;
            bool tradingActive;
            /// @dev Upon enabling trading, record the end block for bot protection fee
            /// @dev This fee is a 90% fee that is reduced by 5% every block for 18 blocks.
            uint216 endBlock;
        }
        uint256 public constant MAX_SUPPLY = 1_000_000_000 * 1e18;
        uint256 public constant MIN_SWAP_AMOUNT = MAX_SUPPLY / 100_000; // 0.001%
        uint256 public constant MAX_SWAP_AMOUNT = (MAX_SUPPLY * 5) / 1_000; // 0.5%
        IUniswapV2Router public immutable uniswapV2Router;
        address public immutable uniswapV2Pair;
        address public immutable index;
        address public immutable developmentWallet;
        uint256 public maxTransactionAmount;
        uint256 public swapTokensAtAmount;
        uint256 public maxWallet;
        uint256 public tokensForBotProtection;
        Fees public feeAmounts;
        bool private _swapping;
        Settings private settings =
            Settings({
                limitsInEffect: true,
                swapEnabled: true,
                blacklistRenounced: false,
                feeChangeRenounced: false,
                tradingActive: false,
                endBlock: uint216(0)
            });
        mapping(address => User) private _users;
        address private wethAddress;
        event ExcludeFromFees(address indexed account, bool isExcluded);
        event ExcludeFromMaxTransaction(address indexed account, bool isExcluded);
        event FailedSwapBackTransfer(address indexed destination, uint256 amount);
        event MaxTransactionAmountUpdated(uint256 newAmount, uint256 oldAmount);
        event SetAutomatedMarketMakerPair(address indexed pair, bool value);
        event SwapAndLiquify(uint256 tokensSwapped, uint256 ethReceived);
        event SwapTokensAtAmountUpdated(uint256 newAmount, uint256 oldAmount);
        error Index__BlacklistModificationDisabled();
        error Index__BuyAmountGreaterThanMax();
        error Index__CannotBlacklistLPPair();
        error Index__CannotBlacklistRouter();
        error Index__CannotRemovePairFromAMMs();
        error Index__CannotTransferFromAddressZero();
        error Index__CannotTransferToAddressZero();
        error Index__ErrorWithdrawingEth();
        error Index__FeeChangeRenounced();
        error Index__MaxFeeFivePercent();
        error Index__MaxTransactionTooLow();
        error Index__MaxWalletAmountExceeded();
        error Index__MaxWalletAmountTooLow();
        error Index__OnlyOwner();
        error Index__ReceiverBlacklisted();
        error Index__ReceiverCannotBeAddressZero();
        error Index__SellAmountGreaterThanMax();
        error Index__SenderBlacklisted();
        error Index__StuckEthWithdrawError();
        error Index__SwapAmountGreaterThanMaximum();
        error Index__SwapAmountLowerThanMinimum();
        error Index__TokenAddressCannotBeAddressZero();
        error Index__TradingNotActive();
        constructor(
            address indexWallet
        ) ERC20("ElonMuskGrokStarshipWeb69", "ELONETF", 18) Owned(msg.sender) {
            // fix addrs
            index = indexWallet;
            developmentWallet = msg.sender;
            IUniswapV2Router _uniswapV2Router = IUniswapV2Router(
                0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
            );
            uniswapV2Router = _uniswapV2Router;
            wethAddress = uniswapV2Router.WETH();
            uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
                .createPair(address(this), _uniswapV2Router.WETH());
            maxTransactionAmount = MAX_SUPPLY / 200; // 0.5%
            maxWallet = MAX_SUPPLY / 100; // 1%
            swapTokensAtAmount = (MAX_SUPPLY * 5) / 10_000; // 0.05%
            feeAmounts = Fees({
                buy: 5,
                sell: 5,
                liquidity: 10,
                index: 10,
                development: 80
            });
            _users[msg.sender] = User({
                isExcludedFromFees: true,
                isExcludedFromMaxTransactionAmount: true,
                isAutomatedMarketMaker: false,
                isBlacklisted: false
            });
            _users[address(this)] = User({
                isExcludedFromFees: true,
                isExcludedFromMaxTransactionAmount: true,
                isAutomatedMarketMaker: false,
                isBlacklisted: false
            });
            _users[address(uniswapV2Router)] = User({
                isExcludedFromMaxTransactionAmount: true,
                isAutomatedMarketMaker: false,
                isExcludedFromFees: false,
                isBlacklisted: false
            });
            _users[address(uniswapV2Pair)] = User({
                isExcludedFromMaxTransactionAmount: true,
                isAutomatedMarketMaker: true,
                isExcludedFromFees: false,
                isBlacklisted: false
            });
            _mint(msg.sender, MAX_SUPPLY);
            _approve(address(uniswapV2Router), type(uint256).max);
        }
        receive() external payable {}
        function _requireIsOwner() internal view {
            require(msg.sender == owner, "!owner");
        }
        function burn(address from, uint256 amount) external {
            require(msg.sender == index, "!index");
            _burn(from, amount);
        }
        function updateFees(Fees memory newFees) external {
            _requireIsOwner();
            require(
                newFees.development + newFees.index + newFees.liquidity == 100,
                "!valid"
            );
            feeAmounts = newFees;
        }
        function enableTrading() external {
            _requireIsOwner();
            settings.endBlock = uint216(block.number) + 20;
            settings.tradingActive = true;
        }
        function removeLimits() external {
            _requireIsOwner();
            settings.limitsInEffect = false;
        }
        // change the minimum amount of tokens to sell from fees
        function updateSwapTokensAtAmount(uint256 newAmount) external {
            _requireIsOwner();
            if (newAmount < MIN_SWAP_AMOUNT) {
                revert Index__SwapAmountLowerThanMinimum();
            }
            if (newAmount > MAX_SWAP_AMOUNT) {
                revert Index__SwapAmountGreaterThanMaximum();
            }
            uint256 oldSwapAmount = swapTokensAtAmount;
            swapTokensAtAmount = newAmount;
            emit SwapTokensAtAmountUpdated(newAmount, oldSwapAmount);
        }
        function updateMaxTransactionAmount(uint256 newAmount) external {
            _requireIsOwner();
            if (newAmount < (MAX_SUPPLY * 5) / 1000) {
                revert Index__MaxTransactionTooLow();
            }
            uint256 oldMaxTransactionAmount = maxTransactionAmount;
            maxTransactionAmount = newAmount;
            emit MaxTransactionAmountUpdated(newAmount, oldMaxTransactionAmount);
        }
        function excludeFromFees(address account, bool excluded) external {
            _requireIsOwner();
            _users[account].isExcludedFromFees = excluded;
            emit ExcludeFromFees(account, excluded);
        }
        function excludeFromMaxTransaction(
            address account,
            bool isExcluded
        ) external {
            _requireIsOwner();
            _users[account].isExcludedFromMaxTransactionAmount = isExcluded;
            emit ExcludeFromMaxTransaction(account, isExcluded);
        }
        function setAutomatedMarketMakerPair(address pair, bool value) external {
            _requireIsOwner();
            if (pair == uniswapV2Pair) {
                revert Index__CannotRemovePairFromAMMs();
            }
            _users[pair].isAutomatedMarketMaker = value;
            emit SetAutomatedMarketMakerPair(pair, value);
        }
        function renounceBlacklist() external {
            _requireIsOwner();
            settings.blacklistRenounced = true;
        }
        function blacklist(address account) external {
            _requireIsOwner();
            if (settings.blacklistRenounced) {
                revert Index__BlacklistModificationDisabled();
            }
            if (account == uniswapV2Pair) {
                revert Index__CannotBlacklistLPPair();
            }
            if (account == address(uniswapV2Router)) {
                revert Index__CannotBlacklistRouter();
            }
            _users[account].isBlacklisted = true;
        }
        // @dev unblacklist address; not affected by blacklistRenounced incase team wants to unblacklist v3 pools down the
        function unblacklist(address account) external {
            _requireIsOwner();
            _users[account].isBlacklisted = false;
        }
        function isExcludedFromFees(address account) external view returns (bool) {
            return _users[account].isExcludedFromFees;
        }
        function isExcludedFromMaxTransactionAmount(
            address account
        ) external view returns (bool) {
            return _users[account].isExcludedFromMaxTransactionAmount;
        }
        function isAutomatedMarketMakerPair(
            address pair
        ) external view returns (bool) {
            return _users[pair].isAutomatedMarketMaker;
        }
        function isBlacklisted(address account) external view returns (bool) {
            return _users[account].isBlacklisted;
        }
        function isSwapEnabled() external view returns (bool) {
            return settings.swapEnabled;
        }
        function amountOfTokensForBotProtection() external view returns (uint256) {
            return tokensForBotProtection;
        }
        function isBlacklistRenounced() external view returns (bool) {
            return settings.blacklistRenounced;
        }
        function isFeeChangeRenounced() external view returns (bool) {
            return settings.feeChangeRenounced;
        }
        function isTradingActive() external view returns (bool) {
            return settings.tradingActive;
        }
        function isLimitInEffect() external view returns (bool) {
            return settings.limitsInEffect;
        }
        function transfer(
            address to,
            uint256 amount
        ) public override returns (bool) {
            _transfer(msg.sender, to, amount);
            return true;
        }
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public override returns (bool) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
            if (allowed != type(uint256).max) {
                allowance[from][msg.sender] = allowed - amount;
            }
            _transfer(from, to, amount);
            return true;
        }
        function _transfer(
            address from,
            address to,
            uint256 amount
        ) internal returns (bool) {
            User memory fromData = _users[from];
            User memory toData = _users[to];
            Settings memory settingCache = settings;
            if (!settingCache.tradingActive) {
                if (!fromData.isExcludedFromFees) {
                    if (!toData.isExcludedFromFees) {
                        revert Index__TradingNotActive();
                    }
                }
            }
            // Apply blacklist protection
            if (fromData.isBlacklisted) {
                revert Index__SenderBlacklisted();
            }
            if (toData.isBlacklisted) {
                revert Index__ReceiverBlacklisted();
            }
            // If zero amount, continue
            if (amount == 0) {
                return true;
            }
            bool excludedFromFees = fromData.isExcludedFromFees ||
                toData.isExcludedFromFees;
            // Cache transaction type for reference.
            // 1 = Buy
            // 2 = Sell
            // 3 = Transfer
            uint8 txType = 3;
            if (fromData.isAutomatedMarketMaker) {
                // Buys originate from the AMM pair
                txType = 1;
            } else if (toData.isAutomatedMarketMaker) {
                // Sells send funds to AMM pair
                txType = 2;
            }
            if (!_swapping) {
                if (settingCache.limitsInEffect) {
                    //when buy
                    if (txType == 1 && !toData.isExcludedFromMaxTransactionAmount) {
                        if (amount > maxTransactionAmount) {
                            revert Index__BuyAmountGreaterThanMax();
                        }
                        if (amount + this.balanceOf(to) > maxWallet) {
                            revert Index__MaxWalletAmountExceeded();
                        }
                    }
                    //when sell
                    else if (
                        txType == 2 && !fromData.isExcludedFromMaxTransactionAmount
                    ) {
                        if (amount > maxTransactionAmount) {
                            revert Index__SellAmountGreaterThanMax();
                        }
                    } else if (!toData.isExcludedFromMaxTransactionAmount) {
                        if (amount + this.balanceOf(to) > maxWallet) {
                            revert Index__MaxWalletAmountExceeded();
                        }
                    }
                }
                if (settingCache.swapEnabled) {
                    // Only sells will trigger the fee swap
                    if (txType == 2) {
                        if (this.balanceOf(address(this)) >= swapTokensAtAmount) {
                            _swapping = true;
                            _swapBack();
                            _swapping = false;
                        }
                    }
                }
            }
            if (txType < 3) {
                bool takeFee = !_swapping;
                // if any account belongs to _isExcludedFromFee account then remove the fee
                if (excludedFromFees) {
                    takeFee = false;
                }
                uint256 fees = 0;
                // only take fees on buys/sells, do not take on wallet transfers
                if (takeFee) {
                    Fees memory feeCache = feeAmounts;
                    // on sell
                    if (txType == 2) {
                        if (feeCache.sell > 0) {
                            fees = (amount * feeCache.sell) / 100;
                        }
                    }
                    // on buy
                    else if (txType == 1) {
                        if (feeCache.buy > 0) {
                            fees = (amount * feeCache.buy) / 100;
                        }
                    }
                    if (block.number < settingCache.endBlock) {
                        uint256 blocksLeft = settingCache.endBlock - block.number;
                        uint256 botFeeMultiplier = 95;
                        // Apply sniper protection - first 18 blocks have a fee reduced 5% each block.
                        if (blocksLeft < 19) {
                            botFeeMultiplier -= (5 * (19 - blocksLeft));
                        }
                        uint256 botFee = (amount * botFeeMultiplier) / 100;
                        // send tokens to dev from bot protection
                        _doTransfer(from, developmentWallet, fees);
                        amount -= botFee;
                        tokensForBotProtection += botFee;
                    }
                    amount -= fees;
                    if (fees > 0) {
                        _doTransfer(from, address(this), fees);
                    }
                }
            }
            _doTransfer(from, to, amount);
            return true;
        }
        function _swapTokensForEth(uint256 tokenAmount) internal {
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = uniswapV2Router.WETH();
            uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                tokenAmount,
                0,
                path,
                address(this),
                block.timestamp
            );
        }
        function _addLiquidity(uint256 tokenAmount, uint256 ethAmount) internal {
            uniswapV2Router.addLiquidityETH{value: ethAmount}(
                address(this),
                tokenAmount,
                0, // slippage is unavoidable
                0, // slippage is unavoidable
                owner,
                block.timestamp
            );
        }
        function _doTransfer(
            address from,
            address to,
            uint256 amount
        ) internal returns (bool) {
            balanceOf[from] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(from, to, amount);
            return true;
        }
        function _swapBack() internal {
            // Cache values
            uint256 contractBalance = this.balanceOf(address(this));
            Fees memory feeCache = feeAmounts;
            if (contractBalance == 0) {
                return;
            }
            // Prevent too many tokens from being swapped
            uint256 maxAmount = swapTokensAtAmount * 20;
            if (contractBalance > maxAmount) {
                contractBalance = maxAmount;
            }
            uint256 liquidityAmount = (contractBalance * feeCache.liquidity) / 100;
            // Halve the amount of liquidity tokens
            uint256 liquidityTokens = liquidityAmount - (liquidityAmount / 2);
            uint256 initialETHBalance = address(this).balance;
            _swapTokensForEth(liquidityTokens);
            uint256 ethForLiquidity = address(this).balance - initialETHBalance;
            if (liquidityTokens > 0 && ethForLiquidity > 0) {
                _addLiquidity(liquidityTokens, ethForLiquidity);
                emit SwapAndLiquify(liquidityTokens, ethForLiquidity);
            }
            uint256 amountToSwapForETH = contractBalance - liquidityAmount;
            _swapTokensForEth(amountToSwapForETH);
            uint256 contractEthAmount = address(this).balance;
            uint256 initialTotalEth = contractEthAmount + (ethForLiquidity * 2);
            uint256 developmentEthAmount = (initialTotalEth *
                feeCache.development) / 100;
            (bool success, ) = address(developmentWallet).call{
                value: developmentEthAmount
            }("");
            require(success);
            uint256 indexEthAmount = contractEthAmount - developmentEthAmount;
            WETH(payable(wethAddress)).deposit{value: indexEthAmount}();
            ERC20(wethAddress).safeTransfer(index, indexEthAmount);
        }
        function _approve(address spender, uint256 amount) internal onlyOwner {
            allowance[address(this)][spender] = amount;
            emit Approval(address(this), spender, amount);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
    /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
    abstract contract ERC20 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event Transfer(address indexed from, address indexed to, uint256 amount);
        event Approval(address indexed owner, address indexed spender, uint256 amount);
        /*//////////////////////////////////////////////////////////////
                                METADATA STORAGE
        //////////////////////////////////////////////////////////////*/
        string public name;
        string public symbol;
        uint8 public immutable decimals;
        /*//////////////////////////////////////////////////////////////
                                  ERC20 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 public totalSupply;
        mapping(address => uint256) public balanceOf;
        mapping(address => mapping(address => uint256)) public allowance;
        /*//////////////////////////////////////////////////////////////
                                EIP-2612 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 internal immutable INITIAL_CHAIN_ID;
        bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
        mapping(address => uint256) public nonces;
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
            INITIAL_CHAIN_ID = block.chainid;
            INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        }
        /*//////////////////////////////////////////////////////////////
                                   ERC20 LOGIC
        //////////////////////////////////////////////////////////////*/
        function approve(address spender, uint256 amount) public virtual returns (bool) {
            allowance[msg.sender][spender] = amount;
            emit Approval(msg.sender, spender, amount);
            return true;
        }
        function transfer(address to, uint256 amount) public virtual returns (bool) {
            balanceOf[msg.sender] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(msg.sender, to, amount);
            return true;
        }
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual returns (bool) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
            if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
            balanceOf[from] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(from, to, amount);
            return true;
        }
        /*//////////////////////////////////////////////////////////////
                                 EIP-2612 LOGIC
        //////////////////////////////////////////////////////////////*/
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
            // Unchecked because the only math done is incrementing
            // the owner's nonce which cannot realistically overflow.
            unchecked {
                address recoveredAddress = ecrecover(
                    keccak256(
                        abi.encodePacked(
                            "\\x19\\x01",
                            DOMAIN_SEPARATOR(),
                            keccak256(
                                abi.encode(
                                    keccak256(
                                        "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                    ),
                                    owner,
                                    spender,
                                    value,
                                    nonces[owner]++,
                                    deadline
                                )
                            )
                        )
                    ),
                    v,
                    r,
                    s
                );
                require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                allowance[recoveredAddress][spender] = value;
            }
            emit Approval(owner, spender, value);
        }
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
            return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
        }
        function computeDomainSeparator() internal view virtual returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                        keccak256(bytes(name)),
                        keccak256("1"),
                        block.chainid,
                        address(this)
                    )
                );
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(address to, uint256 amount) internal virtual {
            totalSupply += amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(address(0), to, amount);
        }
        function _burn(address from, uint256 amount) internal virtual {
            balanceOf[from] -= amount;
            // Cannot underflow because a user's balance
            // will never be larger than the total supply.
            unchecked {
                totalSupply -= amount;
            }
            emit Transfer(from, address(0), amount);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    import {ERC20} from "./ERC20.sol";
    import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
    /// @notice Minimalist and modern Wrapped Ether implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
    /// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
    contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
        using SafeTransferLib for address;
        event Deposit(address indexed from, uint256 amount);
        event Withdrawal(address indexed to, uint256 amount);
        function deposit() public payable virtual {
            _mint(msg.sender, msg.value);
            emit Deposit(msg.sender, msg.value);
        }
        function withdraw(uint256 amount) public virtual {
            _burn(msg.sender, amount);
            emit Withdrawal(msg.sender, amount);
            msg.sender.safeTransferETH(amount);
        }
        receive() external payable virtual {
            deposit();
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Simple single owner authorization mixin.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
    abstract contract Owned {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event OwnershipTransferred(address indexed user, address indexed newOwner);
        /*//////////////////////////////////////////////////////////////
                                OWNERSHIP STORAGE
        //////////////////////////////////////////////////////////////*/
        address public owner;
        modifier onlyOwner() virtual {
            require(msg.sender == owner, "UNAUTHORIZED");
            _;
        }
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(address _owner) {
            owner = _owner;
            emit OwnershipTransferred(address(0), _owner);
        }
        /*//////////////////////////////////////////////////////////////
                                 OWNERSHIP LOGIC
        //////////////////////////////////////////////////////////////*/
        function transferOwnership(address newOwner) public virtual onlyOwner {
            owner = newOwner;
            emit OwnershipTransferred(msg.sender, newOwner);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    import {ERC20} from "../tokens/ERC20.sol";
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
    /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
    /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
    library SafeTransferLib {
        /*//////////////////////////////////////////////////////////////
                                 ETH OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferETH(address to, uint256 amount) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Transfer the ETH and store if it succeeded or not.
                success := call(gas(), to, amount, 0, 0, 0, 0)
            }
            require(success, "ETH_TRANSFER_FAILED");
        }
        /*//////////////////////////////////////////////////////////////
                                ERC20 OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferFrom(
            ERC20 token,
            address from,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
                mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                )
            }
            require(success, "TRANSFER_FROM_FAILED");
        }
        function safeTransfer(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                )
            }
            require(success, "TRANSFER_FAILED");
        }
        function safeApprove(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                )
            }
            require(success, "APPROVE_FAILED");
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.13;
    interface IUniswapV2Factory {
        function createPair(
            address tokenA,
            address tokenB
        ) external returns (address pair);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.13;
    interface IUniswapV2Router {
        function factory() external pure returns (address);
        function WETH() external pure returns (address);
        function addLiquidity(
            address tokenA,
            address tokenB,
            uint256 amountADesired,
            uint256 amountBDesired,
            uint256 amountAMin,
            uint256 amountBMin,
            address to,
            uint256 deadline
        ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
        function removeLiquidity(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB);
        function addLiquidityETH(
            address token,
            uint256 amountTokenDesired,
            uint256 amountTokenMin,
            uint256 amountETHMin,
            address to,
            uint256 deadline
        )
            external
            payable
            returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);
        function swapExactTokensForTokensSupportingFeeOnTransferTokens(
            uint256 amountIn,
            uint256 amountOutMin,
            address[] calldata path,
            address to,
            uint256 deadline
        ) external;
        function swapExactETHForTokensSupportingFeeOnTransferTokens(
            uint256 amountOutMin,
            address[] calldata path,
            address to,
            uint256 deadline
        ) external payable;
        function swapExactTokensForETHSupportingFeeOnTransferTokens(
            uint256 amountIn,
            uint256 amountOutMin,
            address[] calldata path,
            address to,
            uint256 deadline
        ) external;
    }