StandardTokenFactory
0xe40a3445df4cd56ee4de40fabfecb3b9ea3cb4d0
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
15,474 bytes
ABI entries
48
19 read · 8 write
License
none
Contract information
- Address
- 0xe40a3445df4cd56ee4de40fabfecb3b9ea3cb4d0
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0xd03543d97B…F446Cd1418
- Creation tx
- 0x194e199f57…68e9fd6c1a
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (19)
DEFAULT_ADMIN_ROLE() → bytes32
FACTORY_VERSION() → uint32
PAUSER_ROLE() → bytes32
getLaunch(uint256) → tuple
getRoleAdmin(bytes32) → bytes32
hasRole(bytes32, address) → bool
hashLaunchRequest(address, uint256, tuple) → bytes32
identityVerifier() → address
launchFeeManager() → address
launchModuleFactory() → address
launchRequirements() → address
metadataRegistry() → address
paused() → bool
projectNumberByToken(address) → uint256
projectRegistry() → address
supportsInterface(bytes4) → bool
templateRegistry() → address
tokenAt(uint256) → address
tokenCount() → uint256
Events (8)
LaunchOpenedMetadataRegistryConfiguredPausedRoleAdminChangedRoleGrantedRoleRevokedTokenCreatedUnpaused
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "admin",
"type": "address"
},
{
"internalType": "contract ProjectRegistry",
"name": "projectRegistry_",
"type": "address"
},
{
"internalType": "contract LaunchTemplateRegistry",
"name": "templateRegistry_",
"type": "address"
},
{
"internalType": "contract IIdentityAttestationVerifier",
"name": "identityVerifier_",
"type": "address"
},
{
"internalType": "contract ILaunchRequirements",
"name": "launchRequirements_",
"type": "address"
},
{
"internalType": "contract ILaunchFeeManager",
"name": "launchFeeManager_",
"type": "address"
},
{
"internalType": "contract IStandardTokenLaunchModuleFactory",
"name": "launchModuleFactory_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AccessControlBadConfirmation",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "bytes32",
"name": "neededRole",
"type": "bytes32"
}
],
"name": "AccessControlUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "EnforcedPause",
"type": "error"
},
{
"inputs": [],
"name": "ExpectedPause",
"type": "error"
},
{
"inputs": [],
"name": "IncorrectLaunchFee",
"type": "error"
},
{
"inputs": [],
"name": "InvalidAddress",
"type": "error"
},
{
"inputs": [],
"name": "InvalidCreatorFee",
"type": "error"
},
{
"inputs": [],
"name": "InvalidIdentityAttestation",
"type": "error"
},
{
"inputs": [],
"name": "InvalidInitialBuy",
"type": "error"
},
{
"inputs": [],
"name": "InvalidLaunchModule",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [],
"name": "TemplateDisabled",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "launchModule",
"type": "address"
}
],
"name": "LaunchOpened",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "metadataRegistry",
"type": "address"
}
],
"name": "MetadataRegistryConfigured",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Paused",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "previousAdminRole",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "newAdminRole",
"type": "bytes32"
}
],
"name": "RoleAdminChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleGranted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleRevoked",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "launchModule",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"indexed": false,
"internalType": "uint16",
"name": "creatorFeeBps",
"type": "uint16"
}
],
"name": "TokenCreated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Unpaused",
"type": "event"
},
{
"inputs": [],
"name": "DEFAULT_ADMIN_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "FACTORY_VERSION",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PAUSER_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"internalType": "uint16",
"name": "creatorFeeBps",
"type": "uint16"
},
{
"internalType": "bytes",
"name": "identityAttestation",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "launchData",
"type": "bytes"
}
],
"internalType": "struct StandardTokenFactory.LaunchRequest",
"name": "request",
"type": "tuple"
}
],
"name": "createToken",
"outputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "launchModule",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"internalType": "uint16",
"name": "creatorFeeBps",
"type": "uint16"
},
{
"internalType": "bytes",
"name": "identityAttestation",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "launchData",
"type": "bytes"
}
],
"internalType": "struct StandardTokenFactory.LaunchRequest",
"name": "request",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "initialBuyAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "minTokensOut",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "createTokenAndBuy",
"outputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "launchModule",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"name": "getLaunch",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "launchModule",
"type": "address"
},
{
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"internalType": "uint16",
"name": "creatorFeeBps",
"type": "uint16"
},
{
"internalType": "bytes32",
"name": "normalizedNameHash",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "normalizedSymbolHash",
"type": "bytes32"
}
],
"internalType": "struct StandardTokenFactory.TokenLaunchRecord",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
}
],
"name": "getRoleAdmin",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "grantRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "hasRole",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"components": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"internalType": "uint16",
"name": "creatorFeeBps",
"type": "uint16"
},
{
"internalType": "bytes",
"name": "identityAttestation",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "launchData",
"type": "bytes"
}
],
"internalType": "struct StandardTokenFactory.LaunchRequest",
"name": "request",
"type": "tuple"
}
],
"name": "hashLaunchRequest",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "identityVerifier",
"outputs": [
{
"internalType": "contract IIdentityAttestationVerifier",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchFeeManager",
"outputs": [
{
"internalType": "contract ILaunchFeeManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchModuleFactory",
"outputs": [
{
"internalType": "contract IStandardTokenLaunchModuleFactory",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchRequirements",
"outputs": [
{
"internalType": "contract ILaunchRequirements",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "metadataRegistry",
"outputs": [
{
"internalType": "contract IMetadataRegistry",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "projectNumberByToken",
"outputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "projectRegistry",
"outputs": [
{
"internalType": "contract ProjectRegistry",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "callerConfirmation",
"type": "address"
}
],
"name": "renounceRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "revokeRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IMetadataRegistry",
"name": "metadataRegistry_",
"type": "address"
}
],
"name": "setMetadataRegistry",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "templateRegistry",
"outputs": [
{
"internalType": "contract LaunchTemplateRegistry",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "tokenAt",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokenCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "unpause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IIdentityAttestationVerifier } from "../interfaces/IIdentityAttestationVerifier.sol";
import { ILaunchFeeManager } from "../interfaces/ILaunchFeeManager.sol";
import { ILaunchRequirements } from "../interfaces/ILaunchRequirements.sol";
import { IMetadataRegistry } from "../interfaces/IMetadataRegistry.sol";
import {
IStandardTokenLaunchModule,
IStandardTokenLaunchModuleFactory
} from "../interfaces/IStandardTokenLaunchModule.sol";
import { ProductType } from "../types/ProductType.sol";
import { EquifoldToken } from "./EquifoldToken.sol";
import { LaunchTemplateRegistry } from "./LaunchTemplateRegistry.sol";
import { ProjectRegistry } from "./ProjectRegistry.sol";
/// @title Equifold Standard Token Factory
/// @author Equifold
/// @notice Deploys the same fixed-supply token and configured launch path for every project.
contract StandardTokenFactory is AccessControl, Pausable, ReentrancyGuard {
error IncorrectLaunchFee();
error InvalidInitialBuy();
error InvalidAddress();
error InvalidCreatorFee();
error InvalidIdentityAttestation();
error InvalidLaunchModule();
error TemplateDisabled();
uint32 public constant FACTORY_VERSION = 1;
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
struct LaunchRequest {
string name;
string symbol;
string metadataURI;
bytes32 templateKey;
uint32 templateVersion;
uint16 creatorFeeBps;
bytes identityAttestation;
bytes launchData;
}
struct TokenLaunchRecord {
address creator;
address token;
address launchModule;
bytes32 templateKey;
uint32 templateVersion;
uint16 creatorFeeBps;
bytes32 normalizedNameHash;
bytes32 normalizedSymbolHash;
}
struct LaunchCreation {
uint256 projectNumber;
address token;
address launchModule;
bytes32 normalizedNameHash;
bytes32 normalizedSymbolHash;
}
ProjectRegistry public immutable projectRegistry;
LaunchTemplateRegistry public immutable templateRegistry;
IIdentityAttestationVerifier public immutable identityVerifier;
ILaunchRequirements public immutable launchRequirements;
ILaunchFeeManager public immutable launchFeeManager;
IStandardTokenLaunchModuleFactory public immutable launchModuleFactory;
IMetadataRegistry public metadataRegistry;
uint256 public tokenCount;
mapping(uint256 index => address token) public tokenAt;
mapping(uint256 projectNumber => TokenLaunchRecord launch) private _launches;
mapping(address token => uint256 projectNumber) public projectNumberByToken;
event TokenCreated(
uint256 indexed projectNumber,
address indexed token,
address indexed creator,
address launchModule,
bytes32 templateKey,
uint32 templateVersion,
uint16 creatorFeeBps
);
event LaunchOpened(
uint256 indexed projectNumber, address indexed token, address indexed launchModule
);
event MetadataRegistryConfigured(address indexed metadataRegistry);
constructor(
address admin,
ProjectRegistry projectRegistry_,
LaunchTemplateRegistry templateRegistry_,
IIdentityAttestationVerifier identityVerifier_,
ILaunchRequirements launchRequirements_,
ILaunchFeeManager launchFeeManager_,
IStandardTokenLaunchModuleFactory launchModuleFactory_
) {
if (
admin == address(0) || address(projectRegistry_) == address(0)
|| address(templateRegistry_) == address(0)
|| address(identityVerifier_) == address(0)
|| address(launchRequirements_) == address(0)
|| address(launchFeeManager_) == address(0)
|| address(launchModuleFactory_) == address(0)
) revert InvalidAddress();
if (
address(projectRegistry_).code.length == 0
|| address(templateRegistry_).code.length == 0
|| address(identityVerifier_).code.length == 0
|| address(launchRequirements_).code.length == 0
|| address(launchFeeManager_).code.length == 0
|| address(launchModuleFactory_).code.length == 0
) revert InvalidAddress();
projectRegistry = projectRegistry_;
templateRegistry = templateRegistry_;
identityVerifier = identityVerifier_;
launchRequirements = launchRequirements_;
launchFeeManager = launchFeeManager_;
launchModuleFactory = launchModuleFactory_;
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(PAUSER_ROLE, admin);
}
function setMetadataRegistry(
IMetadataRegistry metadataRegistry_
) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (address(metadataRegistry) != address(0)) revert InvalidAddress();
if (address(metadataRegistry_) == address(0) || address(metadataRegistry_).code.length == 0)
{
revert InvalidAddress();
}
metadataRegistry = metadataRegistry_;
emit MetadataRegistryConfigured(address(metadataRegistry_));
}
/// @notice Creates a standard token through the configured identity, requirements, and launch modules.
function createToken(
LaunchRequest calldata request
)
external
payable
whenNotPaused
nonReentrant
returns (uint256 projectNumber, address token, address launchModule)
{
return _createToken(request, 0, 0, 0);
}
/// @notice Creates a token and executes a disclosed creator buy atomically.
function createTokenAndBuy(
LaunchRequest calldata request,
uint256 initialBuyAmount,
uint256 minTokensOut,
uint256 deadline
)
external
payable
whenNotPaused
nonReentrant
returns (uint256 projectNumber, address token, address launchModule)
{
if (initialBuyAmount == 0 || minTokensOut == 0 || deadline < block.timestamp) {
revert InvalidInitialBuy();
}
return _createToken(request, initialBuyAmount, minTokensOut, deadline);
}
function _createToken(
LaunchRequest calldata request,
uint256 initialBuyAmount,
uint256 minTokensOut,
uint256 deadline
) private returns (uint256 projectNumber, address token, address launchModule) {
LaunchTemplateRegistry.StandardTokenTemplate memory template =
templateRegistry.getTemplate(request.templateKey, request.templateVersion);
if (!template.enabled) revert TemplateDisabled();
// The template, not the creator, fixes the initial fee. A creator may
// still reduce it later through the curve's one-way reduction path.
if (request.creatorFeeBps != template.creatorFeeBps) revert InvalidCreatorFee();
if (msg.value != template.launchFeeWei + initialBuyAmount) revert IncorrectLaunchFee();
LaunchCreation memory creation;
creation.projectNumber = projectRegistry.nextProjectNumber();
(creation.normalizedNameHash, creation.normalizedSymbolHash, creation.launchModule) =
_prepareLaunch(request, creation.projectNumber);
creation.token =
_deployToken(request, template, creation.projectNumber, creation.launchModule);
IStandardTokenLaunchModule(creation.launchModule).initializeToken(creation.token);
_finalizeLaunch(request, template, creation, initialBuyAmount, minTokensOut, deadline);
return (creation.projectNumber, creation.token, creation.launchModule);
}
function _finalizeLaunch(
LaunchRequest calldata request,
LaunchTemplateRegistry.StandardTokenTemplate memory template,
LaunchCreation memory creation,
uint256 initialBuyAmount,
uint256 minTokensOut,
uint256 deadline
) private {
_registerProject(
creation.projectNumber,
creation.token,
request,
template,
creation.normalizedNameHash,
creation.normalizedSymbolHash
);
if (address(metadataRegistry) != address(0)) {
metadataRegistry.registerMetadata(
creation.projectNumber,
msg.sender,
request.metadataURI,
keccak256(bytes(request.metadataURI))
);
}
launchFeeManager.collectLaunchFee{ value: template.launchFeeWei }(
msg.sender, creation.projectNumber, request.templateKey, request.templateVersion
);
_recordLaunch(
request,
creation.projectNumber,
creation.token,
creation.launchModule,
creation.normalizedNameHash,
creation.normalizedSymbolHash
);
if (initialBuyAmount != 0) {
IStandardTokenLaunchModule(creation.launchModule).initialBuy{ value: initialBuyAmount }(
minTokensOut, msg.sender, deadline
);
}
}
function _prepareLaunch(
LaunchRequest calldata request,
uint256 projectNumber
) private returns (bytes32 normalizedNameHash, bytes32 normalizedSymbolHash, address module) {
bytes32 launchParametersHash = hashLaunchRequest(msg.sender, projectNumber, request);
(normalizedNameHash, normalizedSymbolHash) = identityVerifier.consumeAttestation(
msg.sender,
request.name,
request.symbol,
launchParametersHash,
request.identityAttestation
);
if (normalizedNameHash == bytes32(0) || normalizedSymbolHash == bytes32(0)) {
revert InvalidIdentityAttestation();
}
launchRequirements.reserveLaunch(
msg.sender, projectNumber, request.templateKey, request.templateVersion
);
module = launchModuleFactory.createLaunchModule(
msg.sender,
projectNumber,
request.templateKey,
request.templateVersion,
request.creatorFeeBps,
request.launchData
);
if (module == address(0) || module.code.length == 0) revert InvalidLaunchModule();
}
function _recordLaunch(
LaunchRequest calldata request,
uint256 projectNumber,
address token,
address launchModule,
bytes32 normalizedNameHash,
bytes32 normalizedSymbolHash
) private {
_launches[projectNumber] = TokenLaunchRecord({
creator: msg.sender,
token: token,
launchModule: launchModule,
templateKey: request.templateKey,
templateVersion: request.templateVersion,
creatorFeeBps: request.creatorFeeBps,
normalizedNameHash: normalizedNameHash,
normalizedSymbolHash: normalizedSymbolHash
});
projectNumberByToken[token] = projectNumber;
tokenAt[tokenCount] = token;
++tokenCount;
emit TokenCreated(
projectNumber,
token,
msg.sender,
launchModule,
request.templateKey,
request.templateVersion,
request.creatorFeeBps
);
emit LaunchOpened(projectNumber, token, launchModule);
}
function _deployToken(
LaunchRequest calldata request,
LaunchTemplateRegistry.StandardTokenTemplate memory template,
uint256 projectNumber,
address launchModule
) private returns (address token) {
token = address(
new EquifoldToken(
EquifoldToken.Initialization({
name: request.name,
symbol: request.symbol,
decimals: template.tokenDecimals,
initialSupply: template.tokenSupply,
burnable: template.burnable,
creator: msg.sender,
projectNumber: projectNumber,
metadataURI: request.metadataURI,
launchContract: launchModule
})
)
);
}
function _registerProject(
uint256 projectNumber,
address token,
LaunchRequest calldata request,
LaunchTemplateRegistry.StandardTokenTemplate memory template,
bytes32 normalizedNameHash,
bytes32 normalizedSymbolHash
) private {
projectRegistry.registerProject(
projectNumber,
ProjectRegistry.Registration({
creator: msg.sender,
productType: ProductType.STANDARD_TOKEN,
primaryContract: token,
templateKey: request.templateKey,
templateVersion: request.templateVersion,
metadataHash: keccak256(bytes(request.metadataURI)),
normalizedNameHash: normalizedNameHash,
normalizedSymbolHash: normalizedSymbolHash,
isGenesis: template.isGenesis,
isOfficial: template.isOfficial
})
);
}
/// @notice Hash bound into identity attestations to prevent cross-chain and
/// cross-request replay.
/// @dev The (factory-sequenced) project number is intentionally NOT bound
/// into the hash. Binding `nextProjectNumber()` serialized all launches — two
/// concurrent launches invalidated each other's attestation, enabling a
/// front-run DoS — while adding no replay protection beyond the per-creator
/// nonce already enforced by the verifier. The parameter is retained for ABI
/// stability and remains ignored.
function hashLaunchRequest(
address creator,
uint256,
LaunchRequest calldata request
) public view returns (bytes32) {
return keccak256(
abi.encode(
block.chainid,
address(this),
creator,
keccak256(bytes(request.name)),
keccak256(bytes(request.symbol)),
keccak256(bytes(request.metadataURI)),
request.templateKey,
request.templateVersion,
request.creatorFeeBps,
keccak256(request.launchData)
)
);
}
function getLaunch(
uint256 projectNumber
) external view returns (TokenLaunchRecord memory) {
return _launches[projectNumber];
}
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() external onlyRole(PAUSER_ROLE) {
_unpause();
}
}
Chain explorer952msChain node81ms