V3LaunchToken
0x36e57597bd62d9bd7b0b61339fe71ec124438bcb
Verification
Verified
v0.8.30+commit.73712a01
Type
Contract
3,076 bytes
ABI entries
31
12 read · 5 write
License
none
Contract information
- Address
- 0x36e57597bd62d9bd7b0b61339fe71ec124438bcb
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.30+commit.73712a01
- Optimization
- Enabled
- Creator
- 0xa3ba5725B4…d6226A3D23
- Creation tx
- 0x36a9140d01…eb803984da
Token
- Name
- Stocks, Onchain
- Symbol
- STOCKS
- Decimals
- 18
- Holders
- 20
- Market
- View token page →
Read contract (12)
DOMAIN_SEPARATOR() → bytes32
allowance(address, address) → uint256
balanceOf(address) → uint256
decimals() → uint8
launcher() → address
maxWalletAmount() → uint256
name() → string
nonces(address) → uint256
pool() → address
restrictionsEndBlock() → uint256
symbol() → string
totalSupply() → uint256
Events (2)
ApprovalTransfer
ABI
[
{
"inputs": [
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AllowanceOverflow",
"type": "error"
},
{
"inputs": [],
"name": "AllowanceUnderflow",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientAllowance",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPermit",
"type": "error"
},
{
"inputs": [],
"name": "MaxWalletExceeded",
"type": "error"
},
{
"inputs": [],
"name": "Permit2AllowanceIsFixedAtInfinity",
"type": "error"
},
{
"inputs": [],
"name": "PermitExpired",
"type": "error"
},
{
"inputs": [],
"name": "PoolAlreadySet",
"type": "error"
},
{
"inputs": [],
"name": "TotalSupplyOverflow",
"type": "error"
},
{
"inputs": [],
"name": "Unauthorized",
"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": "amount",
"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": "amount",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [
{
"internalType": "bytes32",
"name": "result",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "result",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "result",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launcher",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"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": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "result",
"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": "pool",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "restrictionsEndBlock",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"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": "result",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"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": "amount",
"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 "solady/src/tokens/ERC20.sol";
/// @dev Pulled from the launcher during construction rather than passed as
/// constructor args: the CREATE2 initcode hash must depend only on
/// (name, symbol) so the frontend can mine a salt for the token1 ordering
/// without the result shifting with the inclusion block.
interface IV3TokenInitParams {
function tokenInitParams()
external
view
returns (uint256 supply, uint256 restrictionsEndBlock, uint256 maxWalletAmount);
}
/// @title V3LaunchToken
/// @notice The ERC20 behind every traditional (Uniswap v3) launch. Fixed supply,
/// minted in full to the launcher at construction, no owner, no taxes, no
/// mint, no pause. The only non-vanilla behavior is the anti-snipe window:
/// until `restrictionsEndBlock`, no wallet may hold more than
/// `maxWalletAmount` (the pool, launcher, and mint/burn are exempt). After
/// the window the token is behaviorally a stock ERC20 forever.
contract V3LaunchToken is ERC20 {
error Unauthorized();
error PoolAlreadySet();
error MaxWalletExceeded();
address public immutable launcher;
/// @notice First block at which the max-wallet restriction no longer applies.
/// Equal to the launch block when the window is configured to 0 blocks.
uint256 public immutable restrictionsEndBlock;
uint256 public immutable maxWalletAmount;
/// @notice The token's Uniswap v3 pool. Set once by the launcher in the launch
/// transaction (the pool address doesn't exist before the token does).
address public pool;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
launcher = msg.sender;
(uint256 supply, uint256 endBlock, uint256 maxWallet) =
IV3TokenInitParams(msg.sender).tokenInitParams();
restrictionsEndBlock = endBlock;
maxWalletAmount = maxWallet;
_mint(msg.sender, supply);
}
function name() public view override returns (string memory) {
return _name;
}
function symbol() public view override returns (string memory) {
return _symbol;
}
function setPool(address pool_) external {
if (msg.sender != launcher) revert Unauthorized();
if (pool != address(0)) revert PoolAlreadySet();
pool = pool_;
}
/// @dev Anti-snipe: bounded recipient balance during the opening window.
/// Exemptions: the pool (holds the whole supply as liquidity) and the
/// launcher, on both sides — it holds supply transiently at mint (`to`),
/// and it pays fees out to creators during the window (`from`), which must
/// not be capped or fee collection would brick until the window ends.
function _beforeTokenTransfer(address from, address to, uint256 amount)
internal
view
override
{
if (block.number >= restrictionsEndBlock) return;
if (from == launcher || to == launcher || to == pool || to == address(0)) return;
if (balanceOf(to) + amount > maxWalletAmount) revert MaxWalletExceeded();
}
}
Chain explorer1648msChain node80ms