Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x02a75078d50c0259594d47f43C4E2dcC844F76d7
Balance 0 ETH
Nonce 1
Code Size 6604 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

6604 bytes
0x6080604052600436106101c8575f3560e01c806379cc6790116100f2578063bd382b4011610092578063d505accf11610062578063d505accf146105f0578063dd62ed3e1461060f578063ee2923d914610643578063f887ea4014610692575f80fd5b8063bd382b4014610562578063beb0a41614610576578063bf9f2fe71461058a578063c45a0155146105bd575f80fd5b806395d89b41116100cd57806395d89b41146104dd578063a8aa1b31146104f1578063a9059cbb14610510578063b4c2cb471461052f575f80fd5b806379cc6790146104625780637ecebe00146104815780638da5cb5b146104b2575f80fd5b80633644e515116101685780636c56ea64116101385780636c56ea64146103b757806370a08231146103ea5780637233f2be1461041b5780637632781b1461042f575f80fd5b80633644e5151461031c578063381283051461033057806342966c681461036357806361fe25e514610384575f80fd5b806318160ddd116101a357806318160ddd14610289578063209bc610146102af57806323b872dd146102e2578063313ce56714610301575f80fd5b806306fdde03146101ea578063095ea7b3146102145780630c88fc1114610243575f80fd5b366101e65760405163668a64f760e11b815260040160405180910390fd5b5f80fd5b3480156101f5575f80fd5b506101fe6106b1565b60405161020b9190611658565b60405180910390f35b34801561021f575f80fd5b5061023361022e3660046116b8565b610741565b604051901515815260200161020b565b34801561024e575f80fd5b506102767f000000000000000000000000000000000000000000000000000000000000012c81565b60405161ffff909116815260200161020b565b348015610294575f80fd5b506805345cdf77eb68f44c545b60405190815260200161020b565b3480156102ba575f80fd5b506102767f00000000000000000000000000000000000000000000000000000000000000c881565b3480156102ed575f80fd5b506102336102fc3660046116e2565b610791565b34801561030c575f80fd5b506040516012815260200161020b565b348015610327575f80fd5b506102a1610861565b34801561033b575f80fd5b506102767f0000000000000000000000000000000000000000000000000000000000000bb881565b34801561036e575f80fd5b5061038261037d366004611720565b6108dd565b005b34801561038f575f80fd5b506102767f0000000000000000000000000000000000000000000000000000000000000dac81565b3480156103c2575f80fd5b506102767f000000000000000000000000000000000000000000000000000000000000012c81565b3480156103f5575f80fd5b506102a1610404366004611737565b6387a211a2600c9081525f91909152602090205490565b348015610426575f80fd5b506101fe6108ea565b34801561043a575f80fd5b506102a17f0000000000000000000000000000000000000000000000000000000000000e1081565b34801561046d575f80fd5b5061038261047c3660046116b8565b6108f9565b34801561048c575f80fd5b506102a161049b366004611737565b6338377508600c9081525f91909152602090205490565b3480156104bd575f80fd5b506104c55f81565b6040516001600160a01b03909116815260200161020b565b3480156104e8575f80fd5b506101fe610912565b3480156104fc575f80fd5b506006546104c5906001600160a01b031681565b34801561051b575f80fd5b5061023361052a3660046116b8565b610921565b34801561053a575f80fd5b506102a17f000000000000000000000000000000000000000000000000000000000012750081565b34801561056d575f80fd5b506101fe6109ae565b348015610581575f80fd5b506101fe610a39565b348015610595575f80fd5b506102a17f0000000000000000000000000000000000000000000000000000000065abb77b81565b3480156105c8575f80fd5b506104c57f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b81565b3480156105fb575f80fd5b5061038261060a366004611759565b610a48565b34801561061a575f80fd5b506102a16106293660046117ca565b602052637f5e9f20600c9081525f91909152603490205490565b34801561064e575f80fd5b50610657610bdb565b60408051825161ffff90811682526020808501518216908301528383015116918101919091526060918201519181019190915260800161020b565b34801561069d575f80fd5b506007546104c5906001600160a01b031681565b6060600180546106c090611801565b80601f01602080910402602001604051908101604052809291908181526020018280546106ec90611801565b80156107375780601f1061070e57610100808354040283529160200191610737565b820191905f5260205f20905b81548152906001019060200180831161071a57829003601f168201915b5050505050905090565b5f82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa35060015b92915050565b5f61079d848484610c1c565b8360601b33602052637f5e9f208117600c526034600c20805460018101156107da57808511156107d4576313be252b5f526004601cfd5b84810382555b50506387a211a28117600c526020600c208054808511156108025763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a350610857848484610eb2565b5060019392505050565b5f8061086b6106b1565b8051906020012090506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81528160208201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015260a081209250505090565b6108e7338261116e565b50565b6060600480546106c090611801565b6109048233836111f4565b61090e828261116e565b5050565b6060600280546106c090611801565b5f61092d338484610c1c565b6387a211a2600c52335f526020600c208054808411156109545763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a36109a5338484610eb2565b50600192915050565b5f80546109ba90611801565b80601f01602080910402602001604051908101604052809291908181526020018280546109e690611801565b8015610a315780601f10610a0857610100808354040283529160200191610a31565b820191905f5260205f20905b815481529060010190602001808311610a1457829003601f168201915b505050505081565b6060600380546106c090611801565b5f610a516106b1565b80519060200120905084421115610a6f57631a15a3cc5f526004601cfd5b6040518860601b60601c98508760601b60601c975065383775081901600e52885f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f83528360208401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528a60208401528960408401528860608401528060808401528760a084015260c08320604e526042602c205f528660ff16602052856040528460605260208060805f60015afa8b3d5114610b775763ddafbaef5f526004601cfd5b019055777f5e9f20000000000000000000000000000000000000000088176040526034602c2087905587897f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f606052505050505050565b604080516080810182525f808252602082018190529181018290526060810191909152610c17610c126805345cdf77eb68f44c5490565b611235565b905090565b306001600160a01b03831603610c5557604051634ae889f760e01b81526001600160a01b03831660048201526024015b60405180910390fd5b6006546001600160a01b03161580610c7657506007546001600160a01b0316155b15610d7b576001600160a01b037f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b163b15610d7b57604051639efa809760e01b81523060048201525f9081906001600160a01b037f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b1690639efa8097906024016040805180830381865afa158015610d10573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d349190611839565b600680546001600160a01b039283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216179091556007805492909316911617905550505b6006546001600160a01b03848116911614801581610da55750600754600160a01b900460ff166001145b8015610de157506387a211a2600c9081527f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b5f52602090205415155b8015610df757506006546001600160a01b031615155b8015610e6b5750600654604080516318160ddd60e01b815290515f926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa158015610e45573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e699190611866565b115b8015610e8557506007546001600160a01b03858116911614155b8015610e9f57506007546001600160a01b03848116911614155b15610eac57610eac611306565b50505050565b6001600160a01b038216158015610ed957506387a211a2600c9081525f8052602090205415155b15610f0a5760405163281a1b1360e01b81526001600160a01b03808516600483015283166024820152604401610c4c565b816001600160a01b0316836001600160a01b031603610f2857505050565b7f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b6001600160a01b0316826001600160a01b03161480610f9957507f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b6001600160a01b0316836001600160a01b0316145b15610fa357505050565b6006546001600160a01b0384811691811691821491908416145f610fd1610c126805345cdf77eb68f44c5490565b90508215611033576020810151815161ffff90811691161480159061103157325f9081526005602052604090205443900361101f57604051634c5daa2960e01b815260040160405180910390fd5b325f9081526005602052604090204390555b505b5f838061103d5750825b1561111b578380156110535750815161ffff1615155b156110665750805161ffff16840261108c565b82801561107a57505f826020015161ffff16115b1561108c5750602081015161ffff1684025b801561111b576110c1867f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b6127108404611481565b60408051835161ffff90811682526020808601518216908301528483015116818301526060808501519082015290517f34d998f779946ab3611f8616d6e56d2e843ad503dd4778fe03e9636d1d58cb949181900360800190a15b82611165575f8260600151611141886387a211a2600c9081525f91909152602090205490565b119050801561116357604051630b3aaf3960e21b815260040160405180910390fd5b505b50505050505050565b611179825f83610c1c565b6387a211a2600c52815f526020600c208054808311156111a05763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f8181526001600160a01b0383167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a361090e825f83610eb2565b81602052637f5e9f20600c52825f526034600c208054600181011561122e5780831115611228576313be252b5f526004601cfd5b82810382555b5050505050565b604080516080810182525f8082526020820181905291810182905260608101829052906112827f0000000000000000000000000000000000000000000000000000000065abb77b4261187d565b90505f61128e8261150b565b905060405180608001604052806112a484611578565b61ffff1681526020017f000000000000000000000000000000000000000000000000000000000000012c61ffff1681526020018261ffff1681526020016112f7868461ffff16612710910261270f010490565b9052949350505050565b505050565b6007805460ff60a01b1916740200000000000000000000000000000000000000001790556387a211a2600c9081527f000000000000000000000000b51b7d01c94ceff686e6463ce1ea461c2bb8276b5f81905260209091205460405163f13edc1b60e01b815260048101919091526001600160a01b039091169063f13edc1b906024016020604051808303815f875af19250505080156113c3575060408051601f3d908101601f191682019092526113c09181019061189c565b60015b611435576113cf6118bb565b806308c379a00361142b57506113e361190d565b806113ee575061142d565b7f5e1ac124bd79951148ee4b0fd8f7344385b3143569c90ff8751202be1b5c6c968160405161141d9190611658565b60405180910390a15061146c565b505b3d5f803e3d5ffd5b60405181151581527f82b536b864914f05b9c8b19c843619426f7caeda16583d3fa72144110af6159a9060200160405180910390a1505b6007805460ff60a01b1916600160a01b179055565b61148c838383610c1c565b8260601b6387a211a28117600c526020600c208054808411156114b65763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c8160601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a350611301838383610eb2565b5f61078b827f00000000000000000000000000000000000000000000000000000000001275007f00000000000000000000000000000000000000000000000000000000000000c87f0000000000000000000000000000000000000000000000000000000000000dac6115e5565b5f61078b827f0000000000000000000000000000000000000000000000000000000000000e107f0000000000000000000000000000000000000000000000000000000000000bb87f000000000000000000000000000000000000000000000000000000000000012c611619565b5f8385106115f4575080611611565b50670de0b6b3a76400008282038582028590048002829004020482015b949350505050565b5f838510611628575080611611565b81670de0b6b3a7640000868102869004800281900481038502048181111561164e578091505b5095945050505050565b5f602080835283518060208501525f5b8181101561168457858101830151858201604001528201611668565b505f604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b03811681146108e7575f80fd5b5f80604083850312156116c9575f80fd5b82356116d4816116a4565b946020939093013593505050565b5f805f606084860312156116f4575f80fd5b83356116ff816116a4565b9250602084013561170f816116a4565b929592945050506040919091013590565b5f60208284031215611730575f80fd5b5035919050565b5f60208284031215611747575f80fd5b8135611752816116a4565b9392505050565b5f805f805f805f60e0888a03121561176f575f80fd5b873561177a816116a4565b9650602088013561178a816116a4565b95506040880135945060608801359350608088013560ff811681146117ad575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b5f80604083850312156117db575f80fd5b82356117e6816116a4565b915060208301356117f6816116a4565b809150509250929050565b600181811c9082168061181557607f821691505b60208210810361183357634e487b7160e01b5f52602260045260245ffd5b50919050565b5f806040838503121561184a575f80fd5b8251611855816116a4565b60208401519092506117f6816116a4565b5f60208284031215611876575f80fd5b5051919050565b8181038181111561078b57634e487b7160e01b5f52601160045260245ffd5b5f602082840312156118ac575f80fd5b81518015158114611752575f80fd5b5f60033d11156118d15760045f803e505f5160e01c5b90565b601f8201601f1916810167ffffffffffffffff8111828210171561190657634e487b7160e01b5f52604160045260245ffd5b6040525050565b5f60443d101561191a5790565b6040516003193d81016004833e81513d67ffffffffffffffff816024840111818411171561194a57505050505090565b82850191508151818111156119625750505050505090565b843d870101602082850101111561197c5750505050505090565b61198b602082860101876118d4565b50909594505050505056fea26469706673582212207487d9ffbfdb2cec14058d176956fc915a0b1be57a6372596b27795b29232f2d64736f6c63430008160033

