Address Contract Verified
Address
0x02a75078d50c0259594d47f43C4E2dcC844F76d7
Balance
0 ETH
Nonce
1
Code Size
6604 bytes
Creator
0xb51B7D01...276B at tx 0x7a093c64...5b6568
Indexed Transactions
0
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