LaunchToken

0xfe9d3b1bec0635589df44a5b19586d8f2d5d4663

Verification
Verified
v0.8.30+commit.73712a01
Type
Contract
4,645 bytes
ABI entries
41
27 read · 3 write
License
none

Contract information

Address
0xfe9d3b1bec0635589df44a5b19586d8f2d5d4663
Chain
Robinhood Chain (4663)
Compiler
v0.8.30+commit.73712a01
Optimization
Enabled
Creation tx
0xc148648206…90610843d0

Token

Name
r/DOG
Symbol
r/DOG
Decimals
18
Holders
4

Read contract (27)

allowance(address, address)uint256
balanceOf(address)uint256
decimals()uint8
deployer()address
description()string
dexFactory()address
getTokenInfo()tuple
launchFactory()address
launchTime()uint256
liquidityPool()address
locker()address
logo()string
maxTxAmount()uint256
maxTxBps()uint16
maxTxLimit()uint256
maxWalletAmount()uint256
maxWalletBps()uint16
maxWalletLimit()uint256
name()string
pairToken()address
poolFee()uint24
positionManager()address
restrictionEndTime()uint256
restrictionSeconds()uint32
socials()tuple
symbol()string
totalSupply()uint256

Events (2)

ApprovalTransfer

ABI

[
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "internalType": "uint256",
            "name": "supply",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "pairToken",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "positionManager",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "dexFactory",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "locker",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "poolFee",
            "type": "uint24"
          },
          {
            "internalType": "uint16",
            "name": "maxWalletBps",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "maxTxBps",
            "type": "uint16"
          },
          {
            "internalType": "uint32",
            "name": "restrictionSeconds",
            "type": "uint32"
          }
        ],
        "internalType": "struct LauncherTypes.TokenConfig",
        "name": "config",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "address",
            "name": "deployer",
            "type": "address"
          },
          {
            "internalType": "string",
            "name": "logo",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "description",
            "type": "string"
          },
          {
            "components": [
              {
                "internalType": "string",
                "name": "telegram",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "twitter",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "discord",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "website",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "farcaster",
                "type": "string"
              }
            ],
            "internalType": "struct LauncherTypes.Socials",
            "name": "socials",
            "type": "tuple"
          }
        ],
        "internalType": "struct LauncherTypes.TokenInfo",
        "name": "info",
        "type": "tuple"
      }
    ],
    "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": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "maxTx",
        "type": "uint256"
      }
    ],
    "name": "MaxTxExceeded",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "balanceAfter",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "maxWallet",
        "type": "uint256"
      }
    ],
    "name": "MaxWalletExceeded",
    "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": [
      {
        "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": "deployer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "description",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "dexFactory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getTokenInfo",
    "outputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "deployer",
            "type": "address"
          },
          {
            "internalType": "string",
            "name": "logo",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "description",
            "type": "string"
          },
          {
            "components": [
              {
                "internalType": "string",
                "name": "telegram",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "twitter",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "discord",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "website",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "farcaster",
                "type": "string"
              }
            ],
            "internalType": "struct LauncherTypes.Socials",
            "name": "socials",
            "type": "tuple"
          }
        ],
        "internalType": "struct LauncherTypes.TokenInfo",
        "name": "",
        "type": "tuple"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchFactory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchTime",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "liquidityPool",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "locker",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "logo",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxTxAmount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxTxBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxTxLimit",
    "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": "maxWalletBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxWalletLimit",
    "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": "pairToken",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolFee",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "positionManager",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "restrictionEndTime",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "restrictionSeconds",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "socials",
    "outputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "telegram",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "twitter",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "discord",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "website",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "farcaster",
            "type": "string"
          }
        ],
        "internalType": "struct LauncherTypes.Socials",
        "name": "",
        "type": "tuple"
      }
    ],
    "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.30;

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

interface IUniswapV3Factory {
    function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
}

library LauncherTypes {
    struct Socials {
        string telegram;
        string twitter;
        string discord;
        string website;
        string farcaster;
    }

    struct TokenConfig {
        string name;
        string symbol;
        uint256 supply;
        address pairToken;
        address positionManager;
        address dexFactory;
        address locker; // FIX F-7 — fee-custody contract, excluded from anti-snipe limits
        uint24 poolFee;
        uint16 maxWalletBps;
        uint16 maxTxBps;
        uint32 restrictionSeconds; // anti-snipe window duration in SECONDS (real time)
    }

    struct TokenInfo {
        address deployer;
        string logo;
        string description;
        Socials socials;
    }
}