Verified Source Code Full Match

Compiler: v0.8.22+commit.4fc1097e EVM: shanghai Optimization: Yes (825 runs)
ERC20.sol 546 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
///   minting and transferring zero tokens, as well as self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
///   the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The total supply has overflowed.
    error TotalSupplyOverflow();

    /// @dev The allowance has overflowed.
    error AllowanceOverflow();

    /// @dev The allowance has underflowed.
    error AllowanceUnderflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Insufficient allowance.
    error InsufficientAllowance();

    /// @dev The permit is invalid.
    error InvalidPermit();

    /// @dev The permit has expired.
    error PermitExpired();

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

    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

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

    /// @dev The storage slot for the total supply.
    uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;

    /// @dev The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _BALANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;

    /// @dev The allowance slot of (`owner`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;

    /// @dev The nonce slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _NONCES_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let nonceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _NONCES_SLOT_SEED = 0x38377508;

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

    /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
    uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("1")`.
    bytes32 private constant _VERSION_HASH =
        0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
    bytes32 private constant _PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

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

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

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

    /// @dev Returns the decimals places of the token.
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

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

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_TOTAL_SUPPLY_SLOT)
        }
    }

    /// @dev Returns the amount of tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// Emits a {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
        }
        return true;
    }

    /// @dev Transfer `amount` tokens from the caller to `to`.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    ///
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(msg.sender, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, caller())
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(msg.sender, to, amount);
        return true;
    }

    /// @dev Transfers `amount` tokens from `from` to `to`.
    ///
    /// Note: Does not update the allowance if it is the maximum uint256 value.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the allowance slot and load its value.
            mstore(0x20, caller())
            mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
        return true;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EIP-2612                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev For more performance, override to return the constant value
    /// of `keccak256(bytes(name()))` if `name()` will never change.
    function _constantNameHash() internal view virtual returns (bytes32 result) {}

    /// @dev Returns the current nonce for `owner`.
    /// This value is used to compute the signature for EIP-2612 permit.
    function nonces(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the nonce slot and load its value.
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
    /// authorized by a signed approval by `owner`.
    ///
    /// Emits a {Approval} event.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the block timestamp is greater than `deadline`.
            if gt(timestamp(), deadline) {
                mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                revert(0x1c, 0x04)
            }
            let m := mload(0x40) // Grab the free memory pointer.
            // Clean the upper 96 bits.
            owner := shr(96, shl(96, owner))
            spender := shr(96, shl(96, spender))
            // Compute the nonce slot and load its value.
            mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            let nonceValue := sload(nonceSlot)
            // Prepare the domain separator.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            mstore(0x2e, keccak256(m, 0xa0))
            // Prepare the struct hash.
            mstore(m, _PERMIT_TYPEHASH)
            mstore(add(m, 0x20), owner)
            mstore(add(m, 0x40), spender)
            mstore(add(m, 0x60), value)
            mstore(add(m, 0x80), nonceValue)
            mstore(add(m, 0xa0), deadline)
            mstore(0x4e, keccak256(m, 0xc0))
            // Prepare the ecrecover calldata.
            mstore(0x00, keccak256(0x2c, 0x42))
            mstore(0x20, and(0xff, v))
            mstore(0x40, r)
            mstore(0x60, s)
            let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
            // If the ecrecover fails, the returndatasize will be 0x00,
            // `owner` will be checked if it equals the hash at 0x00,
            // which evaluates to false (i.e. 0), and we will revert.
            // If the ecrecover succeeds, the returndatasize will be 0x20,
            // `owner` will be compared against the returned address at 0x20.
            if iszero(eq(mload(returndatasize()), owner)) {
                mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                revert(0x1c, 0x04)
            }
            // Increment and store the updated nonce.
            sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
            // Compute the allowance slot and store the value.
            // The `owner` is already at slot 0x20.
            mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
            sstore(keccak256(0x2c, 0x34), value)
            // Emit the {Approval} event.
            log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            result := keccak256(m, 0xa0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(address(0), to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
            let totalSupplyAfter := add(totalSupplyBefore, amount)
            // Revert if the total supply overflows.
            if lt(totalSupplyAfter, totalSupplyBefore) {
                mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                revert(0x1c, 0x04)
            }
            // Store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(address(0), to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Burns `amount` tokens from `from`, reducing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, address(0), amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, from)
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Subtract and store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
            // Emit the {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        _afterTokenTransfer(from, address(0), amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Moves `amount` of tokens from `from` to `to`.
    function _transfer(address from, address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL ALLOWANCE FUNCTIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and load its value.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let owner_ := shl(96, owner)
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HOOKS TO OVERRIDE                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any transfer of tokens.
    /// This includes minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /// @dev Hook that is called after any transfer of tokens.
    /// This includes minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
CheezburgerBun.sol 270 lines
// SPDX-License-Identifier: MIT
// This contract was deployed using the Cheezburger Factory.
// You can check the tokenomics, website and social from the public read functions.
pragma solidity ^0.8.22;

import {ERC20} from "solady/src/tokens/ERC20.sol";
import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {IUniswapV2Factory} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {CheezburgerDynamicTokenomics} from "./CheezburgerDynamicTokenomics.sol";
import {ICheezburgerFactory} from "./interfaces/ICheezburgerFactory.sol";

contract CheezburgerBun is CheezburgerDynamicTokenomics, ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    error TransferToZeroAddress(address from, address to);
    error TransferToToken(address to);
    error TransferMaxTokensPerWallet();
    error OnlyOneBuyPerBlockAllowed();
    error CannotReceiveEtherDirectly();

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

    event LiquiditySwapSuccess(bool success);
    event LiquiditySwapFailed(string reason);

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

    string public FACTORY_VERSION = "Cheddar (1.1)";
    string private _name;
    string private _symbol;
    string private _website;
    string private _social;
    address public constant owner = address(0);
    mapping(address => uint256) private _holderLastBuyTimestamp;

    ICheezburgerFactory public immutable factory =
        ICheezburgerFactory(msg.sender);
    IUniswapV2Pair public pair;
    IUniswapV2Router02 public router;

    uint8 internal isSwapping = 1;

    constructor(
        TokenCustomization memory _customization,
        DynamicSettings memory _fees,
        DynamicSettings memory _wallet
    ) CheezburgerDynamicTokenomics(_fees, _wallet) {
        _name = _customization.name;
        _symbol = _customization.symbol;
        _website = _customization.website;
        _social = _customization.social;
        _mint(address(factory), _customization.supply * (10 ** decimals()));
    }

    /// @dev Prevents direct Ether transfers to contract
    receive() external payable {
        revert CannotReceiveEtherDirectly();
    }

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

    function name() public view virtual override returns (string memory) {
        return _name;
    }

    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    function website() public view returns (string memory) {
        return _website;
    }

    function social() public view returns (string memory) {
        return _social;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);

        if (to == address(this)) {
            revert TransferToToken(to);
        }

        // Cache pair internally if available
        if (address(pair) == address(0) || address(router) == address(0)) {
            if (address(factory).code.length > 0) {
                (IUniswapV2Router02 _router, IUniswapV2Pair _pair) = factory
                    .burgerRegistryRouterOnly(address(this));
                pair = _pair;
                router = _router;
            }
        }

        bool isBuying = from == address(pair);

        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        LIQUIDITY SWAP                      */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        if (
            !isBuying &&
            isSwapping == 1 &&
            balanceOf(address(factory)) > 0 &&
            address(pair) != address(0) &&
            pair.totalSupply() > 0 &&
            from != address(router) &&
            to != address(router)
        ) {
            doLiquiditySwap();
        }
    }

    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        super._afterTokenTransfer(from, to, amount);

        // Must use burn() to burn tokens
        if (to == address(0) && balanceOf(address(0)) > 0) {
            revert TransferToZeroAddress(from, to);
        }

        // Don't look after self transfers
        if (from == to) {
            return;
        }

        // Ignore Factory-related txs
        if (to == address(factory) || from == address(factory)) {
            return;
        }

        bool isBuying = from == address(pair);
        bool isSelling = to == address(pair);
        DynamicTokenomicsStruct memory tokenomics = _getTokenomics(
            totalSupply()
        );

        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          TXS LIMITS                        */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

        if (isBuying) {
            bool buyFeeStillDecreasing = tokenomics.earlyAccessPremium !=
                tokenomics.sellFee;
            if (buyFeeStillDecreasing) {
                if (_holderLastBuyTimestamp[tx.origin] == block.number) {
                    revert OnlyOneBuyPerBlockAllowed();
                }
                _holderLastBuyTimestamp[tx.origin] = block.number;
            }
        }

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

        uint256 feeAmount = 0;
        if (isBuying || isSelling) {
            unchecked {
                if (isBuying && tokenomics.earlyAccessPremium > 0) {
                    feeAmount = amount * tokenomics.earlyAccessPremium;
                } else if (isSelling && tokenomics.sellFee > 0) {
                    feeAmount = amount * tokenomics.sellFee;
                }
                if (feeAmount > 0) {
                    super._transfer(to, address(factory), feeAmount / 10000);
                    emit AppliedTokenomics(tokenomics);
                }
            }
        }

        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        WALLET LIMITS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

        if (!isSelling) {
            unchecked {
                bool walletExceedLimits = balanceOf(to) >
                    tokenomics.maxTokensPerWallet;
                if (walletExceedLimits) {
                    revert TransferMaxTokensPerWallet();
                }
            }
        }
    }

    function doLiquiditySwap() private lockSwap {
        try factory.beforeTokenTransfer(balanceOf(address(factory))) returns (
            bool result
        ) {
            emit LiquiditySwapSuccess(result);
        } catch Error(string memory reason) {
            emit LiquiditySwapFailed(reason);
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// Burns tokens from the caller.
    ///
    /// @dev Burns `amount` tokens from the caller.
    ///
    /// See {ERC20-_burn}.
    function burn(uint256 amount) external {
        _burn(msg.sender, amount);
    }

    /// Burns tokens from an account's allowance.
    ///
    /// @dev Burns `amount` tokens from `account`, deducting from the caller's
    /// allowance.
    ///
    /// See {ERC20-_burn} and {ERC20-allowance}.
    ///
    /// Requirements:
    ///
    /// - the caller must have allowance for ``accounts``'s tokens of at least
    /// `amount`.
    function burnFrom(address account, uint256 amount) external {
        _spendAllowance(account, msg.sender, amount);
        _burn(account, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Get current dynamic tokenomics
    /// @return DynamicTokenomics struct with current values
    /// @notice Values will change dynamically based on configured durations and percentages
    function getTokenomics()
        external
        view
        returns (DynamicTokenomicsStruct memory)
    {
        return _getTokenomics(totalSupply());
    }

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

    modifier lockSwap() {
        isSwapping = 2;
        _;
        isSwapping = 1;
    }
}
CheezburgerStructs.sol 50 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {IUniswapV2Factory} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

interface CheezburgerStructs {
    /// @dev Settings for customizing the token
    /// @param name The name of the token
    /// @param symbol The symbol for the token
    /// @param website The website associated with the token
    /// @param social A social media link associated with the token
    /// @param supply The max supply of the token
    struct TokenCustomization {
        string name;
        string symbol;
        string website;
        string social;
        uint256 supply;
    }

    /// @dev Settings for dynamic fees that change over time
    /// @param duration The duration over which the rate changes
    /// @param percentStart The starting percentage rate
    /// @param percentEnd The ending percentage rate
    struct DynamicSettings {
        uint256 duration;
        uint16 percentStart;
        uint16 percentEnd;
    }

    /// @dev Settings for liquidity pool fees distributed to addresses
    /// @param feeThresholdPercent The percentage threshold that triggers liquidity swaps
    /// @param feeAddresses The addresses receiving distributed fee amounts
    /// @param feePercentages The percentage fee amounts for each address
    struct LiquiditySettings {
        uint8 feeThresholdPercent;
        address[] feeAddresses;
        uint8[] feePercentages;
    }

    /// @dev Settings for referrals. Referrals get commissions from fees whenever people uses the factory to deploy their token.
    /// @param feeReceiver The addresses receiving commissions
    /// @param feePercentage The percentage fee
    struct ReferralSettings {
        address feeReceiver;
        uint8 feePercentage;
    }
}
CheezburgerDynamicTokenomics.sol 190 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

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

abstract contract CheezburgerDynamicTokenomics is CheezburgerStructs {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    event AppliedTokenomics(DynamicTokenomicsStruct tokenomics);

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

    uint256 public immutable launchStart = block.timestamp;
    uint256 public immutable EARLY_ACCESS_PREMIUM_DURATION;
    uint16 public immutable EARLY_ACCESS_PREMIUM_START;
    uint16 public immutable EARLY_ACCESS_PREMIUM_END;
    uint16 public immutable SELL_FEE_END;
    uint256 public immutable MAX_WALLET_DURATION;
    uint16 public immutable MAX_WALLET_PERCENT_START;
    uint16 public immutable MAX_WALLET_PERCENT_END;

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

    struct DynamicTokenomicsStruct {
        uint16 earlyAccessPremium;
        uint16 sellFee;
        uint16 maxWalletPercentage;
        uint256 maxTokensPerWallet;
    }

    constructor(DynamicSettings memory _fee, DynamicSettings memory _wallet) {
        EARLY_ACCESS_PREMIUM_DURATION = _fee.duration;
        EARLY_ACCESS_PREMIUM_START = _fee.percentStart;
        EARLY_ACCESS_PREMIUM_END = _fee.percentEnd;
        SELL_FEE_END = _fee.percentEnd;
        MAX_WALLET_DURATION = _wallet.duration;
        MAX_WALLET_PERCENT_START = _wallet.percentStart;
        MAX_WALLET_PERCENT_END = _wallet.percentEnd;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Computes current tokenomics values
    /// @return DynamicTokenomics struct with current values
    /// @notice Values will change dynamically based on configured durations and percentages
    function _getTokenomics(
        uint256 _totalSupply
    ) internal view returns (DynamicTokenomicsStruct memory) {
        uint256 _elapsed = block.timestamp - launchStart;
        uint16 _maxWalletPercentage = _currentMaxWalletPercentage(_elapsed);
        return
            DynamicTokenomicsStruct({
                earlyAccessPremium: _currentBuyFeePercent(_elapsed),
                sellFee: SELL_FEE_END,
                maxWalletPercentage: _maxWalletPercentage,
                maxTokensPerWallet: _calculateMaxTokensPerWalletPrecisely(
                    _totalSupply,
                    _maxWalletPercentage
                )
            });
    }

    /// @dev Calculates max tokens per wallet more precisely than standard calculation
    /// @param _totalSupply Current total token supply
    /// @param _maxWalletPercentage Max wallet percentage in basis points (out of 10k)
    /// @return maxTokensPerWallet Precisely calculated max tokens per wallet
    /// @custom:optimizations Uses unchecked math for better gas efficiency
    function _calculateMaxTokensPerWalletPrecisely(
        uint256 _totalSupply,
        uint256 _maxWalletPercentage
    ) private pure returns (uint256) {
        unchecked {
            // Convert the percentage to a fraction and perform the multiplication last to maintain precision
            return (_totalSupply * _maxWalletPercentage + 9999) / 10000;
        }
    }

    /// @dev Gets current buy fee percentage based on elapsed time
    /// @return Current buy fee percentage
    function _currentBuyFeePercent(
        uint256 elapsed
    ) internal view returns (uint16) {
        return
            computeFeePercentage(
                elapsed,
                EARLY_ACCESS_PREMIUM_DURATION,
                EARLY_ACCESS_PREMIUM_START,
                EARLY_ACCESS_PREMIUM_END
            );
    }

    /// @dev Gets current max wallet percentage based on elapsed time
    /// @return Current max wallet percentage
    function _currentMaxWalletPercentage(
        uint256 elapsed
    ) internal view returns (uint16) {
        return
            computeMaxWalletPercentage(
                elapsed,
                MAX_WALLET_DURATION,
                MAX_WALLET_PERCENT_START,
                MAX_WALLET_PERCENT_END
            );
    }

    /// Computes fee percentage based on elapsed time, using an inverted quadratic curve.
    /// The fee percentage starts from a high value and decreases to a low value as time progresses.
    /// This creates a curve that starts fast and then slows down as the elapsed time approaches the total duration.
    ///
    /// @param elapsed The number of seconds that have passed since the launch.
    /// @param duration The total duration in seconds for the decrease.
    /// @param startPercent The starting fee percentage at the launch. Expressed in basis points where 1000 means 10%.
    /// @param endPercent The target fee percentage at the end of the duration. Expressed in basis points where 1000 means 10%.
    /// @return The current fee percentage, expressed in basis points where 1000 means 10%.
    function computeFeePercentage(
        uint256 elapsed,
        uint256 duration,
        uint16 startPercent,
        uint16 endPercent
    ) private pure returns (uint16) {
        if (elapsed >= duration) {
            return endPercent;
        }

        uint16 feePercent;
        /// @solidity memory-safe-assembly
        assembly {
            let scale := 0x0de0b6b3a7640000 // 10^18 in hexadecimal
            // Calculate the position on the curve, x, as a ratio of elapsed time to total duration
            let x := div(mul(elapsed, scale), duration)
            // Subtract squared x from scale to get the inverted position on the curve
            let xx := sub(scale, div(mul(x, x), scale))
            // Calculate delta as a proportion of startPercent scaled by the position on the curve
            let delta := div(mul(startPercent, xx), scale)
            // Ensure feePercent doesn't fall below endPercent
            feePercent := endPercent
            if gt(delta, endPercent) {
                feePercent := delta
            }
        }
        return feePercent;
    }

    /// Computes the maximum wallet percentage based on the elapsed time using a quadratic function.
    /// This function uses the progression of time to determine the maximum wallet percentage allowed,
    /// starting from the `startPercent` and progressively moving towards the `endPercent` in a quadratic manner.
    /// This creates a curve that starts slow and then accelerates as the elapsed time approaches the total duration.
    ///
    /// @param elapsed The number of seconds that have passed since the launch.
    /// @param duration The total duration in seconds for the quadratic progression.
    /// @param startPercent The starting wallet percentage at the launch. Expressed in basis points where 1000 means 10%.
    /// @param endPercent The target wallet percentage at the end of the duration. Expressed in basis points where 1000 means 10%.
    /// @return The current maximum wallet percentage, expressed in basis points where 1000 means 10%.
    function computeMaxWalletPercentage(
        uint256 elapsed,
        uint256 duration,
        uint16 startPercent,
        uint16 endPercent
    ) private pure returns (uint16) {
        // If elapsed time is greater than duration, return endPercent directly
        if (elapsed >= duration) {
            return endPercent;
        }

        uint16 walletPercent;
        /// @solidity memory-safe-assembly
        assembly {
            // Scale factor equivalent to 1 ether in Solidity to handle the fractional values
            let scale := 0x0de0b6b3a7640000 // 10^18 in hexadecimal
            // Calculate the position on the curve, x, as a ratio of elapsed time to total duration
            let x := div(mul(elapsed, scale), duration)
            // Square x to get the position on the curve
            let xx := div(mul(x, x), scale)
            // Calculate the range of percentages and scale by the position on the curve
            let range := sub(endPercent, startPercent)
            let delta := div(mul(range, xx), scale)
            // Add the starting percentage to get the final percentage
            walletPercent := add(startPercent, delta)
        }
        return walletPercent;
    }
}
ICheezburgerFactory.sol 16 lines
// SPDX-License-Identifier: MITz
pragma solidity ^0.8.22;

import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {CheezburgerStructs} from "../CheezburgerStructs.sol";

interface ICheezburgerFactory is CheezburgerStructs {
    function beforeTokenTransfer(
        uint256 _leftSideBalance
    ) external returns (bool);

    function burgerRegistryRouterOnly(
        address token
    ) external view returns (IUniswapV2Router02, IUniswapV2Pair);
}
IUniswapV2Pair.sol 52 lines
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;
}
IUniswapV2Factory.sol 17 lines
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;
}
IUniswapV2Router01.sol 95 lines
pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
IUniswapV2Router02.sol 44 lines
pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

Read Contract

DOMAIN_SEPARATOR 0x3644e515 → bytes32
EARLY_ACCESS_PREMIUM_DURATION 0x7632781b → uint256
EARLY_ACCESS_PREMIUM_END 0x0c88fc11 → uint16
EARLY_ACCESS_PREMIUM_START 0x38128305 → uint16
FACTORY_VERSION 0xbd382b40 → string
MAX_WALLET_DURATION 0xb4c2cb47 → uint256
MAX_WALLET_PERCENT_END 0x61fe25e5 → uint16
MAX_WALLET_PERCENT_START 0x209bc610 → uint16
SELL_FEE_END 0x6c56ea64 → uint16
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
decimals 0x313ce567 → uint8
factory 0xc45a0155 → address
getTokenomics 0xee2923d9 → tuple
launchStart 0xbf9f2fe7 → uint256
name 0x06fdde03 → string
nonces 0x7ecebe00 → uint256
owner 0x8da5cb5b → address
pair 0xa8aa1b31 → address
router 0xf887ea40 → address
social 0x7233f2be → string
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
website 0xbeb0a416 → string

Write Contract 6 functions

These functions modify contract state and require a wallet transaction to execute.

approve 0x095ea7b3
address spender
uint256 amount
returns: bool
burn 0x42966c68
uint256 amount
burnFrom 0x79cc6790
address account
uint256 amount
permit 0xd505accf
address owner
address spender
uint256 value
uint256 deadline
uint8 v
bytes32 r
bytes32 s
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool

Recent Transactions

No transactions found for this address