RawbinFunV3
0x48fb55ee52914523773d619eb0fdfc5a687e0999
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
17,058 bytes
ABI entries
38
20 read · 8 write
License
none
Contract information
- Address
- 0x48fb55ee52914523773d619eb0fdfc5a687e0999
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0x3E551EfA01…74Db60aCe5
- Creation tx
- 0x82c82b3cd7…c42d9465ce
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (20)
FEE_TIMELOCK() → uint256
MAX_CREATION_FEE() → uint256
PLATFORM() → string
PLATFORM_URL() → string
TOTAL_SUPPLY() → uint256
allTokens(uint256) → address, address, address, string, uint256
allTokensLength() → uint256
creationFee() → uint256
defaultMigrationThreshold() → uint256
defaultVirtualEthReserve() → uint256
defaultVirtualTokenReserve() → uint256
feeRecipient() → address
feeRecipientEta() → uint256
isCurve(address) → bool
locker() → address
npm() → address
owner() → address
pendingFeeRecipient() → address
pendingOwner() → address
predictAddresses(address, string, string, bytes32) → address, address
Events (7)
CreationFeeUpdatedFeeRecipientCancelledFeeRecipientProposedFeeRecipientUpdatedOwnershipTransferStartedOwnershipTransferredTokenCreated
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "feeRecipient_",
"type": "address"
},
{
"internalType": "contract INonfungiblePositionManager",
"name": "npm_",
"type": "address"
},
{
"internalType": "uint256",
"name": "virtualEthReserve_",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "virtualTokenReserve_",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "migrationThreshold_",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "creationFee_",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "newFee",
"type": "uint256"
}
],
"name": "CreationFeeUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "cancelled",
"type": "address"
}
],
"name": "FeeRecipientCancelled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "next",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "eta",
"type": "uint256"
}
],
"name": "FeeRecipientProposed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "prev",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "next",
"type": "address"
}
],
"name": "FeeRecipientUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"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": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "curve",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "name",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "metadataURI",
"type": "string"
}
],
"name": "TokenCreated",
"type": "event"
},
{
"inputs": [],
"name": "FEE_TIMELOCK",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_CREATION_FEE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PLATFORM",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PLATFORM_URL",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOTAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "allTokens",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "curve",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "uint256",
"name": "createdAt",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "allTokensLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "applyFeeRecipient",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "cancelFeeRecipient",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "minTokensOut",
"type": "uint256"
}
],
"name": "createToken",
"outputs": [
{
"internalType": "address",
"name": "tokenAddr",
"type": "address"
},
{
"internalType": "address",
"name": "curveAddr",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "creationFee",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultMigrationThreshold",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultVirtualEthReserve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultVirtualTokenReserve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeRecipient",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeRecipientEta",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isCurve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "locker",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "npm",
"outputs": [
{
"internalType": "contract INonfungiblePositionManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingFeeRecipient",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "predictAddresses",
"outputs": [
{
"internalType": "address",
"name": "tokenAddr",
"type": "address"
},
{
"internalType": "address",
"name": "curveAddr",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "r",
"type": "address"
}
],
"name": "proposeFeeRecipient",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newFee",
"type": "uint256"
}
],
"name": "setCreationFee",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "./LaunchToken.sol";
import "./BondingCurveV3.sol";
import "./RawbinLocker.sol";
import "./interfaces/IUniswapV3.sol";
/// @title RawbinFunV3
/// @author Rawbin — the #1 launchpad on Robinhood Chain
/// @custom:website https://rawbin.fun
/// @custom:mission We're building a transparent launchpad. Verified contracts, no
/// hidden owner over tokens, no token tax, liquidity you can never rug. Read the
/// code and verify everything yourself. Every launch is a drop. — rawbin
/// @notice One call deploys a LaunchToken + BondingCurveV3. On migration the curve
/// seeds a Uniswap V3 pool and locks the position in the Locker forever, so
/// trading fees keep flowing (ETH -> platform, token -> creator). The
/// platform fee recipient is owner-controlled behind a 24h timelock.
contract RawbinFunV3 is Ownable2Step {
string public constant PLATFORM = "rawbin.fun";
string public constant PLATFORM_URL = "https://rawbin.fun";
uint256 public constant TOTAL_SUPPLY = 1_000_000_000 ether;
/// @notice The Rawbin mark, enforced on-chain: every token address must end in
/// 0x…999 (last 3 hex nibbles). A token that doesn't end in 999 was not
/// created here — the factory reverts otherwise, so fakes are impossible.
uint160 internal constant VANITY_MASK = 0xFFF;
uint160 internal constant VANITY_SUFFIX = 0x999;
/// @notice Uniswap V3 position manager (canonical deploy on Robinhood Chain).
INonfungiblePositionManager public immutable npm;
/// @notice The Locker that custodies every migrated position NFT. Deployed by
/// this factory in its own constructor and immutable thereafter — the
/// factory↔locker wiring can never be changed (no admin power over it).
address public immutable locker;
// Curve economics — fixed at deploy, no setters.
uint256 public immutable defaultVirtualEthReserve;
uint256 public immutable defaultVirtualTokenReserve;
uint256 public immutable defaultMigrationThreshold;
/// @notice Where platform fees go (creation fee + curve 1% + the V3 ETH-side
/// fees). Mutable but behind a 24h timelock (propose -> apply).
address public feeRecipient;
address public pendingFeeRecipient;
uint256 public feeRecipientEta; // 0 = nothing pending
uint256 public constant FEE_TIMELOCK = 24 hours;
uint256 public creationFee;
uint256 public constant MAX_CREATION_FEE = 0.005 ether;
/// @notice True for every BondingCurveV3 this factory deployed — the Locker uses
/// it to authorize lockPosition().
mapping(address => bool) public isCurve;
struct TokenInfo {
address token;
address curve;
address creator;
string metadataURI;
uint256 createdAt;
}
TokenInfo[] public allTokens;
event TokenCreated(
address indexed token,
address indexed curve,
address indexed creator,
string name,
string symbol,
string metadataURI
);
event CreationFeeUpdated(uint256 newFee);
event FeeRecipientProposed(address indexed next, uint256 eta);
event FeeRecipientUpdated(address indexed prev, address indexed next);
event FeeRecipientCancelled(address indexed cancelled);
constructor(
address feeRecipient_,
INonfungiblePositionManager npm_,
uint256 virtualEthReserve_,
uint256 virtualTokenReserve_,
uint256 migrationThreshold_,
uint256 creationFee_
) Ownable(feeRecipient_) {
require(feeRecipient_ != address(0), "feeRecipient=0");
require(virtualEthReserve_ > 0 && virtualTokenReserve_ > 0 && migrationThreshold_ > 0, "curve params=0");
require(creationFee_ <= MAX_CREATION_FEE, "fee > cap");
feeRecipient = feeRecipient_;
npm = npm_;
defaultVirtualEthReserve = virtualEthReserve_;
defaultVirtualTokenReserve = virtualTokenReserve_;
defaultMigrationThreshold = migrationThreshold_;
creationFee = creationFee_;
// Deploy the Locker now and wire it immutably — no setLocker, no window,
// no admin power to point at a different locker later.
locker = address(new RawbinLocker(npm_, IRawbinFactoryView(address(this))));
}
// ----------------------------------------------------------------- admin
function setCreationFee(uint256 newFee) external onlyOwner {
require(newFee <= MAX_CREATION_FEE, "fee > cap");
creationFee = newFee;
emit CreationFeeUpdated(newFee);
}
// --- feeRecipient change: 24h timelock (propose -> wait -> apply) ---
function proposeFeeRecipient(address r) external onlyOwner {
require(r != address(0), "feeRecipient=0");
pendingFeeRecipient = r;
feeRecipientEta = block.timestamp + FEE_TIMELOCK;
emit FeeRecipientProposed(r, feeRecipientEta);
}
function applyFeeRecipient() external onlyOwner {
// A 24h delay can't be meaningfully gamed by a validator's few-second
// timestamp drift — block.timestamp is the correct clock here.
// forge-lint: disable-next-line(block-timestamp)
require(feeRecipientEta != 0 && block.timestamp >= feeRecipientEta, "timelock");
emit FeeRecipientUpdated(feeRecipient, pendingFeeRecipient);
feeRecipient = pendingFeeRecipient;
pendingFeeRecipient = address(0);
feeRecipientEta = 0;
}
function cancelFeeRecipient() external onlyOwner {
emit FeeRecipientCancelled(pendingFeeRecipient);
pendingFeeRecipient = address(0);
feeRecipientEta = 0;
}
// -------------------------------------------------------------- create
/// @param salt Vanity salt ground off-chain so the token address ends in 0x…99.
/// Namespaced by msg.sender so creators can never collide or grief.
/// @param minTokensOut Slippage bound for the optional dev buy (value above the
/// creation fee buys tokens for the creator in the same tx).
function createToken(
string calldata name,
string calldata symbol,
string calldata metadataURI,
bytes32 salt,
uint256 minTokensOut
) external payable returns (address tokenAddr, address curveAddr) {
require(msg.value >= creationFee, "creation fee not paid");
{
bytes32 nsSalt = keccak256(abi.encodePacked(msg.sender, salt));
BondingCurveV3 curve = new BondingCurveV3{salt: nsSalt}(
defaultVirtualEthReserve,
defaultVirtualTokenReserve,
defaultMigrationThreshold
);
isCurve[address(curve)] = true; // set before any external call
LaunchToken token = new LaunchToken{salt: nsSalt}(name, symbol, TOTAL_SUPPLY, address(curve));
// Enforce the Rawbin mark on-chain: the token address must end in 0x…999.
// The off-chain grinder finds a salt that satisfies this; a bad salt reverts.
require(uint160(address(token)) & VANITY_MASK == VANITY_SUFFIX, "not a 0x...999 address");
curve.initialize(token, msg.sender);
token.initializeMetadata(metadataURI);
uint256 fee = creationFee;
if (fee > 0) {
(bool feeSent, ) = feeRecipient.call{value: fee}("");
require(feeSent, "fee transfer failed");
}
if (msg.value > fee) {
curve.buyFor{value: msg.value - fee}(msg.sender, minTokensOut);
}
tokenAddr = address(token);
curveAddr = address(curve);
}
allTokens.push(TokenInfo({
token: tokenAddr,
curve: curveAddr,
creator: msg.sender,
metadataURI: metadataURI,
createdAt: block.timestamp
}));
emit TokenCreated(tokenAddr, curveAddr, msg.sender, name, symbol, metadataURI);
}
function allTokensLength() external view returns (uint256) {
return allTokens.length;
}
/// @notice Predicts the CREATE2 addresses for a given creator + salt (source of
/// truth for the off-chain vanity grinder).
function predictAddresses(
address creator,
string calldata name,
string calldata symbol,
bytes32 salt
) external view returns (address tokenAddr, address curveAddr) {
bytes32 nsSalt = keccak256(abi.encodePacked(creator, salt));
bytes32 curveInitHash = keccak256(
abi.encodePacked(
type(BondingCurveV3).creationCode,
abi.encode(defaultVirtualEthReserve, defaultVirtualTokenReserve, defaultMigrationThreshold)
)
);
curveAddr = _create2Address(nsSalt, curveInitHash);
bytes32 tokenInitHash = keccak256(
abi.encodePacked(
type(LaunchToken).creationCode,
abi.encode(name, symbol, TOTAL_SUPPLY, curveAddr)
)
);
tokenAddr = _create2Address(nsSalt, tokenInitHash);
}
function _create2Address(bytes32 salt, bytes32 initCodeHash) internal view returns (address) {
return address(uint160(uint256(
keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash))
)));
}
}
Chain explorer5140msChain node82ms