CultOfRobinhood
0x82a7003c0bec0ad36bdc2538c78ac1a22c4c148e
Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
5,308 bytes
ABI entries
35
19 read · 8 write
License
none
Contract information
- Address
- 0x82a7003c0bec0ad36bdc2538c78ac1a22c4c148e
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.28+commit.7893614a
- Optimization
- Enabled
- Creator
- 0x42538432BF…7061BEeE02
- Creation tx
- 0xcab2edf013…78aae5e9f3
Token
- Name
- CultOfRobinhood
- Symbol
- MERRYMEN
- Decimals
- 18
- Holders
- 1,014
- Market
- View token page →
Read contract (19)
AIRDROP_SUPPLY() → uint256
INITIAL_MAX_WALLET_BPS() → uint256
POSITION_MANAGER() → address
TOTAL_SUPPLY() → uint256
allowance(address, address) → uint256
balanceOf(address) → uint256
decimals() → uint8
isExempt(address) → bool
lpNFTId() → uint256
maxWallet() → uint256
name() → string
owner() → address
positionManager() → address
spawner() → address
symbol() → string
totalSupply() → uint256
tradingEnabled() → bool
uniswapV3Pool() → address
walletLimited() → bool
Events (7)
AirdropApprovalExemptSetMaxWalletSetOwnershipTransferredStartedTradingTransfer
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "address[]",
"name": "recipients",
"type": "address[]"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "recipients",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountEach",
"type": "uint256"
}
],
"name": "Airdrop",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "tokenOwner",
"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": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "ExemptSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "bps",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "maxWallet",
"type": "uint256"
}
],
"name": "MaxWalletSet",
"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": "address",
"name": "pool",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "lpNFTId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "positionManager",
"type": "address"
}
],
"name": "StartedTrading",
"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": "AIRDROP_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "INITIAL_MAX_WALLET_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POSITION_MANAGER",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOTAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "o",
"type": "address"
},
{
"internalType": "address",
"name": "s",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"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": "a",
"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": [
{
"internalType": "uint256",
"name": "bps",
"type": "uint256"
}
],
"name": "increaseWalletLimits",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isExempt",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lpNFTId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxWallet",
"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": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "contract INonfungiblePositionManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "recoverERC20",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "setExempt",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "spawner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pmgr",
"type": "address"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"components": [
{
"internalType": "address",
"name": "token0",
"type": "address"
},
{
"internalType": "address",
"name": "token1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint256",
"name": "amount0Desired",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1Desired",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount0Min",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1Min",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"internalType": "struct INonfungiblePositionManager.MintParams",
"name": "params",
"type": "tuple"
}
],
"name": "startTrading",
"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": [],
"name": "tradingEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"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"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "uniswapV3Pool",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "walletLimited",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
//
// =====================================================================
// ____ _ _ ___ __ ___ _ _ _ _
// / ___| _| | |_ / _ \ / _| / _ \ ___| |__ (_)_ __ | |__ ___ ___ | |
// | | | | | | | __| | | | | |_ | | | / _ \ '_ \| | '_ \| '_ \ / _ \ / _ \| |
// | |__| |_| | | |_ | |_| | _|| |_| | (_) | |_) | | | | | | | | (_) | (_) |_|
// \____\__,_|_|\__| \___/|_|(_)\___/ \___/|_.__/|_|_| |_|_| |_|\___/ \___/(_)
// =====================================================================
//
// CultOfRobinhood ($MERRYMEN) — cultofrobinhood.space
//
pragma solidity ^0.8.25;
import {INonfungiblePositionManager} from "./RobinhoodInterfaces.sol";
/**
* @title CultOfRobinhood ($MERRYMEN)
* @notice Fixed-supply ERC-20 with a genesis airdrop performed atomically in the
* constructor: `AIRDROP_SUPPLY` is split EQUALLY across the snapshot of
* top-1000 CASHCAT holders (see scripts/fetch-holders.js), and the
* remainder is minted to `owner` to seed liquidity / treasury.
*
* There is no mint after genesis — `totalSupply()` is permanently
* `TOTAL_SUPPLY`. The owner has NO power over balances; it can only
* `recoverERC20` (rescue unrelated tokens sent here by mistake) and manage
* the anti-whale cap (`increaseWalletLimits`, `setExempt`). Renounceable.
*
* Launches with a 1.5% max-wallet cap (INITIAL_MAX_WALLET_BPS); non-exempt
* addresses cannot receive tokens beyond that. The owner/deployer/contract
* are exempt so genesis + LP seeding work; the cap can only be loosened.
*
* Distribution (TOTAL_SUPPLY = 1,000,000,000):
* - AIRDROP_SUPPLY = 500,000,000 → split equally among `recipients`
* - remainder = 500,000,000 → held by THIS contract, seeded
* one-sided into a Uniswap V3 LP by `startTrading` (LP NFT → owner).
*/
contract CultOfRobinhood {
// ---- ERC-20 metadata ----
string public constant name = "CultOfRobinhood";
string public constant symbol = "MERRYMEN";
uint8 public constant decimals = 18;
// ---- supply constants ----
uint256 public constant TOTAL_SUPPLY = 1_000_000_000 * 1e18; // 1B
uint256 public constant AIRDROP_SUPPLY = 500_000_000 * 1e18; // 50% to holders
// ---- max-wallet (anti-whale) ----
// Limit is expressed in basis points of TOTAL_SUPPLY (10000 = 100% = unlimited).
uint256 public constant INITIAL_MAX_WALLET_BPS = 150; // start at 1.5%
uint256 public maxWallet; // max tokens a non-exempt address may hold
bool public walletLimited; // false once the limit is lifted to 100%
mapping(address => bool) public isExempt; // addresses immune to the limit
// Robinhood Chain (Uniswap V3) NonfungiblePositionManager. It routes MERRYMEN
// when the reserve is seeded into the LP, so it is exempted from the cap in the
// constructor (startTrading also exempts whatever pmgr it is given, plus the
// pool inline). The swap router needs NO exemption: V3 swaps settle directly
// between the pool and the user via the swap callback, so the router never
// custodies MERRYMEN.
address public constant POSITION_MANAGER = 0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3;
// ---- liquidity (one-sided Uniswap V3 seed) ----
bool public tradingEnabled; // set once by startTrading
INonfungiblePositionManager public positionManager;
address public uniswapV3Pool; // exempted inline at creation
uint256 public lpNFTId; // LP position NFT (owned by `owner`)
// ---- state ----
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
address public owner;
address public spawner; // deployer (may be the CultSpawn contract); can startTrading
// ---- events ----
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed tokenOwner, address indexed spender, uint256 value);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event Airdrop(uint256 recipients, uint256 amountEach);
event MaxWalletSet(uint256 bps, uint256 maxWallet);
event ExemptSet(address indexed account, bool exempt);
event StartedTrading(address indexed pool, uint256 lpNFTId, address positionManager);
/**
* @param _owner receives the non-airdropped remainder (LP / treasury).
* @param recipients the eligible top-1000 CASHCAT holders (must be unique,
* non-zero). Each receives `AIRDROP_SUPPLY / len` tokens.
*/
constructor(address _owner, address[] memory recipients) {
require(_owner != address(0), "owner=0");
uint256 n = recipients.length;
require(n > 0, "no recipients");
require(AIRDROP_SUPPLY >= n, "airdrop too small");
owner = _owner;
spawner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
// Anti-whale cap: start at 1.5% of supply. Exempt the parties that must
// legitimately hold or route more than the cap — the owner (holds the 50%
// reserve for LP), the deployer, and this contract. The owner can exempt
// the LP pool / router before seeding liquidity via setExempt().
maxWallet = (TOTAL_SUPPLY * INITIAL_MAX_WALLET_BPS) / 10000;
walletLimited = true;
_setExempt(_owner, true);
_setExempt(msg.sender, true); // deployer / spawner
_setExempt(address(this), true);
_setExempt(POSITION_MANAGER, true); // exempted up front so LP seeding never hits the cap
emit MaxWalletSet(INITIAL_MAX_WALLET_BPS, maxWallet);
// Equal split; integer division leaves dust that rolls into the remainder.
uint256 amountEach = AIRDROP_SUPPLY / n;
uint256 dropped;
for (uint256 i = 0; i < n; i++) {
address to = recipients[i];
require(to != address(0), "recipient=0");
// Overwrite (not +=): the snapshot is deduped, so a first-write model
// makes an accidental duplicate a no-op rather than a double-mint.
require(_balances[to] == 0, "duplicate");
_balances[to] = amountEach;
emit Transfer(address(0), to, amountEach);
unchecked { dropped += amountEach; }
}
// The undropped remainder (the 500M LP reserve + rounding dust) is held by
// THIS contract, to be seeded one-sided into the Uniswap V3 LP by
// startTrading(). address(this) is exempt, so it is unaffected by the cap.
uint256 remainder = TOTAL_SUPPLY - dropped;
_balances[address(this)] += remainder;
emit Transfer(address(0), address(this), remainder);
_totalSupply = TOTAL_SUPPLY;
emit Airdrop(n, amountEach);
}
// ---- ERC-20 views ----
function totalSupply() external view returns (uint256) { return _totalSupply; }
function balanceOf(address a) external view returns (uint256) { return _balances[a]; }
function allowance(address o, address s) external view returns (uint256) { return _allowances[o][s]; }
// ---- ERC-20 core ----
function transfer(address to, uint256 amount) external returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
function approve(address spender, uint256 amount) external returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 a = _allowances[from][msg.sender];
if (a != type(uint256).max) {
require(a >= amount, "allowance");
unchecked { _approve(from, msg.sender, a - amount); }
}
_transfer(from, to, amount);
return true;
}
function _transfer(address from, address to, uint256 amount) private {
require(from != address(0) && to != address(0), "zero");
uint256 bal = _balances[from];
require(bal >= amount, "balance");
// Anti-whale: a non-exempt recipient may not end up holding > maxWallet.
if (walletLimited && !isExempt[to]) {
require(_balances[to] + amount <= maxWallet, "max wallet");
}
unchecked {
_balances[from] = bal - amount;
_balances[to] += amount;
}
emit Transfer(from, to, amount);
}
function _approve(address o, address s, uint256 amount) private {
require(o != address(0) && s != address(0), "zero");
_allowances[o][s] = amount;
emit Approval(o, s, amount);
}
// ---- liquidity: one-sided Uniswap V3 seed (owner) ----
/// Create the MERRYMEN/WETH pool and seed the entire contract-held reserve
/// one-sided into it; the LP position NFT is minted to `owner`. Callable once.
/// @dev Mirrors LootProtocolToken.startTrading / LootSpawn.start. The caller
/// (an off-chain script) supplies the sorted token0/token1, the initial
/// `sqrtPriceX96`, and a tick range placed entirely on the MERRYMEN side
/// of the price so no WETH is required. The position manager and the pool
/// are exempt (this contract already is), so the seed transfer bypasses
/// the max-wallet cap.
function startTrading(
address pmgr,
uint160 sqrtPriceX96,
INonfungiblePositionManager.MintParams calldata params
) external {
require(msg.sender == owner || msg.sender == spawner, "not owner");
require(!tradingEnabled, "trading on");
require(params.recipient == owner, "recipient");
require(params.token0 != params.token1, "same token");
require(params.token0 == address(this) || params.token1 == address(this), "not this token");
// Seed exactly the reserve this contract holds (one-sided: only MERRYMEN).
uint256 lpAmount = _balances[address(this)];
require(lpAmount > 0, "no reserve");
if (params.token1 == address(this)) {
require(params.amount1Desired == lpAmount && params.amount0Desired == 0, "amounts");
} else {
require(params.amount0Desired == lpAmount && params.amount1Desired == 0, "amounts");
}
tradingEnabled = true;
positionManager = INonfungiblePositionManager(pmgr);
_setExempt(pmgr, true); // no-op if pmgr is the constant
_allowances[address(this)][pmgr] = lpAmount; // let the PM pull the reserve
address pool = positionManager.createAndInitializePoolIfNecessary(
params.token0, params.token1, params.fee, sqrtPriceX96
);
uniswapV3Pool = pool;
_setExempt(pool, true); // exempt the pool inline
(lpNFTId, , , ) = positionManager.mint(params);
emit StartedTrading(pool, lpNFTId, pmgr);
}
// ---- max-wallet controls (owner) ----
/// Raise the max-wallet cap. Monotonic: the new cap must be >= the current
/// one (owner can loosen, never tighten), and is expressed in basis points of
/// TOTAL_SUPPLY. Passing 10000 (100%) lifts the limit permanently.
function increaseWalletLimits(uint256 bps) external {
require(msg.sender == owner, "not owner");
require(walletLimited, "unlimited");
require(bps <= 10000, "bps>100%");
uint256 newMax = (TOTAL_SUPPLY * bps) / 10000;
require(newMax >= maxWallet, "can only raise");
maxWallet = newMax;
if (bps == 10000) walletLimited = false; // limit lifted for good
emit MaxWalletSet(bps, newMax);
}
/// Whitelist/mark an address immune to the max-wallet cap (e.g. the LP pool
/// after creation, extra routers, CEX deposit wallets). Owner only.
function setExempt(address account, bool exempt) external {
require(msg.sender == owner, "not owner");
_setExempt(account, exempt);
}
function _setExempt(address account, bool exempt) private {
require(account != address(0), "zero");
isExempt[account] = exempt;
emit ExemptSet(account, exempt);
}
// ---- ownership (recovery + limit control; no power over balances) ----
function transferOwnership(address newOwner) external {
require(msg.sender == owner, "not owner");
emit OwnershipTransferred(owner, newOwner); // newOwner=0 renounces
owner = newOwner;
}
/// Rescue unrelated ERC-20s sent to this contract by mistake. Cannot touch
/// $MERRYMEN balances of any holder — it only moves this contract's own
/// balance of some *other* token, which is normally zero.
function recoverERC20(address token, address to, uint256 amount) external {
require(msg.sender == owner, "not owner");
require(token != address(this), "self");
(bool ok, bytes memory data) = token.call(
abi.encodeWithSelector(0xa9059cbb, to, amount) // transfer(address,uint256)
);
require(ok && (data.length == 0 || abi.decode(data, (bool))), "recover failed");
}
}
Chain explorer4146msChain node90ms