RobintownLaunchpad

0x7a9fd5255570f47c11261faa639de4e7083b2e65

Verification
Verified
v0.8.20+commit.a1b79de6
Type
Contract
13,079 bytes
ABI entries
37
20 read · 9 write
License
none

Contract information

Address
0x7a9fd5255570f47c11261faa639de4e7083b2e65
Chain
Robinhood Chain (4663)
Compiler
v0.8.20+commit.a1b79de6
Optimization
Enabled
Creation tx
0xba704315fc…8ecf68b9df

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (20)

BPS()uint256
LP_SUPPLY()uint256
TOTAL_SUPPLY()uint256
VIRTUAL_TOKEN_SEED()uint256
allTokens(uint256)address
allTradingPaused()bool
buysPaused()bool
creationPaused()bool
creatorFees(address)uint256
creatorOf(address)address
curves(address)uint128, uint128, uint128, uint128, address, bool, bool, uint16, bool
distributor()address
migrator()address
owner()address
progressBps(address)uint256
protocolFees()uint256
quoteBuy(address, uint256)uint256, uint256
tokenCount()uint256
tradeFeeBps()uint16
virtualEthSeed()uint128

Events (7)

GraduatedMigratedOwnershipTransferredPauseStateSetTokenCreatedTokenTradingPauseSetTrade

ABI

