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
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