StonkToken
0x424fad0de2a6caa2becc28d3230cc4a672b0c466
Verification
Verified
v0.8.33+commit.64118f21
Type
Contract
4,604 bytes
ABI entries
51
18 read · 14 write
License
none
Contract information
- Address
- 0x424fad0de2a6caa2becc28d3230cc4a672b0c466
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.33+commit.64118f21
- Optimization
- Enabled
- Creator
- 0x5F26c189Fa…1b578f79D0
- Creation tx
- 0x324c4fe49a…5e710e6c42
Token
- Name
- StonkMiner
- Symbol
- STONK
- Decimals
- 18
- Holders
- 90
- Market
- View token page →
Read contract (18)
DEAD() → address
DEFAULT_ADMIN_ROLE() → bytes32
INITIAL_SUPPLY() → uint256
MAX_SUPPLY() → uint256
MINTER_ROLE() → bytes32
allowance(address, address) → uint256
balanceOf(address) → uint256
decimals() → uint8
getRoleAdmin(bytes32) → bytes32
hasRole(bytes32, address) → bool
initialMintDone() → bool
name() → string
owner() → address
pendingOwner() → address
remainingMintableSupply() → uint256
supportsInterface(bytes4) → bool
symbol() → string
totalSupply() → uint256
Events (8)
ApprovalOwnershipTransferStartedOwnershipTransferredRoleAdminChangedRoleGrantedRoleRevokedTokensBurnedTransfer
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "admin",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AccessControlBadConfirmation",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "bytes32",
"name": "neededRole",
"type": "bytes32"
}
],
"name": "AccessControlUnauthorizedAccount",
"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": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"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": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "previousAdminRole",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "newAdminRole",
"type": "bytes32"
}
],
"name": "RoleAdminChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleGranted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleRevoked",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newTotalSupply",
"type": "uint256"
}
],
"name": "TokensBurned",
"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": "DEAD",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "DEFAULT_ADMIN_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "INITIAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MINTER_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"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": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "burnFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
}
],
"name": "getRoleAdmin",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "grantRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "hasRole",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "initialSupply",
"type": "uint256"
}
],
"name": "initialMint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "initialMint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "initialMintDone",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "remainingMintableSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "callerConfirmation",
"type": "address"
}
],
"name": "renounceRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "revokeRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"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"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
/// @title StonkToken
/// @notice Clean, fair-launch ERC20 for the STONK grid-mining protocol.
/// @notice - NO trading tax, NO team allocation, NO growth-fund allocation. Every STONK beyond the
/// one-time genesis seed is mined through gameplay.
/// @notice - Maximum supply of 500,000 STONK (MAX_SUPPLY cap on outstanding supply).
/// @notice - Minting is restricted to MINTER_ROLE (held solely by the StonkHub emission governor).
/// @notice - Burns (including transfers to 0x…dEaD / zero address, which are redirected to real
/// burns) reduce totalSupply and reopen mint headroom — the circular emission model.
contract StonkToken is ERC20, ERC20Burnable, AccessControl, Ownable2Step {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
uint256 public constant MAX_SUPPLY = 500_000e18; // 500,000 STONK
/// @notice The conventional "dead" address. Transfers here (or to the zero address) are
/// redirected to a real burn so they reduce totalSupply and reopen mint headroom for
/// the circular emission model — rather than parking supply at 0xdead forever.
address public constant DEAD = 0x000000000000000000000000000000000000dEaD;
// Initial mint tracking
bool public initialMintDone; // Tracks if initial mint has been completed
uint256 public constant INITIAL_SUPPLY = 1_000e18; // Default genesis seed (used by initialMint(to)); initialMint(to, amount) can start smaller
event TokensBurned(address indexed account, uint256 amount, uint256 newTotalSupply);
constructor(address admin) ERC20("StonkMiner", "STONK") Ownable(admin) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
}
// ------------------------------------------------------------------
// Genesis + emission
// ------------------------------------------------------------------
/// @notice Initial mint of the default seed supply (INITIAL_SUPPLY) - once, owner-only.
/// @dev Prefer initialMint(to, amount) to launch with a smaller circulating seed and let the
/// rest emit naturally via the hub.
/// @param to Address to receive the seed supply
function initialMint(address to) external onlyOwner {
_initialMint(to, INITIAL_SUPPLY);
}
/// @notice Initial mint with a caller-chosen seed supply - once, owner-only.
/// @dev Lets a launch start with a small circulating supply (e.g. just enough to seed the first
/// liquidity pool); ongoing supply is emitted over time by the hub up to MAX_SUPPLY.
/// @param to Address to receive the seed supply
/// @param initialSupply Total tokens to mint at genesis (0 < initialSupply <= MAX_SUPPLY)
function initialMint(address to, uint256 initialSupply) external onlyOwner {
_initialMint(to, initialSupply);
}
/// @dev Shared one-time genesis mint. Total supply increases by exactly `initialSupply`.
function _initialMint(address to, uint256 initialSupply) private {
require(!initialMintDone, "initial mint already done");
require(to != address(0), "invalid address");
require(initialSupply > 0, "initialSupply=0");
require(initialSupply <= MAX_SUPPLY, "exceeds max supply");
initialMintDone = true;
_mint(to, initialSupply);
}
/// @notice Mint tokens - restricted to MINTER_ROLE (the StonkHub emission governor).
/// @dev Mints exactly `amount` to `to`. No side allocations of any kind.
/// @dev MAX_SUPPLY is a cap on outstanding supply: because burns decrease totalSupply(),
/// minting can continue up to MAX_SUPPLY even after burns (circular emission).
/// @param to Address to receive the minted tokens
/// @param amount Amount of tokens to mint
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
require(initialMintDone, "initial mint not done");
require(totalSupply() + amount <= MAX_SUPPLY, "exceeds max supply");
_mint(to, amount);
}
// ------------------------------------------------------------------
// Burns (all paths reduce totalSupply and emit TokensBurned)
// ------------------------------------------------------------------
/// @notice Burn tokens from caller - decreases total supply, reopening mint headroom.
function burn(uint256 value) public override {
super.burn(value);
emit TokensBurned(msg.sender, value, totalSupply());
}
/// @notice Burn tokens from account (requires allowance) - decreases total supply.
function burnFrom(address account, uint256 value) public override {
super.burnFrom(account, value);
emit TokensBurned(account, value, totalSupply());
}
/// @notice Transfers to the zero address or 0xdead are treated as burns.
/// @dev Standard ERC20 reverts on transfer-to-zero, and a transfer to 0xdead would otherwise
/// just park supply there without reducing totalSupply. We redirect both to a real burn so
/// "sending to dead" reopens mint headroom (circular emission), consistent with burn().
function transfer(address to, uint256 value) public override returns (bool) {
if (to == address(0) || to == DEAD) {
_burnToDead(_msgSender(), value);
return true;
}
return super.transfer(to, value);
}
/// @notice transferFrom to the zero address or 0xdead is treated as a burn (allowance spent).
function transferFrom(address from, address to, uint256 value) public override returns (bool) {
if (to == address(0) || to == DEAD) {
_spendAllowance(from, _msgSender(), value);
_burnToDead(from, value);
return true;
}
return super.transferFrom(from, to, value);
}
/// @dev Shared burn path for dead/zero-address transfers; emits the same TokensBurned event as
/// burn()/burnFrom() so all burns look identical off-chain.
function _burnToDead(address from, uint256 value) private {
_burn(from, value);
emit TokensBurned(from, value, totalSupply());
}
/// @notice Remaining mintable headroom under the outstanding-supply cap.
function remainingMintableSupply() external view returns (uint256) {
return MAX_SUPPLY - totalSupply();
}
}
Chain explorer4477msChain node89ms