LaunchToken

0x44ac7802f6ec8ae2507f246b669a4158e8d3478a

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
2,901 bytes
ABI entries
33
15 read · 5 write
License
none

Contract information

Address
0x44ac7802f6ec8ae2507f246b669a4158e8d3478a
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0xfb4a88cb6f…1ffd2b8aa9

Token

Name
Hood Tracker
Symbol
HDTX
Decimals
18
Holders
364

Read contract (15)

SUPPLY()uint256
allowance(address, address)uint256
balanceOf(address)uint256
decimals()uint8
factory()address
isExempt(address)bool
launchTime()uint256
limitSeconds()uint256
limitsLifted()bool
maxTxAmount()uint256
maxWalletAmount()uint256
name()string
pool()address
symbol()string
totalSupply()uint256

Events (2)

ApprovalTransfer

ABI

[
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name_",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol_",
        "type": "string"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "address[]",
        "name": "exempt",
        "type": "address[]"
      },
      {
        "internalType": "uint256",
        "name": "limitSeconds_",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "maxTxDuringWindow",
        "type": "uint256"
      }
    ],
    "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"
  },
  {
    "inputs": [],
    "name": "MaxTxExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MaxWalletExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotFactory",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PoolAlreadySet",
    "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": "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": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "isExempt",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchTime",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "liftLimits",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "limitSeconds",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "limitsLifted",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxTxAmount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxWalletAmount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pool",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pool_",
        "type": "address"
      }
    ],
    "name": "setPool",
    "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.24;

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

/// @title LaunchToken
/// @notice Fixed-supply launchpad token with a Noxa-style anti-snipe window.
///
/// Reverse-engineered from live NOXA tokens on Robinhood Chain (RobinhoodieLife
/// 0xe70d…fcd1, CASHCAT 0x020b…18b4 — identical template bytecode). Their on-chain
/// state read exactly:
///   totalSupply      = 1_000_000_000e18   (1B, 18 dec)
///   maxWalletAmount  =    20_000_000e18   (2% of supply)
///   maxTxAmount      = 1_000_000_000e18   (per-tx cap; lifted to full supply post-window)
///
/// The guard lives inside the token's transfer path (not a separate module), and
/// is enforced only while `block.timestamp <= launchTime + limitSeconds`. The
/// window is measured in SECONDS, not blocks: on Arbitrum-stack chains (Robinhood
/// Chain included) `block.number` returns the parent-chain (Ethereum L1) block
/// number, so a block-denominated window silently runs ~120× longer than the L2
/// block rate suggests. Timestamps are L2-native and unambiguous.
///
/// The max-wallet cap applies to what a wallet ACCUMULATES (buys, transfers in).
/// The pool is excused from the cap as a recipient — otherwise every sell
/// (wallet → pool, which holds ~all supply) would revert and only exempt parties
/// could exit. Buys stay capped: pool → wallet still checks the recipient.
/// After the window, limits lift permanently; there is no early-lift override.
contract LaunchToken is ERC20 {
    uint256 public constant SUPPLY = 1_000_000_000e18; // 1B, matches Noxa
    uint8 private constant _DECIMALS = 18;

    /// @dev 2% of supply — the exact Noxa value (20,000,000e18).
    uint256 public maxWalletAmount;
    /// @dev Per-tx cap during the window. Lifted to SUPPLY when the window ends.
    uint256 public maxTxAmount;
    /// @dev Timestamp the token launched; the anti-snipe window is relative to it.
    uint256 public immutable launchTime;
    /// @dev Length of the anti-snipe window in seconds (~1h).
    uint256 public immutable limitSeconds;

    address public immutable factory;
    /// @dev The V3 pool. Exempt from the max-wallet cap as RECIPIENT only, so
    ///      sells always flow; set once by the factory right after pool creation
    ///      (the pool doesn't exist yet when this token's constructor runs).
    address public pool;
    /// @dev Addresses exempt from limits (factory, NPM, locker, dead, creator).
    mapping(address => bool) public isExempt;

    bool public limitsLifted;

    error MaxWalletExceeded();
    error MaxTxExceeded();
    error NotFactory();
    error PoolAlreadySet();

    constructor(
        string memory name_,
        string memory symbol_,
        address creator,
        address[] memory exempt,
        uint256 limitSeconds_,
        uint256 maxTxDuringWindow
    ) ERC20(name_, symbol_) {
        factory = msg.sender; // deployed by the LaunchFactory
        launchTime = block.timestamp;
        limitSeconds = limitSeconds_;

        maxWalletAmount = SUPPLY / 50; // 2%
        maxTxAmount = maxTxDuringWindow == 0 ? SUPPLY : maxTxDuringWindow;

        isExempt[msg.sender] = true; // factory
        isExempt[creator] = true;
        for (uint256 i = 0; i < exempt.length; i++) {
            isExempt[exempt[i]] = true;
        }

        // Entire supply is minted to the factory, which seeds it single-sided
        // into the V3 pool in the same transaction.
        _mint(msg.sender, SUPPLY);
    }

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

    /// @notice One-time pool registration, factory-only, called in the same
    ///         launch transaction. NOT a general exemption: the pool is only
    ///         excused from the max-wallet check as recipient (sell path).
    function setPool(address pool_) external {
        if (msg.sender != factory) revert NotFactory();
        if (pool != address(0)) revert PoolAlreadySet();
        pool = pool_;
    }

    /// @notice Anyone can lift limits once the window has elapsed. There is no
    ///         early-lift path — not even for the factory or its owner.
    function liftLimits() external {
        require(block.timestamp > launchTime + limitSeconds, "window still active");
        _lift();
    }

    function _lift() internal {
        if (limitsLifted) return;
        limitsLifted = true;
        maxWalletAmount = SUPPLY;
        maxTxAmount = SUPPLY;
    }

    /// @dev The anti-snipe guard. OZ v5 routes mint/burn/transfer through _update.
    function _update(address from, address to, uint256 value) internal override {
        // Auto-lift once the window passes so the first post-window transfer
        // clears the limits without needing an explicit call.
        if (!limitsLifted && block.timestamp > launchTime + limitSeconds) {
            _lift();
        }

        if (!limitsLifted && from != address(0)) {
            // Exempt parties (factory seed, NPM, locker, creator) bypass limits.
            bool exemptFlow = isExempt[from] || isExempt[to];
            if (!exemptFlow) {
                if (value > maxTxAmount) revert MaxTxExceeded();
                // The pool accumulates by design (it holds the curve): capping it
                // as recipient would block every sell. Wallets stay capped at 2%.
                if (to != pool && balanceOf(to) + value > maxWalletAmount) {
                    revert MaxWalletExceeded();
                }
            }
        }

        super._update(from, to, value);
    }
}
Chain explorer5093msChain node78ms