LaunchToken
0xe9bb58664b2eec09bbbbae67bcd0643333dd6d7d
Verification
Verified
v0.8.35+commit.47b9dedd
Type
Contract
3,844 bytes
ABI entries
33
11 read · 4 write
License
none
Contract information
- Address
- 0xe9bb58664b2eec09bbbbae67bcd0643333dd6d7d
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.35+commit.47b9dedd
- Optimization
- Enabled
- Creator
- 0xFa6b1Da94a…5bF3Ad050c
- Creation tx
- 0x98666aeeb6…f497744673
Token
- Name
- Owlto Fun
- Symbol
- OF
- Decimals
- 18
- Holders
- 423
- Market
- View token page →
Read contract (11)
DOMAIN_SEPARATOR() → bytes32
TOTAL_SUPPLY() → uint256
allowance(address, address) → uint256
balanceOf(address) → uint256
decimals() → uint8
eip712Domain() → bytes1, string, string, uint256, address, bytes32, uint256[]
metadataHash() → bytes32
name() → string
nonces(address) → uint256
symbol() → string
totalSupply() → uint256
Events (3)
ApprovalEIP712DomainChangedTransfer
ABI
[
{
"inputs": [
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
},
{
"internalType": "bytes32",
"name": "metadataHash_",
"type": "bytes32"
},
{
"internalType": "address",
"name": "curve_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "ECDSAInvalidSignature",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "length",
"type": "uint256"
}
],
"name": "ECDSAInvalidSignatureLength",
"type": "error"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "ECDSAInvalidSignatureS",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "allowance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientAllowance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientBalance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "approver",
"type": "address"
}
],
"name": "ERC20InvalidApprover",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "ERC20InvalidReceiver",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "ERC20InvalidSender",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "ERC20InvalidSpender",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "ERC2612ExpiredSignature",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "signer",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "ERC2612InvalidSigner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "uint256",
"name": "currentNonce",
"type": "uint256"
}
],
"name": "InvalidAccountNonce",
"type": "error"
},
{
"inputs": [],
"name": "InvalidShortString",
"type": "error"
},
{
"inputs": [
{
"internalType": "string",
"name": "str",
"type": "string"
}
],
"name": "StringTooLong",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "EIP712DomainChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOTAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "eip712Domain",
"outputs": [
{
"internalType": "bytes1",
"name": "fields",
"type": "bytes1"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "version",
"type": "string"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"internalType": "address",
"name": "verifyingContract",
"type": "address"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256[]",
"name": "extensions",
"type": "uint256[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "metadataHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.35;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {ILaunchToken} from "./interfaces/ILaunchToken.sol";
/// @title LaunchToken — ROBBED_ launch token (spec §6.1, §8.3; contracts.md §2.1)
/// @notice Plain OpenZeppelin v5 `ERC20` + `ERC20Permit`. 18 decimals. Ownerless, no
/// mint/burn/hooks/taxes/blacklist. The entire fixed supply is minted exactly once, in the
/// constructor, to the owning `BondingCurve`. The only surface beyond stock OZ is the
/// immutable `metadataHash` commitment (§8.3) and the `TOTAL_SUPPLY` getter.
/// @dev Implements the frozen {ILaunchToken}. Design decisions (recorded for the security gate):
///
/// 1. Ownerless by construction — the contract inherits NO access-control mixin (`Ownable`/
/// `AccessControl` are not imported). Chosen over "inherit Ownable and renounce" because
/// renouncement is a post-deploy runtime action that can be forgotten or front-run, whereas
/// "no owner code at all" is a compile-time guarantee with a smaller attack surface (spec
/// §6.1 "ownerless"; §6.6 "owner can never touch token economics"). Proven by the unit test
/// asserting the `owner()` selector is absent (reverts).
///
/// 2. No `burn()`/`_burn`, no `mint()` beyond the single constructor `_mint`. OZ ERC20 exposes
/// no public mint/burn; we add none. Graduation "dust burn" (spec §12.13) is a plain
/// `transfer` to `0x…dEaD` performed by the migrator — the token itself is burn-free, so
/// `totalSupply()` is constant `1e27` forever (contracts.md §2.1 invariant). Proven by unit
/// tests calling the `mint`/`burn` selectors and asserting they are not present.
///
/// 3. `metadataHash` is `immutable`, set once in the constructor. Chosen over a storage
/// variable to make tamper-impossibility a bytecode property (no SSTORE path exists),
/// matching the §8.3 integrity-commitment requirement.
///
/// 4. `ERC20Permit(name_)` fixes the EIP-712 domain `version` to `"1"` (OZ v5 default) and uses
/// the token name as the domain name — the standard EIP-2612 layout wagmi/viem expect, so
/// `sellWithPermit` (Router, M1-9) and any wallet-side permit signing interoperate without a
/// bespoke domain. Verified by the permit unit test signing and spending an allowance.
///
/// Emits no events of its own; the constructor `_mint` emits the standard
/// `Transfer(address(0) → curve)`.
contract LaunchToken is ERC20, ERC20Permit, ILaunchToken {
/// @inheritdoc ILaunchToken
/// @dev `constant` (never an SSTORE target) — the fixed 1,000,000,000e18 supply (spec §6.4,
/// contracts.md §2.1). `CURVE_SUPPLY + LP_TOKEN_TRANCHE == TOTAL_SUPPLY` is asserted by the
/// factory/deploy script, not here (this contract owns only the supply total).
uint256 public constant override TOTAL_SUPPLY = 1_000_000_000e18;
/// @inheritdoc ILaunchToken
/// @dev `immutable`: no code path can change it after construction (§8.3 commitment).
bytes32 public immutable override metadataHash;
/// @param name_ Token name (length validated upstream by the factory, contracts.md §2.2).
/// @param symbol_ Ticker (length validated upstream by the factory).
/// @param metadataHash_ keccak256 of the canonicalized metadata JSON (§8.3). Non-zero enforced by
/// the factory (`ZeroMetadataHash`); stored verbatim, immutably.
/// @param curve_ The owning BondingCurve; receives the full `TOTAL_SUPPLY` at birth.
/// @dev Input validation (name/symbol length, non-zero hash, non-zero curve) lives in the factory
/// before deploy (contracts.md §2.1 "Errors: none of its own"), keeping this contract a
/// minimal, audit-trivial ERC20. The mint to `curve_` is the sole supply-creating action.
constructor(string memory name_, string memory symbol_, bytes32 metadataHash_, address curve_)
ERC20(name_, symbol_)
ERC20Permit(name_)
{
metadataHash = metadataHash_;
_mint(curve_, TOTAL_SUPPLY);
}
/// @inheritdoc IERC20Permit
/// @dev Pure disambiguation override: `nonces(address)` is inherited both from `ERC20Permit`
/// (concrete, backed by OZ `Nonces`) and from `IERC20Permit` via {ILaunchToken}. Solidity
/// requires an explicit override listing both bases; behaviour is unchanged (`super`
/// forwards to the OZ implementation). No new storage or logic is introduced.
function nonces(address owner) public view virtual override(ERC20Permit, IERC20Permit) returns (uint256) {
return super.nonces(owner);
}
}
Chain explorer1486msChain node79ms