// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract NasdaqCommunityToken { string public name; string public symbol; string public referenceTicker; string public website; string public twitter; string public telegram; uint8 public constant decimals = 18; uint16 public constant sellFeeBps = 100; uint256 public immutable totalSupply; uint256 public immutable tokensPerBNB; address public immutable creator; uint256 public creatorFees; bool private entered; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); event Bought(address indexed account, uint256 bnbIn, uint256 tokensOut); event Sold(address indexed account, uint256 tokensIn, uint256 bnbOut, uint256 fee); event FeesClaimed(address indexed creator, uint256 amount); error InvalidConfiguration(); error InsufficientBalance(); error InsufficientAllowance(); error Slippage(); error SoldOut(); error InsufficientReserve(); error CreatorOnly(); error TransferFailed(); error ReentrantCall(); modifier nonReentrant() { if (entered) revert ReentrantCall(); entered = true; _; entered = false; } constructor(string memory tokenName, string memory tokenSymbol, string memory ticker, string memory site, string memory x, string memory tg, uint256 supplyTokens, uint256 rate, address tokenCreator) { if (block.chainid != 56 || bytes(tokenName).length < 3 || bytes(tokenName).length > 40 || bytes(tokenSymbol).length < 2 || bytes(tokenSymbol).length > 9 || supplyTokens < 1_000 || supplyTokens > 1_000_000_000 || rate == 0 || rate > 1_000_000_000 || tokenCreator == address(0)) revert InvalidConfiguration(); name = tokenName; symbol = tokenSymbol; referenceTicker = ticker; website = site; twitter = x; telegram = tg; totalSupply = supplyTokens * 1 ether; tokensPerBNB = rate; creator = tokenCreator; balanceOf[address(this)] = totalSupply; emit Transfer(address(0), address(this), totalSupply); } function buy(uint256 minTokensOut) external payable nonReentrant returns (uint256 tokensOut) { if (msg.value == 0) revert InvalidConfiguration(); tokensOut = msg.value * tokensPerBNB; if (tokensOut < minTokensOut) revert Slippage(); if (balanceOf[address(this)] < tokensOut) revert SoldOut(); _transfer(address(this), msg.sender, tokensOut); emit Bought(msg.sender, msg.value, tokensOut); } function sell(uint256 tokenAmount, uint256 minBnbOut) external nonReentrant returns (uint256 bnbOut) { if (tokenAmount == 0) revert InvalidConfiguration(); uint256 gross = tokenAmount / tokensPerBNB; uint256 fee = gross * sellFeeBps / 10_000; bnbOut = gross - fee; if (bnbOut < minBnbOut) revert Slippage(); if (address(this).balance < gross) revert InsufficientReserve(); _transfer(msg.sender, address(this), tokenAmount); creatorFees += fee; (bool sent,) = payable(msg.sender).call{value: bnbOut}(""); if (!sent) revert TransferFailed(); emit Sold(msg.sender, tokenAmount, bnbOut, fee); } function claimFees() external nonReentrant { if (msg.sender != creator) revert CreatorOnly(); uint256 amount = creatorFees; creatorFees = 0; (bool sent,) = payable(creator).call{value: amount}(""); if (!sent) revert TransferFailed(); emit FeesClaimed(creator, amount); } function quoteBuy(uint256 bnbIn) external view returns (uint256) { return bnbIn * tokensPerBNB; } function quoteSell(uint256 tokenAmount) external view returns (uint256) { uint256 gross = tokenAmount / tokensPerBNB; return gross - (gross * sellFeeBps / 10_000); } function reserveBNB() external view returns (uint256) { return address(this).balance - creatorFees; } function circulatingSupply() external view returns (uint256) { return totalSupply - balanceOf[address(this)]; } 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) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transferFrom(address from, address to, uint256 amount) external returns (bool) { uint256 available = allowance[from][msg.sender]; if (available != type(uint256).max) { if (available < amount) revert InsufficientAllowance(); unchecked { allowance[from][msg.sender] = available - amount; } emit Approval(from, msg.sender, allowance[from][msg.sender]); } _transfer(from, to, amount); return true; } function _transfer(address from, address to, uint256 amount) private { if (to == address(0)) revert InvalidConfiguration(); uint256 balance = balanceOf[from]; if (balance < amount) revert InsufficientBalance(); unchecked { balanceOf[from] = balance - amount; balanceOf[to] += amount; } emit Transfer(from, to, amount); } } contract NasdaqLaunchFactory { mapping(bytes32 => address) public marketFor; address[] public allMarkets; event MarketCreated(bytes32 indexed key, address indexed market, address indexed creator, string referenceTicker, string symbol, uint256 supplyTokens, uint256 tokensPerBNB); error InvalidConfiguration(); error SymbolTaken(); constructor() { if (block.chainid != 56) revert InvalidConfiguration(); } function createMarket(string calldata tokenName, string calldata tokenSymbol, string calldata referenceTicker, string calldata website, string calldata twitter, string calldata telegram, uint256 supplyTokens, uint256 tokensPerBNB) external returns (address market) { _validateSymbol(bytes(tokenSymbol)); _validateTicker(bytes(referenceTicker)); bytes32 key = keccak256(bytes(tokenSymbol)); if (marketFor[key] != address(0)) revert SymbolTaken(); market = address(new NasdaqCommunityToken{salt: key}(tokenName, tokenSymbol, referenceTicker, website, twitter, telegram, supplyTokens, tokensPerBNB, msg.sender)); marketFor[key] = market; allMarkets.push(market); emit MarketCreated(key, market, msg.sender, referenceTicker, tokenSymbol, supplyTokens, tokensPerBNB); } function marketsLength() external view returns (uint256) { return allMarkets.length; } function _validateSymbol(bytes memory value) private pure { if (value.length < 2 || value.length > 9) revert InvalidConfiguration(); for (uint256 i; i < value.length; ++i) { bytes1 c = value[i]; if (!((c >= 0x41 && c <= 0x5A) || (c >= 0x30 && c <= 0x39))) revert InvalidConfiguration(); } } function _validateTicker(bytes memory value) private pure { if (value.length == 0 || value.length > 8) revert InvalidConfiguration(); for (uint256 i; i < value.length; ++i) { bytes1 c = value[i]; if (!((c >= 0x41 && c <= 0x5A) || (c >= 0x30 && c <= 0x39))) revert InvalidConfiguration(); } } }