TokenFactory
0xa60784a3d6d8c1e82cbbc959e14e205a528b3056
Verification
Verified
v0.8.25+commit.b61c2a91
Type
Contract
9,015 bytes
ABI entries
32
12 read · 8 write
License
none
Contract information
- Address
- 0xa60784a3d6d8c1e82cbbc959e14e205a528b3056
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.25+commit.b61c2a91
- Optimization
- Enabled
- Creator
- 0x8bC9dC2B64…0981616aF1
- Creation tx
- 0xdfc7cb9b62…93b4a5d5ad
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (12)
PLATFORM_WALLET_1() → address
PLATFORM_WALLET_2() → address
creationFee() → uint256
defaultRewards(uint256) → address, address
deployedTokenCount() → uint256
deployedTokens(uint256) → address
getDefaultBuyPath(uint256) → address[]
isDeployedToken(address) → bool
owner() → address
platformBps() → uint256
rewardAdmin() → address
tokenDeployer() → address
Events (6)
CreationFeeUpdatedDefaultRewardUpdatedOwnershipTransferredPlatformBpsUpdatedRewardAdminUpdatedTokenCreated
ABI
[
{
"inputs": [
{
"internalType": "uint256",
"name": "_platformBps",
"type": "uint256"
},
{
"internalType": "address",
"name": "_rewardAdmin",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "InvalidSlot",
"type": "error"
},
{
"inputs": [],
"name": "NotRewardAdmin",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "UnsupportedChain",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "newFee",
"type": "uint256"
}
],
"name": "CreationFeeUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "slot",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "router",
"type": "address"
}
],
"name": "DefaultRewardUpdated",
"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": false,
"internalType": "uint256",
"name": "newBps",
"type": "uint256"
}
],
"name": "PlatformBpsUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "newAdmin",
"type": "address"
}
],
"name": "RewardAdminUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "distributor",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "name",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"indexed": false,
"internalType": "uint256",
"name": "totalSupply",
"type": "uint256"
},
{
"indexed": false,
"internalType": "string",
"name": "imageURI",
"type": "string"
}
],
"name": "TokenCreated",
"type": "event"
},
{
"inputs": [],
"name": "PLATFORM_WALLET_1",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PLATFORM_WALLET_2",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_name",
"type": "string"
},
{
"internalType": "string",
"name": "_symbol",
"type": "string"
},
{
"internalType": "uint256",
"name": "_totalSupply",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_launchFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_tradingFee",
"type": "uint256"
},
{
"internalType": "address",
"name": "_devWallet",
"type": "address"
},
{
"internalType": "string",
"name": "_imageURI",
"type": "string"
},
{
"internalType": "string",
"name": "_description",
"type": "string"
},
{
"internalType": "string",
"name": "_website",
"type": "string"
},
{
"internalType": "string",
"name": "_twitter",
"type": "string"
},
{
"internalType": "string",
"name": "_telegram",
"type": "string"
},
{
"internalType": "string",
"name": "_discord",
"type": "string"
}
],
"name": "createToken",
"outputs": [
{
"internalType": "address",
"name": "tokenAddr",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_name",
"type": "string"
},
{
"internalType": "string",
"name": "_symbol",
"type": "string"
},
{
"internalType": "uint256",
"name": "_totalSupply",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_launchFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_tradingFee",
"type": "uint256"
},
{
"internalType": "address",
"name": "_devWallet",
"type": "address"
},
{
"internalType": "string",
"name": "_imageURI",
"type": "string"
},
{
"internalType": "string",
"name": "_description",
"type": "string"
},
{
"internalType": "string",
"name": "_website",
"type": "string"
},
{
"internalType": "string",
"name": "_twitter",
"type": "string"
},
{
"internalType": "string",
"name": "_telegram",
"type": "string"
},
{
"internalType": "string",
"name": "_discord",
"type": "string"
},
{
"internalType": "uint256",
"name": "_lpTokenAmount",
"type": "uint256"
},
{
"internalType": "bool",
"name": "_burnLP",
"type": "bool"
}
],
"name": "createTokenAndLaunch",
"outputs": [
{
"internalType": "address",
"name": "tokenAddr",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "creationFee",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "defaultRewards",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "router",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "deployedTokenCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "deployedTokens",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "slot",
"type": "uint256"
}
],
"name": "getDefaultBuyPath",
"outputs": [
{
"internalType": "address[]",
"name": "",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isDeployedToken",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "platformBps",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "rewardAdmin",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_fee",
"type": "uint256"
}
],
"name": "setCreationFee",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "slot",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "_router",
"type": "address"
},
{
"internalType": "address[]",
"name": "buyPath",
"type": "address[]"
}
],
"name": "setDefaultReward",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_bps",
"type": "uint256"
}
],
"name": "setPlatformBps",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_admin",
"type": "address"
}
],
"name": "setRewardAdmin",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "tokenDeployer",
"outputs": [
{
"internalType": "contract TokenDeployer",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"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.25;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { FactoryFeeToken } from "./FactoryFeeToken.sol";
import { MultiDexUserDividendDistributor } from "./MultiDexUserDividendDistributor.sol";
import { TokenDeployer } from "./TokenDeployer.sol";
/// @title TokenFactory
/// @notice Deploys FactoryFeeToken instances with 5-reward dividends,
/// auto-configures default reward slots, and transfers full
/// ownership to the deployer (msg.sender of createToken).
/// Token creation is delegated to TokenDeployer to keep the
/// factory under the 24 KB contract size limit.
contract TokenFactory is Ownable {
// ─── Platform constants ────────────────────────────────────────────
address public constant PLATFORM_WALLET_1 = 0xeFC9266D86e4D46099A43CC9eF74A290A1C0e098;
address public constant PLATFORM_WALLET_2 = 0x8bC9dC2B645cC9E6cc36466bAc03560981616aF1;
// ─── State ─────────────────────────────────────────────────────────
/// @notice External deployer contract (holds FactoryFeeToken creation bytecode).
TokenDeployer public immutable tokenDeployer;
/// @notice Address allowed to update the 3 default reward configs.
address public rewardAdmin;
/// @notice Basis points of swapped native taken by the platform (immutable per token).
uint public platformBps;
/// @notice Native wei required to call createToken (split 50/50 to platform wallets).
uint public creationFee;
// ─── Default reward config ─────────────────────────────────────────
struct DefaultReward {
address token;
address router;
}
DefaultReward[3] public defaultRewards;
mapping(uint256 => address[]) internal _defaultBuyPaths;
// ─── Deployed tokens tracking ──────────────────────────────────────
address[] public deployedTokens;
mapping(address => bool) public isDeployedToken;
// ─── Errors ────────────────────────────────────────────────────────
error NotRewardAdmin();
error UnsupportedChain();
error InvalidSlot();
// ─── Events ────────────────────────────────────────────────────────
event TokenCreated(
address indexed token,
address indexed distributor,
address indexed deployer,
string name,
string symbol,
uint256 totalSupply,
string imageURI
);
event DefaultRewardUpdated(uint256 indexed slot, address token, address router);
event RewardAdminUpdated(address newAdmin);
event PlatformBpsUpdated(uint newBps);
event CreationFeeUpdated(uint newFee);
// ─── Constructor ───────────────────────────────────────────────────
/// @param _platformBps Initial platform basis points (e.g. 2000 = 20 %)
/// @param _rewardAdmin Address allowed to update default rewards
constructor(
uint _platformBps,
address _rewardAdmin
) Ownable(msg.sender) {
require(_platformBps <= 5000, "platform > 50%");
require(_rewardAdmin != address(0), "zero admin");
platformBps = _platformBps;
rewardAdmin = _rewardAdmin;
tokenDeployer = new TokenDeployer();
}
// ─── Modifiers ─────────────────────────────────────────────────────
modifier onlyRewardAdmin() {
if (msg.sender != rewardAdmin && msg.sender != owner()) revert NotRewardAdmin();
_;
}
// ─── Router detection ──────────────────────────────────────────────
function _getRouter() internal view returns (address) {
uint256 id = block.chainid;
if (id == 1) return 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; // Uniswap V2
if (id == 56) return 0x10ED43C718714eb63d5aA57B78B54704E256024E; // PancakeSwap V2
if (id == 137) return 0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff; // QuickSwap V2
if (id == 43114) return 0x60aE616a2155Ee3d9A68541Ba4544862310933d4; // TraderJoe
if (id == 42161) return 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506; // SushiSwap (Arbitrum)
if (id == 8453) return 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24; // Uniswap V2 (Base)
if (id == 250) return 0xF491e7B69E4244ad4002BC14e878a34207E38c29; // SpookySwap
if (id == 10) return 0x9c12939390052919aF3155f41Bf4160Fd3666A6f; // SushiSwap (Optimism)
if (id == 25) return 0x145863Eb42Cf62847A6Ca784e6416C1682b1b2Ae; // VVS Finance (Cronos)
if (id == 2372) return 0x2600E57E2044d62277775A925709af0047c28Eb7; // BESC DEX
if (id == 4663) return 0x89e5DB8B5aA49aA85AC63f691524311AEB649eba; // Robinhood Uniswap V2
if (id == 31337) return address(0); // Hardhat
revert UnsupportedChain();
}
// ─── Reward admin: default reward management ───────────────────────
/// @notice Set or update one of the 3 default reward configs.
function setDefaultReward(
uint256 slot,
address token,
address _router,
address[] calldata buyPath
) external onlyRewardAdmin {
if (slot >= 3) revert InvalidSlot();
require(token != address(0), "zero token");
require(_router != address(0), "zero router");
require(buyPath.length >= 2, "path too short");
require(buyPath[buyPath.length - 1] == token, "path must end with token");
defaultRewards[slot] = DefaultReward({ token: token, router: _router });
_defaultBuyPaths[slot] = buyPath;
emit DefaultRewardUpdated(slot, token, _router);
}
function getDefaultBuyPath(uint256 slot) external view returns (address[] memory) {
return _defaultBuyPaths[slot];
}
// ─── Owner: factory config ─────────────────────────────────────────
function setRewardAdmin(address _admin) external onlyOwner {
require(_admin != address(0), "zero admin");
rewardAdmin = _admin;
emit RewardAdminUpdated(_admin);
}
function setPlatformBps(uint _bps) external onlyOwner {
require(_bps <= 5000, "platform > 50%");
platformBps = _bps;
emit PlatformBpsUpdated(_bps);
}
function setCreationFee(uint _fee) external onlyOwner {
creationFee = _fee;
emit CreationFeeUpdated(_fee);
}
// ─── Core: create token ────────────────────────────────────────────
/// @notice Deploy a new FactoryFeeToken with dividend distributor.
/// Trading stays closed so the owner can age the deployment,
/// run a presale, or launch later via enableTrading.
function createToken(
string calldata _name,
string calldata _symbol,
uint256 _totalSupply,
uint256 _launchFee,
uint256 _tradingFee,
address _devWallet,
string calldata _imageURI,
string calldata _description,
string calldata _website,
string calldata _twitter,
string calldata _telegram,
string calldata _discord
) external payable returns (address tokenAddr) {
tokenAddr = _createToken(
_name,
_symbol,
_totalSupply,
_launchFee,
_tradingFee,
_devWallet,
_imageURI,
_description,
_website,
_twitter,
_telegram,
_discord,
false,
0,
false
);
}
/// @notice Deploy a token and open the market in the same transaction.
/// @dev `msg.value` must cover `creationFee` (unless platform-exempt) plus
/// the native amount used for initial V2 liquidity. Tokens for LP are
/// seeded onto the token contract before enableTrading is called.
/// @param _lpTokenAmount Whole-token amount (18 decimals applied) reserved for LP
/// @param _burnLP If true, LP tokens are burned; otherwise sent to the deployer
function createTokenAndLaunch(
string calldata _name,
string calldata _symbol,
uint256 _totalSupply,
uint256 _launchFee,
uint256 _tradingFee,
address _devWallet,
string calldata _imageURI,
string calldata _description,
string calldata _website,
string calldata _twitter,
string calldata _telegram,
string calldata _discord,
uint256 _lpTokenAmount,
bool _burnLP
) external payable returns (address tokenAddr) {
require(_lpTokenAmount > 0, "zero lp tokens");
require(_lpTokenAmount <= _totalSupply, "lp > supply");
tokenAddr = _createToken(
_name,
_symbol,
_totalSupply,
_launchFee,
_tradingFee,
_devWallet,
_imageURI,
_description,
_website,
_twitter,
_telegram,
_discord,
true,
_lpTokenAmount,
_burnLP
);
}
function _createToken(
string calldata _name,
string calldata _symbol,
uint256 _totalSupply,
uint256 _launchFee,
uint256 _tradingFee,
address _devWallet,
string calldata _imageURI,
string calldata _description,
string calldata _website,
string calldata _twitter,
string calldata _telegram,
string calldata _discord,
bool launchNow,
uint256 lpTokenAmount,
bool burnLP
) internal returns (address tokenAddr) {
bool isPlatform = msg.sender == PLATFORM_WALLET_1 || msg.sender == PLATFORM_WALLET_2;
uint feeOwed = isPlatform ? 0 : creationFee;
require(msg.value >= feeOwed, "insufficient creation fee");
require(_devWallet != address(0), "zero dev wallet");
uint lpValue = 0;
if (launchNow) {
// Only the creation fee goes to platform wallets; remainder seeds LP.
if (feeOwed > 0) {
uint half = feeOwed / 2;
(bool s1,) = PLATFORM_WALLET_1.call{value: half}("");
(bool s2,) = PLATFORM_WALLET_2.call{value: feeOwed - half}("");
require(s1 && s2, "fee transfer failed");
}
lpValue = msg.value - feeOwed;
require(lpValue > 0, "zero lp native");
} else if (msg.value > 0) {
// Deploy-only: entire msg.value is treated as the creation fee (legacy behavior).
uint half = msg.value / 2;
(bool s1,) = PLATFORM_WALLET_1.call{value: half}("");
(bool s2,) = PLATFORM_WALLET_2.call{value: msg.value - half}("");
require(s1 && s2, "fee transfer failed");
}
address routerAddr = _getRouter();
tokenAddr = tokenDeployer.deploy(
_name,
_symbol,
_totalSupply,
_devWallet,
PLATFORM_WALLET_1,
PLATFORM_WALLET_2,
platformBps,
_launchFee,
_tradingFee,
routerAddr,
_imageURI,
_description,
_website,
_twitter,
_telegram,
_discord
);
FactoryFeeToken token = FactoryFeeToken(payable(tokenAddr));
MultiDexUserDividendDistributor dist = token.dividendDistributor();
for (uint i = 0; i < 3; i++) {
DefaultReward memory dr = defaultRewards[i];
if (dr.token != address(0) && _defaultBuyPaths[i].length >= 2) {
dist.setDefaultSlot(i, dr.token, dr.router, _defaultBuyPaths[i]);
}
}
token.excludeFromFees(msg.sender, true);
token.excludeFromLimits(msg.sender, true);
token.setExcludedFromMaxWallet(msg.sender, true);
uint minted = IERC20(tokenAddr).balanceOf(address(this));
// lpTokenAmount is in whole tokens; minted balance is 18-decimal wei.
uint lpWei = launchNow ? lpTokenAmount * 1e18 : 0;
require(lpWei <= minted, "lp > minted");
if (launchNow) {
uint toDeployer = minted - lpWei;
if (toDeployer > 0) {
IERC20(tokenAddr).transfer(msg.sender, toDeployer);
}
// Seed LP inventory onto the token contract, then open the market.
IERC20(tokenAddr).transfer(tokenAddr, lpWei);
token.enableTrading{value: lpValue}(burnLP, 0);
// Factory received LP when burnLP=false; forward it to the deployer.
if (!burnLP) {
address pair = token.uniswapV2Pair();
uint lpBal = IERC20(pair).balanceOf(address(this));
if (lpBal > 0) {
IERC20(pair).transfer(msg.sender, lpBal);
}
}
} else {
IERC20(tokenAddr).transfer(msg.sender, minted);
}
token.transferOwnership(msg.sender);
dist.transferOwnership(msg.sender);
deployedTokens.push(tokenAddr);
isDeployedToken[tokenAddr] = true;
emit TokenCreated(
tokenAddr,
address(dist),
msg.sender,
_name,
_symbol,
_totalSupply,
_imageURI
);
}
// ─── Views ─────────────────────────────────────────────────────────
function deployedTokenCount() external view returns (uint256) {
return deployedTokens.length;
}
}
Chain explorer2472msChain node79ms