BaseBase
0x8e...a846
0x8E...A846

0x8E...A846

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.25+commit.b61c2a91
语言
Solidity
合同源代码
文件 1 的 10:ERC20.sol
// 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);
    }
}
合同源代码
文件 2 的 10:ERC20Token.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

import "solmate/tokens/ERC20.sol";
import "solmate/auth/Owned.sol";

contract ERC20Token is ERC20, Owned {
    string public tokenUri;
    bool public transferLocked;

    constructor(
        string memory name,
        string memory symbol,
        uint8 decimals,
        uint256 tradeableSupply,
        address _owner,
        string memory _tokenUri
    ) ERC20(name, symbol, decimals) Owned(_owner) {
        tokenUri = _tokenUri;
        transferLocked = true;
        _mint(_owner, tradeableSupply);
    }

    /// @notice Mint reserve tokens, unlock transfers, and transfer ownership to address(0)
    /// @param reserveSupply The amount of tokens to mint
    function mintReserveSupply(uint256 reserveSupply) public onlyOwner {
        transferLocked = false;
        _mint(owner, reserveSupply);
        transferOwnership(address(0));
    }

    /// @notice Transfer tokens -- only if transfers are not locked
    /// @param to The address to transfer to
    /// @param amount The amount to transfer
    function transfer(address to, uint256 amount) public override returns (bool) {
        _checkTransferLock(msg.sender, to);
        return super.transfer(to, amount);
    }

    /// @notice Transfer tokens from -- only if transfers are not locked
    /// @param from The address to transfer from
    /// @param to The address to transfer to
    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        _checkTransferLock(from, to);
        return super.transferFrom(from, to, amount);
    }

    /// @notice Lock transfers to owner while transferLocked is on
    /// @param from The address to transfer from
    /// @param to The address to transfer to
    function _checkTransferLock(address from, address to) internal view {
        require(!transferLocked || to == owner || from == owner, "Transfers are locked");
    }
}
合同源代码
文件 3 的 10: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;
}
合同源代码
文件 4 的 10: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;
}
合同源代码
文件 5 的 10:IWETH.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

interface IWETH {
    function deposit() external payable;
    function withdraw(uint256) external;
    function approve(address guy, uint256 wad) external returns (bool);
    function transfer(address dst, uint256 wad) external returns (bool);
    function balanceOf(address owner) external view returns (uint256);
}
合同源代码
文件 6 的 10:Null.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

contract Null {}
合同源代码
文件 7 的 10:Owned.sol
// 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);
    }
}
合同源代码
文件 8 的 10:ReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() virtual {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}
合同源代码
文件 9 的 10:SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

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

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

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

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

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

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success :=
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(and(call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), exists)) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `1` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero(call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00)) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}
合同源代码
文件 10 的 10:SeriousMarket.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

import "./ERC20Token.sol";
import "./IWETH.sol";
import "./Null.sol";
import "solmate/auth/Owned.sol";
import "solmate/utils/ReentrancyGuard.sol";
import "solady/utils/SafeTransferLib.sol";
import "v2-core/interfaces/IUniswapV2Factory.sol";
import "v2-core/interfaces/IUniswapV2Pair.sol";

