LaunchToken

0x8f0df045f3612f9dc5f8c77278c15ce0cbe515a0

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
2,401 bytes
ABI entries
26
14 read · 3 write
License
none

Contract information

Address
0x8f0df045f3612f9dc5f8c77278c15ce0cbe515a0
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xdfba1a819e…8d13b01fdf

Token

Name
The first Robin hood
Symbol
TFRH
Decimals
6
Holders
133

Read contract (14)

TOTAL_SUPPLY()uint256
allowance(address, address)uint256
antiSnipeBlocks()uint64
balanceOf(address)uint256
decimals()uint8
factory()address
launchBlock()uint64
limitExempt(address)bool
limitsActive()bool
maxWallet()uint256
maxWalletBps()uint16
name()string
symbol()string
totalSupply()uint256

Events (2)

ApprovalTransfer

ABI

[
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name_",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol_",
        "type": "string"
      },
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "maxWalletBps_",
        "type": "uint16"
      },
      {
        "internalType": "uint64",
        "name": "antiSnipeBlocks_",
        "type": "uint64"
      },
      {
        "internalType": "address",
        "name": "quote_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "v3Factory_",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "poolFee_",
        "type": "uint24"
      },
      {
        "internalType": "address[]",
        "name": "exempt_",
        "type": "address[]"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "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"
  },
  {
    "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": "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": "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": [],
    "name": "antiSnipeBlocks",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "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": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchBlock",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "limitExempt",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "limitsActive",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxWalletBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "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"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/// @title LaunchToken
/// @notice The per-launch ERC-20. 6 decimals, a fixed 1,000,000,000 * 1e6 supply minted ONCE to the
///         factory at construction, no mint function (mint authority effectively renounced).
///
///         Optional NOXA-style anti-snipe: when `maxWalletBps > 0`, a max-wallet cap (in bps of total
///         supply) is enforced for the first `antiSnipeBlocks` blocks after launch — it applies during
///         the bonding curve / instant pool, so no single wallet can grab more than the cap at launch —
///         then it auto-lifts and the token trades freely. Enforced in OZ's `_update` hook.
///
///         IMMUTABLE BY DESIGN (v2): `maxWalletBps` and `antiSnipeBlocks` are immutables and the
///         exemption set is written ONLY in the constructor — there is NO setter of any kind on this
///         contract. Nothing (not the factory, not the platform owner) can retune the cap, extend the
///         window, or exempt an address after deployment. The restriction is fixed at launch and
///         self-expires by block number, after which this is a fully standard ERC-20. This zero-
///         callable-surface design is deliberate: a mutable cap + owner-controlled exemption list is
///         bytecode-indistinguishable from a honeypot to security scanners (Blockaid flagged v1
///         tokens as malicious on exactly that pattern), and the platform never needed the levers —
///         every exemption is knowable at construction time.
///
///         The launch/graduation Uniswap V3 pool cannot be passed in by the factory (its address
///         derives from THIS token's address — circular), so the token computes it itself via the
///         canonical CREATE2 formula: the pool for (this, quote, fee) under `v3Factory`. This also
///         fixes a v1 launch bug: the real NFPM mints by transferring factory → pool, and v1 never
///         exempted the pool, so any single-sided launch (or same-block full-curve graduation) inside
///         an active anti-snipe window reverted "max wallet (anti-snipe)".
///
///         `maxWalletBps == 0` ⇒ no limits at all (the Monad / back-compatible default); exemptions
///         are then skipped entirely (never read when limits are off) to save launch gas.
contract LaunchToken is ERC20 {
    uint8 private constant DECIMALS = 6;
    uint256 public constant TOTAL_SUPPLY = 1_000_000_000 * 10 ** 6; // 1B, 6dp = 1e15

    /// @dev Canonical Uniswap V3 pool init-code hash — verified against the live rh4663 deployment
    ///      (CeoCat pool recomputed byte-exact). Both target chains use canonical V3 deployments; a
    ///      chain with a non-standard hash needs a new token version, not a runtime knob.
    bytes32 private constant POOL_INIT_CODE_HASH =
        0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;

    address public immutable factory; // the launchpad — holds reserves, runs the curve + graduation
    uint16 public immutable maxWalletBps; // e.g. 300 = 3% of supply; 0 = no limit. Fixed at launch.
    uint64 public immutable antiSnipeBlocks; // anti-snipe window length, in blocks. Fixed at launch.
    uint64 public immutable launchBlock; // creation block — window = [launchBlock, launchBlock+window)
    /// @notice Constructor-only exemption set: factory / zero / dead / launch infrastructure (locker,
    ///         position manager, router), the creator, and the self-computed launch/graduation pool.
    ///         No setter exists — this mapping is immutable after construction.
    mapping(address => bool) public limitExempt;

    /// @param recipient the factory — it holds the for-sale (800M) + LP-held (200M) reserves.
    /// @param quote_ the pool counter-asset (WETH/WMON — or an ERC-20 quote like $ROBIN). Used only
    ///        to pre-compute the launch/graduation pool exemption; ignored when limits are off or
    ///        `v3Factory_` is zero.
    /// @param v3Factory_ the canonical Uniswap V3 factory this token's pool will be created under.
    ///        Zero ⇒ skip pool pre-exemption (e.g. graduation venue not configured yet).
    /// @param poolFee_ the fee tier of the launch/graduation pool.
    /// @param exempt_ launch-time infrastructure + the creator. Zero entries are skipped.
    constructor(
        string memory name_,
        string memory symbol_,
        address recipient,
        uint16 maxWalletBps_,
        uint64 antiSnipeBlocks_,
        address quote_,
        address v3Factory_,
        uint24 poolFee_,
        address[] memory exempt_
    ) ERC20(name_, symbol_) {
        require(maxWalletBps_ <= 10000, "bps");
        factory = recipient;
        maxWalletBps = maxWalletBps_;
        antiSnipeBlocks = antiSnipeBlocks_;
        launchBlock = uint64(block.number);

        // Exemptions only matter while limits are active; skip the SSTOREs entirely when they never
        // will be (maxWalletBps == 0 makes limitsActive() permanently false).
        if (maxWalletBps_ > 0) {
            limitExempt[recipient] = true; // factory holds ~100% of supply through the curve phase
            limitExempt[address(0)] = true; // mint / burn endpoints
            limitExempt[0x000000000000000000000000000000000000dEaD] = true; // LP / burn sink
            for (uint256 i; i < exempt_.length; ++i) {
                if (exempt_[i] != address(0)) limitExempt[exempt_[i]] = true;
            }
            // The launch/graduation pool: computed, not passed — its address derives from this
            // token's own address (CREATE2), which no caller can know before deployment.
            if (v3Factory_ != address(0) && quote_ != address(0)) {
                (address t0, address t1) =
                    address(this) < quote_ ? (address(this), quote_) : (quote_, address(this));
                address pool = address(
                    uint160(
                        uint256(
                            keccak256(
                                abi.encodePacked(
                                    hex"ff",
                                    v3Factory_,
                                    keccak256(abi.encode(t0, t1, poolFee_)),
                                    POOL_INIT_CODE_HASH
                                )
                            )
                        )
                    )
                );
                limitExempt[pool] = true;
            }
        }

        _mint(recipient, TOTAL_SUPPLY);
    }

    /// @notice Max tokens a non-exempt wallet may hold while limits are active (max ⇒ unlimited).
    function maxWallet() public view returns (uint256) {
        return maxWalletBps == 0 ? type(uint256).max : (TOTAL_SUPPLY * maxWalletBps) / 10000;
    }

    /// @notice True while the max-wallet cap is enforced (anti-snipe window still open).
    function limitsActive() public view returns (bool) {
        return maxWalletBps > 0 && block.number < uint256(launchBlock) + antiSnipeBlocks;
    }

    function decimals() public pure override returns (uint8) {
        return DECIMALS;
    }

    /// @dev Enforce the max wallet on the recipient during the anti-snipe window only; exempt addresses
    ///      (factory, pool, zero, dead, launch infra, creator) are never capped. After the window — or
    ///      when maxWalletBps==0 — the cap is inert. `maxWalletBps`/`antiSnipeBlocks` are immutables,
    ///      so the post-window hot path costs no storage reads.
    function _update(address from, address to, uint256 value) internal override {
        if (limitsActive() && !limitExempt[to]) {
            require(balanceOf(to) + value <= maxWallet(), "max wallet (anti-snipe)");
        }
        super._update(from, to, value);
    }
}
Chain explorer3516msChain node108ms