contract LaunchToken is ERC20 {
    error MaxWalletExceeded(address account, uint256 balanceAfter, uint256 maxWallet);
    error MaxTxExceeded(address account, uint256 amount, uint256 maxTx);

    LauncherTypes.TokenInfo private _tokenInfo;
    mapping(address account => uint256 amount) private _originBought;

    address public immutable launchFactory;
    address public immutable pairToken;
    address public immutable positionManager;
    address public immutable dexFactory;
    address public immutable locker; // FIX F-7 — excluded from anti-snipe so fee collection can't revert
    uint24 public immutable poolFee;
    uint16 public immutable maxWalletBps;
    uint16 public immutable maxTxBps;
    uint32 public immutable restrictionSeconds;
    // NB: on Robinhood Chain the `block.number` opcode returns the L1 block number (~12s cadence),
    // NOT an L2 per-block counter — far too coarse for any per-block logic — so this contract never
    // uses block.number. ALL anti-snipe is wall-clock (block.timestamp) based.
    uint256 public immutable launchTime; // block.timestamp at deploy (wall-clock, reliable)
    uint256 public immutable restrictionEndTime; // launchTime + restrictionSeconds
    uint256 public immutable maxWalletAmount;
    uint256 public immutable maxTxAmount;

    constructor(LauncherTypes.TokenConfig memory config, LauncherTypes.TokenInfo memory info)
        ERC20(config.name, config.symbol)
    {
        launchFactory = msg.sender;
        pairToken = config.pairToken;
        positionManager = config.positionManager;
        dexFactory = config.dexFactory;
        locker = config.locker;
        poolFee = config.poolFee;
        maxWalletBps = config.maxWalletBps;
        maxTxBps = config.maxTxBps;
        restrictionSeconds = config.restrictionSeconds;
        launchTime = block.timestamp;
        restrictionEndTime = block.timestamp + config.restrictionSeconds;
        maxWalletAmount = (config.supply * config.maxWalletBps) / 10_000;
        maxTxAmount = (config.supply * config.maxTxBps) / 10_000;

        _tokenInfo = info;

        _mint(msg.sender, config.supply);
    }

    function liquidityPool() public view returns (address) {
        return IUniswapV3Factory(dexFactory).getPool(address(this), pairToken, poolFee);
    }

    function maxWalletLimit() external view returns (uint256) {
        return maxWalletAmount;
    }

    function maxTxLimit() external view returns (uint256) {
        return maxTxAmount;
    }

    function getTokenInfo() external view returns (LauncherTypes.TokenInfo memory) {
        return _tokenInfo;
    }

    function deployer() external view returns (address) {
        return _tokenInfo.deployer;
    }

    function logo() external view returns (string memory) {
        return _tokenInfo.logo;
    }

    function description() external view returns (string memory) {
        return _tokenInfo.description;
    }

    function socials() external view returns (LauncherTypes.Socials memory) {
        return _tokenInfo.socials;
    }

    function _update(address from, address to, uint256 value) internal override {
        // Anti-snipe is wall-clock only (block.timestamp) and scoped to BUYS. The window is active
        // from launch (t0) through restrictionEndTime. We only meter/limit tokens LEAVING the pool
        // (from == pool) to a normal wallet, which means sells (to == pool), wallet-to-wallet
        // transfers, and any router/aggregator that holds tokens mid-swap are NEVER restricted — the
        // token can always be sold. block.number is deliberately not used (it is the L1 block number
        // on this chain, ~12s cadence, and the old `== launchBlock` check blocked ALL buys for up to
        // one full L1 block after launch, which honeypot scanners correctly flag as a transfer trap).
        if (value > 0 && block.timestamp <= restrictionEndTime) {
            address pool = liquidityPool();
            // FIX F-7 — the fee-custody `locker` is excluded (collectFees transfers pool->locker
            // during the window). FIX F-9 — the per-address buy meter keys on the RECIPIENT (`to`),
            // the same entity the max-wallet cap tracks, which is correct under AA/bundlers/aggregators
            // where tx.origin is a shared relayer.
            if (
                pool != address(0) &&
                from == pool &&
                to != launchFactory &&
                to != _tokenInfo.deployer &&
                to != locker
            ) {
                // Per-address cumulative buy cap (maxTx, +10% headroom).
                uint256 bought = _originBought[to] + value;
                _originBought[to] = bought;
                uint256 maxBuy = (maxTxAmount * 11_000) / 10_000;
                if (maxBuy == 0 || bought > maxBuy) {
                    revert MaxTxExceeded(to, bought, maxBuy);
                }
                // Per-wallet holding cap.
                uint256 balanceAfter = balanceOf(to) + value;
                if (balanceAfter > maxWalletAmount) {
                    revert MaxWalletExceeded(to, balanceAfter, maxWalletAmount);
                }
            }
        }

        super._update(from, to, value);
    }
}
Chain explorer6707msChain node82ms