LaunchFactory

0x56d2a4512b00e792831e74149126994da3c62967

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
8,371 bytes
ABI entries
20
10 read · 1 write
License
none

Contract information

Address
0x56d2a4512b00e792831e74149126994da3c62967
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x46a3574e15…461956050d

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (10)

controllerImplementation()address
feeController()address
guardian()address
launch(uint256)tuple
launchCount()uint256
launchHook()address
launchRouter()address
predictController(uint256, address, string, string)address
tokenScaleDivisor()uint256
treasury()address

Events (1)

LaunchCreated

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "controllerImplementation_",
        "type": "address"
      },
      {
        "internalType": "contract LaunchHook",
        "name": "hook_",
        "type": "address"
      },
      {
        "internalType": "contract FeeController",
        "name": "feeController_",
        "type": "address"
      },
      {
        "internalType": "contract LaunchRouter",
        "name": "router_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "guardian_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "treasury_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "target",
        "type": "address"
      }
    ],
    "name": "AddressEmptyCode",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "AddressInsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CreatorMustCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ERC1167FailedCreateClone",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "FailedInnerCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidAddress",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "launchId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "controller",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "team",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "receipt",
        "type": "address"
      }
    ],
    "name": "LaunchCreated",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "controllerImplementation",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "team",
        "type": "address"
      }
    ],
    "name": "createLaunch",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "launchId",
        "type": "uint256"
      },
      {
        "components": [
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "controller",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "receipt",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creatorEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "teamEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "protocolEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creatorProceedsEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "team",
            "type": "address"
          }
        ],
        "internalType": "struct LaunchFactory.LaunchRecord",
        "name": "record",
        "type": "tuple"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeController",
    "outputs": [
      {
        "internalType": "contract FeeController",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "guardian",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "launchId",
        "type": "uint256"
      }
    ],
    "name": "launch",
    "outputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "controller",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "receipt",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creatorEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "teamEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "protocolEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creatorProceedsEscrow",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "team",
            "type": "address"
          }
        ],
        "internalType": "struct LaunchFactory.LaunchRecord",
        "name": "",
        "type": "tuple"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchHook",
    "outputs": [
      {
        "internalType": "contract LaunchHook",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchRouter",
    "outputs": [
      {
        "internalType": "contract LaunchRouter",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "launchId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      }
    ],
    "name": "predictController",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tokenScaleDivisor",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "treasury",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { Clones } from "openzeppelin-contracts/contracts/proxy/Clones.sol";
import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

import { FeeController } from "./FeeController.sol";
import { LaunchController } from "./LaunchController.sol";
import { LaunchHook } from "./LaunchHook.sol";
import { LaunchRouter } from "./LaunchRouter.sol";
import { LaunchToken } from "./LaunchToken.sol";
import { ProtocolTokenFeeVault } from "./ProtocolTokenFeeVault.sol";
import { LaunchConstants } from "./libraries/LaunchConstants.sol";

/// @notice Permissionless launch creation with exact approved allocation custody.
contract LaunchFactory {
    using SafeERC20 for IERC20;

    struct LaunchRecord {
        address token;
        address controller;
        address receipt;
        address creatorEscrow;
        address teamEscrow;
        address protocolEscrow;
        address creatorProceedsEscrow;
        address creator;
        address team;
    }

    address public immutable controllerImplementation;
    LaunchHook public immutable launchHook;
    FeeController public immutable feeController;
    LaunchRouter public immutable launchRouter;
    address public immutable guardian;
    address public immutable treasury;
    uint256 public launchCount;
    mapping(uint256 launchId => LaunchRecord) private launches;

    error InvalidAddress();
    error CreatorMustCall();

    event LaunchCreated(
        uint256 indexed launchId,
        address indexed token,
        address indexed controller,
        address creator,
        address team,
        address receipt
    );

    constructor(
        address controllerImplementation_,
        LaunchHook hook_,
        FeeController feeController_,
        LaunchRouter router_,
        address guardian_,
        address treasury_
    ) {
        if (
            controllerImplementation_ == address(0) || address(hook_) == address(0)
                || address(feeController_) == address(0) || address(router_) == address(0) || guardian_ == address(0)
                || treasury_ == address(0)
        ) revert InvalidAddress();
        controllerImplementation = controllerImplementation_;
        launchHook = hook_;
        feeController = feeController_;
        launchRouter = router_;
        guardian = guardian_;
        treasury = treasury_;
    }

    function launch(uint256 launchId) external view returns (LaunchRecord memory) {
        return launches[launchId];
    }

    /// @notice One on production chains and 1,000 on Robinhood testnet 46630.
    function tokenScaleDivisor() external view returns (uint256) {
        return LaunchConstants.tokenScaleDivisor();
    }

    function createLaunch(string calldata name, string calldata symbol, address creator, address team)
        external
        returns (uint256 launchId, LaunchRecord memory record)
    {
        if (creator != msg.sender) revert CreatorMustCall();
        if (team == address(0)) revert InvalidAddress();
        launchId = launchCount++;
        bytes32 salt = keccak256(abi.encode(block.chainid, address(this), launchId, creator, name, symbol));
        address controllerAddress = Clones.cloneDeterministic(controllerImplementation, salt);
        LaunchToken token = new LaunchToken(name, symbol, address(this));
        ProtocolTokenFeeVault protocolTokenFeeVault = new ProtocolTokenFeeVault(IERC20(address(token)), treasury);
        LaunchController controller = LaunchController(payable(controllerAddress));
        controller.initialize(
            IERC20(address(token)), creator, guardian, address(launchRouter), address(protocolTokenFeeVault)
        );

        IERC20 erc20 = IERC20(address(token));
        erc20.safeTransfer(controllerAddress, LaunchConstants.controllerTokens());

        feeController.registerController(controllerAddress, controller.poolId(), creator);
        launchRouter.registerController(controllerAddress);
        launchHook.registerPool(controller.getPoolKey(), controllerAddress, controller.bandLowerSqrts());
        controller.seed();

        record = LaunchRecord({
            token: address(token),
            controller: controllerAddress,
            receipt: address(controller.receipt()),
            creatorEscrow: address(0),
            teamEscrow: address(0),
            protocolEscrow: address(protocolTokenFeeVault),
            creatorProceedsEscrow: address(0),
            creator: creator,
            team: team
        });
        launches[launchId] = record;
        emit LaunchCreated(launchId, address(token), controllerAddress, creator, team, record.receipt);
    }

    function predictController(uint256 launchId, address creator, string calldata name, string calldata symbol)
        external
        view
        returns (address)
    {
        bytes32 salt = keccak256(abi.encode(block.chainid, address(this), launchId, creator, name, symbol));
        return Clones.predictDeterministicAddress(controllerImplementation, salt, address(this));
    }
}
Chain explorer4112msChain node75ms