[
  {
    "inputs": [
      {
        "internalType": "uint128",
        "name": "virtualEthSeed_",
        "type": "uint128"
      },
      {
        "internalType": "uint16",
        "name": "tradeFeeBps_",
        "type": "uint16"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "raisedEth",
        "type": "uint256"
      }
    ],
    "name": "Graduated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethLiquidity",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenLiquidity",
        "type": "uint256"
      }
    ],
    "name": "Migrated",
    "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": "bool",
        "name": "creationPaused",
        "type": "bool"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "buysPaused",
        "type": "bool"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "allTradingPaused",
        "type": "bool"
      }
    ],
    "name": "PauseStateSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "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": "uint16",
        "name": "keyAllocationBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint48",
        "name": "snapshotBlock",
        "type": "uint48"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "curveSupply",
        "type": "uint256"
      }
    ],
    "name": "TokenCreated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "paused",
        "type": "bool"
      }
    ],
    "name": "TokenTradingPauseSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "trader",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "isBuy",
        "type": "bool"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "Trade",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LP_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TOTAL_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "VIRTUAL_TOKEN_SEED",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "allTokens",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "allTradingPaused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "minTokensOut",
        "type": "uint256"
      }
    ],
    "name": "buy",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "buysPaused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "internalType": "uint16",
        "name": "keyAllocationBps",
        "type": "uint16"
      },
      {
        "internalType": "uint256",
        "name": "minTokensOut",
        "type": "uint256"
      }
    ],
    "name": "createToken",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "creationPaused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "creatorFees",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "creatorOf",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "curves",
    "outputs": [
      {
        "internalType": "uint128",
        "name": "virtualEth",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "virtualTokens",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "realEth",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "realTokens",
        "type": "uint128"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "bool",
        "name": "graduated",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "migrated",
        "type": "bool"
      },
      {
        "internalType": "uint16",
        "name": "keyAllocationBps",
        "type": "uint16"
      },
      {
        "internalType": "bool",
        "name": "tradingPaused",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "distributor",
    "outputs": [
      {
        "internalType": "contract RobintownKeyDistributor",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "migrator",
    "outputs": [
      {
        "internalType": "contract RobintownMigrator",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "progressBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "protocolFees",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "ethIn",
        "type": "uint256"
      }
    ],
    "name": "quoteBuy",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "tokensOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minEthOut",
        "type": "uint256"
      }
    ],
    "name": "sell",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "value",
        "type": "address"
      }
    ],
    "name": "setDistributor",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "value",
        "type": "address"
      }
    ],
    "name": "setMigrator",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bool",
        "name": "creation",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "buys",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "allTrading",
        "type": "bool"
      }
    ],
    "name": "setPauseState",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "bool",
        "name": "paused",
        "type": "bool"
      }
    ],
    "name": "setTokenTradingPaused",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tokenCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tradeFeeBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "virtualEthSeed",
    "outputs": [
      {
        "internalType": "uint128",
        "name": "",
        "type": "uint128"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {RobintownToken} from "./RobintownToken.sol";
import {RobintownMigrator} from "./RobintownMigrator.sol";
import {RobintownKeyDistributor} from "./RobintownKeyDistributor.sol";

/// @notice Robintown fair-launch virtual-reserve bonding curve.
contract RobintownLaunchpad is Ownable, ReentrancyGuard {
    uint256 public constant TOTAL_SUPPLY = 1_000_000e18;
    uint256 public constant LP_SUPPLY = 200_000e18;
    uint256 public constant VIRTUAL_TOKEN_SEED = 1_145_000e18;
    uint256 public constant BPS = 10_000;

    struct Curve {
        uint128 virtualEth;
        uint128 virtualTokens;
        uint128 realEth;
        uint128 realTokens;
        address creator;
        bool graduated;
        bool migrated;
        uint16 keyAllocationBps;
        bool tradingPaused;
    }

    uint128 public immutable virtualEthSeed;
    uint16 public immutable tradeFeeBps;
    RobintownMigrator public migrator;
    RobintownKeyDistributor public distributor;
    bool public creationPaused;
    bool public buysPaused;
    bool public allTradingPaused;
    mapping(address => Curve) public curves;
    mapping(address => uint256) public creatorFees;
    uint256 public protocolFees;
    address[] public allTokens;

    event TokenCreated(address indexed token, address indexed creator, string name, string symbol, uint16 keyAllocationBps, uint48 snapshotBlock, uint256 curveSupply);
    event Trade(address indexed token, address indexed trader, bool isBuy, uint256 ethAmount, uint256 tokenAmount, uint256 fee);
    event Graduated(address indexed token, uint256 raisedEth);
    event Migrated(address indexed token, address indexed pool, uint256 ethLiquidity, uint256 tokenLiquidity);
    event PauseStateSet(bool creationPaused, bool buysPaused, bool allTradingPaused);
    event TokenTradingPauseSet(address indexed token, bool paused);

    constructor(uint128 virtualEthSeed_, uint16 tradeFeeBps_) {
        require(virtualEthSeed_ != 0 && tradeFeeBps_ <= 500, "BAD_CONFIG");
        virtualEthSeed = virtualEthSeed_;
        tradeFeeBps = tradeFeeBps_;
    }

    function setMigrator(address value) external onlyOwner {
        require(address(migrator) == address(0) && value != address(0), "MIGRATOR_LOCKED");
        migrator = RobintownMigrator(value);
    }

    function setDistributor(address value) external onlyOwner {
        require(address(distributor) == address(0) && value != address(0), "DISTRIBUTOR_LOCKED");
        distributor = RobintownKeyDistributor(value);
    }

    function setPauseState(bool creation, bool buys, bool allTrading) external onlyOwner {
        creationPaused = creation; buysPaused = buys; allTradingPaused = allTrading;
        emit PauseStateSet(creation, buys, allTrading);
    }

    function setTokenTradingPaused(address token, bool paused) external onlyOwner {
        require(curves[token].creator != address(0), "UNKNOWN_TOKEN");
        curves[token].tradingPaused = paused;
        emit TokenTradingPauseSet(token, paused);
    }

    function createToken(string calldata name, string calldata symbol, uint16 keyAllocationBps, uint256 minTokensOut)
        external payable nonReentrant returns (address token)
    {
        require(!creationPaused && keyAllocationBps >= 100 && keyAllocationBps <= 1000, "CREATION_UNAVAILABLE");
        require(address(distributor) != address(0), "NO_DISTRIBUTOR");
        uint256 keySupply = TOTAL_SUPPLY * keyAllocationBps / BPS;
        uint256 curveSupply = TOTAL_SUPPLY - LP_SUPPLY - keySupply;
        token = address(new RobintownToken(name, symbol, TOTAL_SUPPLY, address(this)));
        uint256 scaledVirtualTokens = VIRTUAL_TOKEN_SEED * curveSupply / (TOTAL_SUPPLY - LP_SUPPLY);
        curves[token] = Curve(virtualEthSeed, uint128(scaledVirtualTokens), 0, uint128(curveSupply), msg.sender, false, false, keyAllocationBps, false);
        allTokens.push(token);
        RobintownToken(token).approve(address(distributor), keySupply);
        distributor.register(token, msg.sender, uint48(block.number), keyAllocationBps, keySupply);
        emit TokenCreated(token, msg.sender, name, symbol, keyAllocationBps, uint48(block.number), curveSupply);
        if (msg.value != 0) _buy(token, msg.sender, msg.value, minTokensOut);
        else require(minTokensOut == 0, "SLIPPAGE");
    }

    function buy(address token, uint256 minTokensOut) external payable nonReentrant {
        require(msg.value != 0, "ZERO_AMOUNT");
        require(!buysPaused && !allTradingPaused && !curves[token].tradingPaused, "TRADING_PAUSED");
        _buy(token, msg.sender, msg.value, minTokensOut);
    }

    function sell(address token, uint256 amount, uint256 minEthOut) external nonReentrant {
        Curve storage c = curves[token];
        require(!allTradingPaused && !c.tradingPaused, "TRADING_PAUSED");
        require(c.creator != address(0) && !c.graduated && amount != 0, "NOT_LIVE");
        uint256 ethOut = uint256(c.virtualEth) - Math.ceilDiv(uint256(c.virtualEth) * c.virtualTokens, uint256(c.virtualTokens) + amount);
        uint256 fee = ethOut * tradeFeeBps / BPS;
        uint256 payout = ethOut - fee;
        require(payout >= minEthOut && payout != 0, "SLIPPAGE");
        c.virtualEth -= uint128(ethOut);
        c.virtualTokens += uint128(amount);
        c.realEth -= uint128(ethOut);
        c.realTokens += uint128(amount);
        _accrueFee(token, fee);
        require(RobintownToken(token).transferFrom(msg.sender, address(this), amount), "TRANSFER_FAILED");
        (bool ok,) = msg.sender.call{value: payout}("");
        require(ok, "ETH_TRANSFER_FAILED");
        emit Trade(token, msg.sender, false, payout, amount, fee);
    }

    function _buy(address token, address recipient, uint256 ethIn, uint256 minTokensOut) internal {
        Curve storage c = curves[token];
        require(!buysPaused && !allTradingPaused && !c.tradingPaused, "TRADING_PAUSED");
        require(c.creator != address(0) && !c.graduated, "NOT_LIVE");
        uint256 fee = ethIn * tradeFeeBps / BPS;
        uint256 curveEth = ethIn - fee;
        uint256 tokensOut = uint256(c.virtualTokens) - uint256(c.virtualEth) * c.virtualTokens / (uint256(c.virtualEth) + curveEth);
        uint256 refund;
        if (tokensOut >= c.realTokens) {
            tokensOut = c.realTokens;
            uint256 exactEth = Math.ceilDiv(uint256(c.virtualEth) * c.virtualTokens, uint256(c.virtualTokens) - tokensOut) - c.virtualEth;
            uint256 gross = Math.ceilDiv(exactEth * BPS, BPS - tradeFeeBps);
            if (gross > ethIn) gross = ethIn;
            curveEth = exactEth;
            fee = gross - exactEth;
            refund = ethIn - gross;
        }
        require(tokensOut >= minTokensOut && tokensOut != 0, "SLIPPAGE");
        c.virtualEth += uint128(curveEth);
        c.virtualTokens -= uint128(tokensOut);
        c.realEth += uint128(curveEth);
        c.realTokens -= uint128(tokensOut);
        _accrueFee(token, fee);
        require(RobintownToken(token).transfer(recipient, tokensOut), "TRANSFER_FAILED");
        emit Trade(token, recipient, true, curveEth + fee, tokensOut, fee);

        if (c.realTokens == 0) {
            require(address(migrator) != address(0), "NO_MIGRATOR");
            c.graduated = true;
            c.migrated = true;
            uint256 liquidityEth = c.realEth;
            c.realEth = 0;
            RobintownToken(token).approve(address(migrator), LP_SUPPLY);
            address pool = migrator.migrate{value: liquidityEth}(token, LP_SUPPLY);
            emit Graduated(token, liquidityEth);
            emit Migrated(token, pool, liquidityEth, LP_SUPPLY);
        }
        if (refund != 0) {
            (bool ok,) = recipient.call{value: refund}("");
            require(ok, "REFUND_FAILED");
        }
    }

    function _accrueFee(address token, uint256 fee) internal {
        uint256 creatorCut = fee * 8_000 / BPS;
        creatorFees[token] += creatorCut;
        protocolFees += fee - creatorCut;
    }

    function quoteBuy(address token, uint256 ethIn) external view returns (uint256 tokensOut, uint256 fee) {
        Curve memory c = curves[token];
        fee = ethIn * tradeFeeBps / BPS;
        tokensOut = uint256(c.virtualTokens) - uint256(c.virtualEth) * c.virtualTokens / (uint256(c.virtualEth) + ethIn - fee);
        if (tokensOut > c.realTokens) tokensOut = c.realTokens;
    }

    function progressBps(address token) external view returns (uint256) {
        Curve memory c = curves[token];
        uint256 initial = TOTAL_SUPPLY - LP_SUPPLY - TOTAL_SUPPLY * c.keyAllocationBps / BPS;
        return (initial - c.realTokens) * BPS / initial;
    }

    function tokenCount() external view returns (uint256) { return allTokens.length; }
    function creatorOf(address token) external view returns (address) { return curves[token].creator; }
}
Chain explorer4023msChain node97ms