contract SeriousMarketProtocol is Owned, ReentrancyGuard {
    ///
    /// STATE
    ///

    // Token constants
    uint8 public constant tokenDecimals = 18; // All tokens have 18 decimal places
    uint256 public constant tradeableSupply = 657_000_000; // Supply available to users

    // Curve constants
    int256 public constant bondingCurveSlope = 30; // In wei
    int256 private constant _halfBondingCurveSlope = bondingCurveSlope / 2; // In wei
    int256 public constant bondingCurveStartingPrice = 1_000_000_000; // In wei
    uint256 public constant totalLiquidity = uint256(
        bondingCurveStartingPrice * int256(tradeableSupply) + (bondingCurveSlope * (int256(tradeableSupply) ** 2)) / 2
    ); // In wei, total ETH generated from sales of tradeable supply
    uint256 public constant cooldownPeriod = 100; // Blocks

    // Pool constants
    uint256 public constant fractionalTokensPerWeth = 48067795;
    IUniswapV2Factory public immutable uniswapFactory;
    IWETH public immutable weth;
    address public immutable nullContractAddress;

    // Per token data
    struct TokenData {
        ERC20Token token;
        uint256 tokensSold;
        bool tradingEnabled;
        uint256 cooldownEnd; // Block number when trading can be re-enabled
    }

    /// Mapping of ERC20 token addresses to their respective ERC20Token instances
    mapping(address => TokenData) public tokenDatas;

    // Mappings for referral codes
    mapping(bytes8 => address) public referralCodeToAddress;
    mapping(address => bytes8) public addressToReferralCode;
    mapping(address => bool) public isReferralCodeUsed;

    // Fees / rewards
    address public feeWallet;
    uint256 public buyFeeBps;
    uint256 public sellFeeBps;
    uint256 public referralFeeBps; // Bps on protocol fee
    uint256 public poolCreationReward;

    ///
    /// EVENTS
    ///

    /// @dev Emitted when the fee wallet is set
    /// @param feeWallet The address of the new fee wallet
    event FeeWalletSet(address indexed feeWallet);

    /// @dev Emitted when the buy fee is set
    /// @param buyFeeBps The new buy fee in basis points
    event BuyFeeSet(uint256 buyFeeBps);

    /// @dev Emitted when the sell fee is set
    /// @param sellFeeBps The new sell fee in basis points
    event SellFeeSet(uint256 sellFeeBps);

    /// @dev Emitted when the referral fee is set
    /// @param referrerFeeBps The new referral fee in basis points
    event ReferralFeeSet(uint256 referrerFeeBps);

    /// @dev Emitted when the pool creation reward is set
    /// @param poolCreationReward The new pool creation reward in wei
    event PoolCreationRewardSet(uint256 poolCreationReward);

    /// @dev Emitted when a referral code is generated
    /// @param referrer The address of the referrer
    /// @param referralCode The generated referral code
    event ReferralCodeGenerated(address indexed referrer, bytes8 referralCode);

    /// @dev Emitted when a new ERC20 token is created
    /// @param creator The address of the creator
    /// @param tokenAddress The address of the newly created token
    /// @param name The name of the new token
    /// @param symbol The symbol of the new token
    /// @param tokenUri The URI of the metadata of the new token
    /// @param blockTimestamp The timestamp of the block the token was created in
    event TokenContractCreated(
        address indexed creator,
        address indexed tokenAddress,
        string name,
        string symbol,
        string tokenUri,
        uint256 blockTimestamp
    );

    /// @dev Emitted when a user buys tokens with ETH
    /// @param tokenAddress The address of the ERC20 token they bought
    /// @param buyer The address of the buyer
    /// @param referrer The address of the referrer
    /// @param amountWholeTokens The amount of tokens they bought (in whole tokens)
    /// @param ethTradeValue The amount of ETH the trade costs - not including fee
    /// @param feeValue The amount of the fee in ETH
    /// @param referralFeeValue The amount of the referral fee in ETH
    /// @param buyerTokenBalance The balance of the buyer after the trade - in wei
    /// @param totalNumTokensSold The total number of tokens sold by the factory (in whole tokens)
    /// @param blockTimestamp The timestamp of the block the trade was made in
    event TokensBought(
        address indexed tokenAddress,
        address indexed buyer,
        address indexed referrer,
        uint256 amountWholeTokens,
        uint256 ethTradeValue,
        uint256 feeValue,
        uint256 referralFeeValue,
        uint256 buyerTokenBalance,
        uint256 totalNumTokensSold,
        uint256 blockTimestamp
    );

    /// @dev Emitted when a user sells tokens for ETH
    /// @param tokenAddress The address of the ERC20 token they sold
    /// @param seller The address of the seller
    /// @param referrer The address of the referrer
    /// @param amountWholeTokens The amount of tokens they sold (in whole tokens)
    /// @param ethTradeValue The amount of ETH they are quoted -- not including fee
    /// @param feeValue The amount of the fee in ETH
    /// @param referralFeeValue The amount of the referral fee in ETH
    /// @param sellerTokenBalance The balance of the seller after the trade - in wei
    /// @param totalNumTokensSold The total number of tokens sold by the factory (in whole tokens)
    /// @param blockTimestamp The timestamp of the block the trade was made in
    event TokensSold(
        address indexed tokenAddress,
        address indexed seller,
        address indexed referrer,
        uint256 amountWholeTokens,
        uint256 ethTradeValue,
        uint256 feeValue,
        uint256 referralFeeValue,
        uint256 sellerTokenBalance,
        uint256 totalNumTokensSold,
        uint256 blockTimestamp
    );

    /// @dev Emitted when a new pool is created and liquidity is added
    /// @param poolAddress The address of the new pool
    /// @param tokenAddress The address of the ERC20 token in the pool
    /// @param tokenAmountSent The amount of tokens sent to the pool
    /// @param wethAmountSent The amount of WETH sent to the pool
    /// @param priorWethAmountInPool The amount of WETH in the pool before the trade
    /// @param blockTimestamp The timestamp of the block the pool was created in
    event PoolFunded(
        address indexed poolAddress,
        address indexed tokenAddress,
        uint256 tokenAmountSent,
        uint256 wethAmountSent,
        uint256 priorWethAmountInPool,
        uint256 blockTimestamp
    );

    /// @dev Emitted when ETH is sent directly to the contract
    /// @param sender The address of the sender
    /// @param amount The amount of ETH sent
    event EthReceived(address indexed sender, uint256 amount);

    ///
    /// CONSTRUCTOR
    ///

    constructor(address _owner, address _feeWallet, address _uniswapFactory, address _weth) Owned(_owner) {
        require(_feeWallet != address(0), "Fee wallet cannot be the zero address");
        feeWallet = _feeWallet;
        uniswapFactory = IUniswapV2Factory(_uniswapFactory);
        weth = IWETH(_weth);
        nullContractAddress = address(new Null());
        buyFeeBps = 100;
        sellFeeBps = 100;
        referralFeeBps = 1000;
        poolCreationReward = 0.1 ether;
    }

    ///
    /// SETTERS
    ///

    /// @notice Allows the owner to set the fee wallet
    /// @param _feeWallet The address of the new fee wallet
    function setFeeWallet(address _feeWallet) external onlyOwner {
        require(_feeWallet != address(0), "Fee wallet cannot be the zero address");
        feeWallet = _feeWallet;
        emit FeeWalletSet(_feeWallet);
    }

    /// @notice Allows the owner to set the buy fee
    /// @param _buyFeeBps The new buy fee in basis points
    function setBuyFee(uint256 _buyFeeBps) external onlyOwner {
        require(_buyFeeBps <= 10_000, "Fee must be <= 10000 basis points");
        buyFeeBps = _buyFeeBps;
        emit BuyFeeSet(_buyFeeBps);
    }

    /// @notice Allows the owner to set the referral fee
    /// @param _referralFeeBps The new referral fee in basis points
    function setReferralFee(uint256 _referralFeeBps) external onlyOwner {
        require(_referralFeeBps <= 10_000, "Fee must be <= 10000 basis points");
        referralFeeBps = _referralFeeBps;
        emit ReferralFeeSet(_referralFeeBps);
    }

    /// @notice Allows the owner to set the sell fee
    /// @param _sellFeeBps The new sell fee in basis points
    function setSellFee(uint256 _sellFeeBps) external onlyOwner {
        require(_sellFeeBps <= 10_000, "Fee must be <= 10000 basis points");
        sellFeeBps = _sellFeeBps;
        emit SellFeeSet(_sellFeeBps);
    }

    /// @notice Allows the owner to set the pool creation reward
    /// @param _poolCreationReward The new pool creation reward in wei
    function setPoolCreationReward(uint256 _poolCreationReward) external onlyOwner {
        require(_poolCreationReward < totalLiquidity, "Reward must be less than total liquidity");
        poolCreationReward = _poolCreationReward;
        emit PoolCreationRewardSet(_poolCreationReward);
    }

    ///
    /// MUTATING FUNCTIONS
    ///

    /// @notice Generates a referral code for the caller
    function generateReferralCode() external returns (bytes8) {
        bytes memory encodedData = abi.encodePacked(msg.sender, block.number);
        bytes32 hash = keccak256(encodedData);
        bytes8 truncatedHash = bytes8(hash);

        require(referralCodeToAddress[truncatedHash] == address(0), "Referral code already in use");
        require(!isReferralCodeUsed[msg.sender], "Address already has referral code");

        referralCodeToAddress[truncatedHash] = msg.sender;
        addressToReferralCode[msg.sender] = truncatedHash;
        isReferralCodeUsed[msg.sender] = true;

        emit ReferralCodeGenerated(msg.sender, truncatedHash);

        return truncatedHash;
    }

    /// @notice Creates a new ERC20 token with specified parameters
    /// @param name The name of the new token
    /// @param symbol The symbol of the new token
    /// @param tokenUri The URI of the metadata of the new token
    /// @return The address of the newly created ERC20 token
    /// @dev This function mints an initial supply of totalSupply tokens to the factory address
    function createToken(string memory name, string memory symbol, string memory tokenUri) external returns (address) {
        ERC20Token newToken =
            new ERC20Token(name, symbol, tokenDecimals, _getFractionalTokens(tradeableSupply), address(this), tokenUri);

        // Track the created token in a mapping so the contract knows which tokens it has created
        tokenDatas[address(newToken)] = TokenData(newToken, 0, true, 0);

        emit TokenContractCreated(msg.sender, address(newToken), name, symbol, tokenUri, block.timestamp);
        return address(newToken);
    }

    /// @notice Creates / gets a pool and adds liquidity to it
    /// @param tokenAddress The address of the ERC20 token to create the pool with
    /// @return The address of the newly created pool
    function addLiquidityToUniswapPool(address tokenAddress) external nonReentrant returns (address) {
        TokenData memory tokenData = tokenDatas[tokenAddress];
        require(tokenData.token != ERC20Token(address(0)), "Token does not exist");
        require(tokenData.tradingEnabled, "Pool has already been created");
        require(tokenData.tokensSold >= tradeableSupply, "Not enough tokens sold to create pool");

        // Disable trading on token
        tokenDatas[tokenAddress].tradingEnabled = false;

        // Convert ETH from contract to WETH
        uint256 ethAmount = totalLiquidity - poolCreationReward;
        weth.deposit{value: ethAmount}();

        // Create the pool if it doesn't already exist
        address poolAddress = uniswapFactory.getPair(address(weth), tokenAddress);
        if (poolAddress == address(0)) {
            poolAddress = uniswapFactory.createPair(address(weth), tokenAddress);
        }

        // Mint the correct number of tokens to maintain the expected price ratio
        uint256 wethInPool = weth.balanceOf(poolAddress);
        uint256 tokenAmount = fractionalTokensPerWeth * (ethAmount + wethInPool);
        tokenData.token.mintReserveSupply(tokenAmount);

        // Send tokens to pool -- no LP claimed because it's a donation
        SafeTransferLib.safeTransfer(tokenAddress, poolAddress, tokenAmount);
        SafeTransferLib.safeTransfer(address(weth), poolAddress, ethAmount);
        require(
            IUniswapV2Pair(poolAddress).mint(address(nullContractAddress)) > 0,
            "Failed to initalize the tokens in the pool"
        );

        // Send pool reward to creator
        SafeTransferLib.safeTransferETH(msg.sender, poolCreationReward);

        emit PoolFunded(poolAddress, tokenAddress, tokenAmount, ethAmount, wethInPool, block.timestamp);
        return poolAddress;
    }

    /// @notice Allows users to buy tokens with ETH
    /// @param tokenAddress The address of the ERC20 token they want to buy
    /// @param amount The amount of tokens they want to purchase (in whole tokens) - calculate this with getTokensForBuyTradeWithFee
    /// @param referralCode The referral code of the referrer
    function buyToken(address tokenAddress, uint256 amount, bytes8 referralCode) external payable nonReentrant {
        require(amount > 0, "Amount must be greater than 0");
        require(referralCodeToAddress[referralCode] != msg.sender, "Cannot refer yourself");

        TokenData memory tokenData = tokenDatas[tokenAddress];
        require(tokenData.token != ERC20Token(address(0)), "Token does not exist");
        require(tokenData.tradingEnabled, "Trading is disabled for this token");

        // Get num tokens sold, in whole tokens
        require(tokenData.tokensSold < tradeableSupply, "All tokens have been sold");

        // Buy out remaining tokens if tokens sold
        if (tokenData.tokensSold + amount > tradeableSupply) {
            amount = tradeableSupply - tokenData.tokensSold;
        }

        // Get the cost of the trade in ETH and confirm the user has sent enough
        uint256 ethTradeValue = getEthValueForTrade(int256(tokenData.tokensSold), int256(amount));
        uint256 protocolFee = getBuyFee(ethTradeValue);
        uint256 totalEthValue = ethTradeValue + protocolFee;
        require(msg.value >= totalEthValue, "Not enough Ether sent");

        tokenDatas[tokenAddress].tokensSold += amount;
        uint256 tokensSold = tokenDatas[tokenAddress].tokensSold;
        if (tokensSold == tradeableSupply) {
            tokenDatas[tokenAddress].cooldownEnd = block.number + cooldownPeriod;
        }

        // Transfer ERC20 tokens to the user
        SafeTransferLib.safeTransfer(tokenAddress, msg.sender, _getFractionalTokens(amount));

        // Transfer referral fee to referrer if referrer present
        (address referrer, uint256 referralFee) = _transferReferralFee(referralCode, protocolFee);
        protocolFee -= referralFee;

        // Send protocol fee to fee wallet
        SafeTransferLib.safeTransferETH(feeWallet, protocolFee);

        // Send remaining ETH to the user
        uint256 refund = msg.value - totalEthValue;
        if (refund > 0) {
            SafeTransferLib.safeTransferETH(msg.sender, refund);
        }

        emit TokensBought(
            tokenAddress,
            msg.sender,
            referrer,
            amount,
            ethTradeValue,
            protocolFee,
            referralFee,
            tokenData.token.balanceOf(msg.sender),
            tokensSold,
            block.timestamp
        );
    }

    /// @notice Allows users to sell tokens for ETH
    /// @param tokenAddress The address of the ERC20 token they want to sell
    /// @param amount The amount of tokens they want to sell (in whole tokens)
    /// @param estimatedEthReturned The estimated amount of ETH returned to the user - calculate this with getEthValueForTradeWithFee
    /// @param slippageBps The slippage in basis points
    /// @param referralCode The referral code of the referrer
    function sellToken(
        address tokenAddress,
        uint256 amount,
        uint256 estimatedEthReturned,
        uint256 slippageBps,
        bytes8 referralCode
    ) external nonReentrant {
        require(amount > 0, "Amount must be greater than 0");
        require(slippageBps <= 10_000, "Slippage must be <= 10000 basis points");
        require(referralCodeToAddress[referralCode] != msg.sender, "Cannot refer yourself");

        TokenData memory tokenData = tokenDatas[tokenAddress];
        ERC20Token token = tokenData.token;
        require(token != ERC20Token(address(0)), "Token does not exist");
        require(tokenData.tradingEnabled, "Trading is disabled for this token");
        require(block.number >= tokenData.cooldownEnd, "No trading during cooldown period");
        require(getWholeTokenBalance(msg.sender, address(token)) >= amount, "Not enough tokens in user wallet");

        // Get the value of the trade in ETH
        uint256 tokensSold = tokenData.tokensSold;
        uint256 ethTradeValue = getEthValueForTrade(int256(tokensSold), -int256(amount));
        uint256 protocolFee = getSellFee(ethTradeValue);
        uint256 ethValueForUser = ethTradeValue - protocolFee;

        // Check if the user is getting the expected amount of ETH
        if (ethValueForUser < estimatedEthReturned) {
            require(
                estimatedEthReturned - ethValueForUser <= (estimatedEthReturned * slippageBps) / 10_000,
                "Slippage exceeded"
            );
        }

        tokenDatas[tokenAddress].tokensSold -= amount;

        // Transfer ERC20 tokens from user's wallet to contract
        SafeTransferLib.safeTransferFrom(tokenAddress, msg.sender, address(this), _getFractionalTokens(amount));

        // Transfer referral fee to referrer if referrer present
        (address referrer, uint256 referralFee) = _transferReferralFee(referralCode, protocolFee);
        protocolFee -= referralFee;

        // Send protocol fee to fee wallet
        SafeTransferLib.safeTransferETH(feeWallet, protocolFee);

        // Send ETH to user
        SafeTransferLib.safeTransferETH(msg.sender, ethValueForUser);

        emit TokensSold(
            address(token),
            msg.sender,
            referrer,
            amount,
            ethTradeValue,
            protocolFee,
            referralFee,
            token.balanceOf(msg.sender),
            tokenDatas[tokenAddress].tokensSold,
            block.timestamp
        );
    }

    ///
    /// INTERNAL FUNCTIONS
    ///

    /// @notice Transfers the referral fee to the referrer
    /// @param referralCode The referral code of the referrer
    /// @param protocolFeeValue The amount of ETH for the protocol fee
    /// @return The address of the referrer and the amount of the referral fee (in wei)
    function _transferReferralFee(bytes8 referralCode, uint256 protocolFeeValue) internal returns (address, uint256) {
        address referrer = referralCodeToAddress[referralCode];
        uint256 referralFee = 0;
        if (referrer != address(0)) {
            referralFee = getReferralFee(protocolFeeValue);
            SafeTransferLib.forceSafeTransferETH(referrer, referralFee);
        }

        return (referrer, referralFee);
    }

    /// @notice Converts a whole number of tokens to a fractional number of tokens
    /// @param amount The amount of tokens to convert
    function _getFractionalTokens(uint256 amount) internal pure returns (uint256) {
        return amount * (10 ** tokenDecimals);
    }

    /// @notice Babylonian sqrt method 
    /// @notice Reference is UniswapV2 math: https://github.com/Uniswap/v2-core/blob/master/contracts/libraries/Math.sol
    /// @param y The number to take the square root of
    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;
        }
    }

    ///
    /// VIEW FUNCTIONS
    ///

    /// @notice Retrieves the fee charged for a referral
    /// @param protocolFeeValue The amount of ETH for the protocol fee
    /// @return The fee taken out of the protocol fee for the referrer (in wei)
    function getReferralFee(uint256 protocolFeeValue) public view returns (uint256) {
        return (protocolFeeValue * referralFeeBps) / 10_000;
    }

    /// @notice Retrieves the fee charged for a buy transaction
    /// @param ethValue The amount of ETH sent in the transaction
    /// @return The fee charged for the transaction (in wei)
    function getBuyFee(uint256 ethValue) public view returns (uint256) {
        return (ethValue * buyFeeBps) / 10_000;
    }

    /// @notice Retrieves the fee charged for a sell transaction
    /// @param ethValue The amount of ETH returned in the transaction
    /// @return The fee charged for the transaction (in wei)
    function getSellFee(uint256 ethValue) public view returns (uint256) {
        return (ethValue * sellFeeBps) / 10_000;
    }

    /// @notice Retrieves the current price of the token
    /// @param supplySold The total number of tokens sold by the factory
    /// @return The current ETH price of the token (in wei)
    function getCurrentPrice(int256 supplySold) public pure returns (uint256) {
        return uint256(bondingCurveSlope * supplySold + bondingCurveStartingPrice);
    }

    /// @notice Retrieves the amount of ETH required to purchase a specified number of tokens
    /// @param supplySold The total number of tokens sold by the factory
    /// @param amount The number of tokens the user wants to purchase - positive is a buy, negative is a sell
    /// @return The amount of ETH required to purchase the specified number of tokens (in wei)
    function getEthValueForTrade(int256 supplySold, int256 amount) public pure returns (uint256) {
        int256 ethValue = (bondingCurveSlope * (amount ** 2)) / 2 + bondingCurveSlope * supplySold * amount
            + bondingCurveStartingPrice * amount;

        if (ethValue < 0) {
            return uint256(-ethValue);
        }

        return uint256(ethValue);
    }

    /// @notice Retrieves the amount of ETH required to purchase a specified number of tokens, including the fee
    /// @param supplySold The total number of tokens sold by the factory
    /// @param amount The number of tokens the user wants to purchase - positive is a buy, negative is a sell
    /// @return The amount of ETH required to purchase the specified number of tokens, including the fee (in wei)
    function getEthValueForTradeWithFee(int256 supplySold, int256 amount) public view returns (uint256) {
        uint256 ethValue = getEthValueForTrade(supplySold, amount);
        return amount >= 0 ? ethValue + getBuyFee(ethValue) : ethValue - getSellFee(ethValue);
    }

    /// @notice Retrieves the number of tokens a user can buy with a specified amount of ETH
    /// @param supplySold The total number of tokens sold by the factory
    /// @param ethValue The amount of ETH the user wants to spend
    /// @return The number of tokens the user can buy (in whole tokens)
    function getTokensForBuyTradeWithFee(int256 supplySold, uint256 ethValue, uint256 slippageBps)
        public
        view
        returns (uint256)
    {
        require(ethValue > 0, "ETH value must be greater than 0");
        require(slippageBps <= 10_000, "Slippage must be <= 10000 basis points");
        uint256 realValue = (ethValue * 10_000) / (buyFeeBps + slippageBps + 10_000);
        int256 a = _halfBondingCurveSlope;
        int256 b = bondingCurveSlope * supplySold + bondingCurveStartingPrice;
        int256 c = -int256(realValue);

        int256 discriminant = b ** 2 - 4 * a * c;
        int256 tokens = (-b + int256(_sqrt(uint256(discriminant)))) / (2 * a);

        return uint256(tokens);
    }

    /// @notice Retrieves the balance of a specified ERC20 token
    /// @param tokenOwner The address of the token holder
    /// @param tokenAddress The address of the ERC20 token
    /// @return balance The balance of the specified token held by the owner (in whole tokens)
    function getWholeTokenBalance(address tokenOwner, address tokenAddress) public view returns (uint256) {
        ERC20 token = ERC20(tokenAddress);
        uint256 balanceInSubunits = token.balanceOf(tokenOwner);
        return balanceInSubunits / (10 ** tokenDecimals); // Convert wei to whole tokens
    }

    ///
    /// SPECIAL FUNCTIONS
    ///

    /// @notice Allows the factory to receive Ether
    receive() external payable {
        emit EthReceived(msg.sender, msg.value);
    }
}
设置
{
  "compilationTarget": {
    "src/SeriousMarket.sol": "SeriousMarketProtocol"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [
    ":ds-test/=lib/solmate/lib/ds-test/src/",
    ":forge-std/=lib/forge-std/src/",
    ":solady/=lib/solady/src/",
    ":solmate/=lib/solmate/src/",
    ":swap-router-contracts/=lib/swap-router-contracts/",
    ":v2-core/=lib/v2-core/contracts/",
    ":v2-periphery/=lib/v2-periphery/contracts/",
    ":v3-periphery/=lib/v3-periphery/"
  ],
  "viaIR": true
}
ABI
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_feeWallet","type":"address"},{"internalType":"address","name":"_uniswapFactory","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"buyFeeBps","type":"uint256"}],"name":"BuyFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EthReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeWallet","type":"address"}],"name":"FeeWalletSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolCreationReward","type":"uint256"}],"name":"PoolCreationRewardSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmountSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wethAmountSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"priorWethAmountInPool","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"PoolFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"bytes8","name":"referralCode","type":"bytes8"}],"name":"ReferralCodeGenerated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"referrerFeeBps","type":"uint256"}],"name":"ReferralFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sellFeeBps","type":"uint256"}],"name":"SellFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"string","name":"tokenUri","type":"string"},{"indexed":false,"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"TokenContractCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountWholeTokens","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethTradeValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"referralFeeValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyerTokenBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalNumTokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"TokensBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountWholeTokens","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethTradeValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"referralFeeValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerTokenBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalNumTokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"TokensSold","type":"event"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"addLiquidityToUniswapPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToReferralCode","outputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondingCurveSlope","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondingCurveStartingPrice","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes8","name":"referralCode","type":"bytes8"}],"name":"buyToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cooldownPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"tokenUri","type":"string"}],"name":"createToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fractionalTokensPerWeth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"generateReferralCode","outputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ethValue","type":"uint256"}],"name":"getBuyFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"supplySold","type":"int256"}],"name":"getCurrentPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"supplySold","type":"int256"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"getEthValueForTrade","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"supplySold","type":"int256"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"getEthValueForTradeWithFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"protocolFeeValue","type":"uint256"}],"name":"getReferralFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ethValue","type":"uint256"}],"name":"getSellFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"supplySold","type":"int256"},{"internalType":"uint256","name":"ethValue","type":"uint256"},{"internalType":"uint256","name":"slippageBps","type":"uint256"}],"name":"getTokensForBuyTradeWithFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOwner","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getWholeTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isReferralCodeUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nullContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolCreationReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"referralCodeToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sellFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"estimatedEthReturned","type":"uint256"},{"internalType":"uint256","name":"slippageBps","type":"uint256"},{"internalType":"bytes8","name":"referralCode","type":"bytes8"}],"name":"sellToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_buyFeeBps","type":"uint256"}],"name":"setBuyFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeWallet","type":"address"}],"name":"setFeeWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolCreationReward","type":"uint256"}],"name":"setPoolCreationReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_referralFeeBps","type":"uint256"}],"name":"setReferralFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_sellFeeBps","type":"uint256"}],"name":"setSellFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenDatas","outputs":[{"internalType":"contract ERC20Token","name":"token","type":"address"},{"internalType":"uint256","name":"tokensSold","type":"uint256"},{"internalType":"bool","name":"tradingEnabled","type":"bool"},{"internalType":"uint256","name":"cooldownEnd","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradeableSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapV2Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]