EquifoldV3LaunchModuleFactory
0x7a27d4c9c339c048ae6740ccb55095aa9611dbff
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
16,434 bytes
ABI entries
46
22 read · 7 write
License
none
Contract information
- Address
- 0x7a27d4c9c339c048ae6740ccb55095aa9611dbff
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0xd03543d97B…F446Cd1418
- Creation tx
- 0x2dbfc58d16…d4b17da3b5
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (22)
FACTORY_VERSION() → uint32
INITIAL_TICK() → int24
MAX_TX_BPS() → uint16
MAX_WALLET_BPS() → uint16
POOL_FEE() → uint24
RESTRICTION_BLOCKS() → uint32
feeCollector() → address
launchRequirements() → address
maxTxBps() → uint16
maxWalletBps() → uint16
moduleByProject(uint256) → address
owner() → address
pendingOwner() → address
poolFee() → uint24
positionManager() → address
projectByModule(address) → uint256
restrictionBlocks() → uint32
standardTokenFactory() → address
swapRouter() → address
treasury() → address
v3Factory() → address
weth() → address
Events (6)
OwnershipTransferStartedOwnershipTransferredProjectReleasedStandardTokenFactoryConfiguredV3NativeModuleCreatedV3NativePoolCreated
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "initialOwner",
"type": "address"
},
{
"internalType": "contract ILaunchRequirements",
"name": "launchRequirements_",
"type": "address"
},
{
"internalType": "contract LaunchpadFeeCollector",
"name": "feeCollector_",
"type": "address"
},
{
"internalType": "contract IWrappedNative",
"name": "weth_",
"type": "address"
},
{
"internalType": "address",
"name": "v3Factory_",
"type": "address"
},
{
"internalType": "address",
"name": "positionManager_",
"type": "address"
},
{
"internalType": "address",
"name": "swapRouter_",
"type": "address"
},
{
"internalType": "address payable",
"name": "treasury_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyConfigured",
"type": "error"
},
{
"inputs": [],
"name": "InvalidAddress",
"type": "error"
},
{
"inputs": [],
"name": "InvalidLaunchData",
"type": "error"
},
{
"inputs": [],
"name": "NotInitialized",
"type": "error"
},
{
"inputs": [],
"name": "NotModule",
"type": "error"
},
{
"inputs": [],
"name": "NotStandardTokenFactory",
"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": "RestrictionActive",
"type": "error"
},
{
"inputs": [],
"name": "UnknownProject",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"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": true,
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"name": "ProjectReleased",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "standardTokenFactory",
"type": "address"
}
],
"name": "StandardTokenFactoryConfigured",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "module",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
}
],
"name": "V3NativeModuleCreated",
"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": "pool",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "module",
"type": "address"
}
],
"name": "V3NativePoolCreated",
"type": "event"
},
{
"inputs": [],
"name": "FACTORY_VERSION",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "INITIAL_TICK",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_TX_BPS",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_WALLET_BPS",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POOL_FEE",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "RESTRICTION_BLOCKS",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "templateKey",
"type": "bytes32"
},
{
"internalType": "uint32",
"name": "templateVersion",
"type": "uint32"
},
{
"internalType": "uint16",
"name": "",
"type": "uint16"
},
{
"internalType": "bytes",
"name": "launchData",
"type": "bytes"
}
],
"name": "createLaunchModule",
"outputs": [
{
"internalType": "address",
"name": "launchModule",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "feeCollector",
"outputs": [
{
"internalType": "contract LaunchpadFeeCollector",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchRequirements",
"outputs": [
{
"internalType": "contract ILaunchRequirements",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxTxBps",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "maxWalletBps",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"name": "moduleByProject",
"outputs": [
{
"internalType": "address",
"name": "module",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "pool",
"type": "address"
}
],
"name": "notifyPoolInitialized",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolFee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "module",
"type": "address"
}
],
"name": "projectByModule",
"outputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
}
],
"name": "releaseProject",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "restrictionBlocks",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "standardTokenFactory_",
"type": "address"
}
],
"name": "setStandardTokenFactory",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "standardTokenFactory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "swapRouter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "treasury",
"outputs": [
{
"internalType": "address payable",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "v3Factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "weth",
"outputs": [
{
"internalType": "contract IWrappedNative",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { EquifoldV3LaunchModule } from "../dex/EquifoldV3LaunchModule.sol";
import { EquifoldRestrictedToken } from "./EquifoldRestrictedToken.sol";
import { LaunchpadFeeCollector } from "./LaunchpadFeeCollector.sol";
import { IFeeShareRegistry } from "../interfaces/IFeeShareRegistry.sol";
import { ILaunchRequirements } from "../interfaces/ILaunchRequirements.sol";
import { IStandardTokenLaunchModuleFactory } from "../interfaces/IStandardTokenLaunchModule.sol";
import { IWrappedNative } from "../interfaces/IWrappedNative.sol";
/// @title Equifold gen3 V3-native launch module factory
/// @author Equifold
/// @notice The creation boundary for {EquifoldV3LaunchModule}s, mirroring {BondingCurveFactory}:
/// only the StandardTokenFactory may create modules, each is registered with the launchpad
/// fee collector, and each starts with the default single-creator fee split. Hard-codes the
/// gen3 V3-native economics (pool fee, launch tick, per-wallet/per-tx buy caps, restriction
/// window) and exposes them as config getters consumed when deploying the launch token. This
/// factory is also the launch requirements' lifecycle manager: it releases a project's
/// reservation once the token's launch restriction window has elapsed.
contract EquifoldV3LaunchModuleFactory is IStandardTokenLaunchModuleFactory, Ownable2Step {
error AlreadyConfigured();
error InvalidAddress();
error InvalidLaunchData();
error NotInitialized();
error NotModule();
error NotStandardTokenFactory();
error RestrictionActive();
error UnknownProject();
uint32 public constant FACTORY_VERSION = 1;
/// @notice Hard-coded gen3 V3-native economics (binding launch parameters).
uint24 public constant POOL_FEE = 10_000;
int24 public constant INITIAL_TICK = -203_600;
uint16 public constant MAX_WALLET_BPS = 300;
uint16 public constant MAX_TX_BPS = 330;
uint32 public constant RESTRICTION_BLOCKS = 3000;
ILaunchRequirements public immutable launchRequirements;
LaunchpadFeeCollector public immutable feeCollector;
IWrappedNative public immutable weth;
address public immutable v3Factory;
address public immutable positionManager;
address public immutable swapRouter;
address payable public immutable treasury;
address public standardTokenFactory;
mapping(uint256 projectNumber => address module) public moduleByProject;
mapping(address module => uint256 projectNumber) public projectByModule;
event StandardTokenFactoryConfigured(address indexed standardTokenFactory);
event V3NativeModuleCreated(
uint256 indexed projectNumber,
address indexed module,
address indexed creator,
bytes32 templateKey,
uint32 templateVersion
);
/// @notice DEX-discovery event: emitted once a module's V3 pool actually exists.
event V3NativePoolCreated(
uint256 indexed projectNumber, address indexed token, address indexed pool, address module
);
event ProjectReleased(uint256 indexed projectNumber);
constructor(
address initialOwner,
ILaunchRequirements launchRequirements_,
LaunchpadFeeCollector feeCollector_,
IWrappedNative weth_,
address v3Factory_,
address positionManager_,
address swapRouter_,
address payable treasury_
) Ownable(initialOwner) {
if (
initialOwner == address(0) || address(launchRequirements_) == address(0)
|| address(feeCollector_) == address(0) || address(weth_) == address(0)
|| v3Factory_ == address(0) || positionManager_ == address(0)
|| swapRouter_ == address(0) || treasury_ == address(0)
) revert InvalidAddress();
if (
address(launchRequirements_).code.length == 0 || address(feeCollector_).code.length == 0
|| address(weth_).code.length == 0 || v3Factory_.code.length == 0
|| positionManager_.code.length == 0 || swapRouter_.code.length == 0
) revert InvalidAddress();
launchRequirements = launchRequirements_;
feeCollector = feeCollector_;
weth = weth_;
v3Factory = v3Factory_;
positionManager = positionManager_;
swapRouter = swapRouter_;
treasury = treasury_;
}
/// @notice One-time wiring of the StandardTokenFactory allowed to create modules.
/// @dev Mirrors {BondingCurveFactory.setStandardTokenFactory}.
function setStandardTokenFactory(
address standardTokenFactory_
) external onlyOwner {
if (standardTokenFactory != address(0)) revert AlreadyConfigured();
if (standardTokenFactory_ == address(0) || standardTokenFactory_.code.length == 0) {
revert InvalidAddress();
}
standardTokenFactory = standardTokenFactory_;
emit StandardTokenFactoryConfigured(standardTokenFactory_);
}
/// @inheritdoc IStandardTokenLaunchModuleFactory
/// @dev Mirrors {BondingCurveFactory.createLaunchModule}: StandardTokenFactory-only, empty
/// `launchData`, then deploy → record → register with the fee collector → default split.
/// `creatorFeeBps` is unused here — the V3 module's fee split is a fixed 70/30.
function createLaunchModule(
address creator,
uint256 projectNumber,
bytes32 templateKey,
uint32 templateVersion,
uint16, // creatorFeeBps — unused: the V3 module uses a fixed 70/30 split, not a configurable bps
bytes calldata launchData
) external returns (address launchModule) {
if (msg.sender != standardTokenFactory) revert NotStandardTokenFactory();
if (launchData.length != 0) revert InvalidLaunchData();
EquifoldV3LaunchModule module = new EquifoldV3LaunchModule(
EquifoldV3LaunchModule.Initialization({
moduleFactory: address(this),
standardTokenFactory: standardTokenFactory,
creator: creator,
treasury: treasury,
feeCollector: address(feeCollector),
weth: weth,
v3Factory: v3Factory,
positionManager: positionManager,
swapRouter: swapRouter,
projectNumber: projectNumber,
poolFee: POOL_FEE,
initialTick: INITIAL_TICK
})
);
launchModule = address(module);
moduleByProject[projectNumber] = launchModule;
projectByModule[launchModule] = projectNumber;
feeCollector.registerCurve(projectNumber, launchModule, creator);
feeCollector.initializeCreatorSplit(projectNumber, _defaultCreatorSplit(creator));
emit V3NativeModuleCreated(
projectNumber, launchModule, creator, templateKey, templateVersion
);
}
/// @notice Called once by a module this factory created, right after its pool is initialized, to
/// emit the DEX-discovery event with the real token + pool addresses.
/// @dev Restricted to modules this factory actually created.
function notifyPoolInitialized(
address token,
address pool
) external {
uint256 projectNumber = projectByModule[msg.sender];
if (projectNumber == 0) revert NotModule();
emit V3NativePoolCreated(projectNumber, token, pool, msg.sender);
}
/// @notice Releases a project's launch reservation once its token's restriction window has
/// elapsed. Permissionless — the elapsed window is the only gate.
/// @dev This factory is the {ILaunchRequirements} lifecycle manager; it forwards the release.
function releaseProject(
uint256 projectNumber
) external {
address module = moduleByProject[projectNumber];
if (module == address(0)) revert UnknownProject();
address token = EquifoldV3LaunchModule(payable(module)).token();
if (token == address(0)) revert NotInitialized();
if (block.number <= EquifoldRestrictedToken(token).restrictionEndBlock()) {
revert RestrictionActive();
}
launchRequirements.releaseLaunch(projectNumber);
emit ProjectReleased(projectNumber);
}
/// @notice The launch token's pool fee tier.
function poolFee() external pure returns (uint24) {
return POOL_FEE;
}
/// @notice Per-recipient balance cap (bps of supply) during the restriction window.
function maxWalletBps() external pure returns (uint16) {
return MAX_WALLET_BPS;
}
/// @notice Per-recipient cumulative buy cap (bps of supply) during the restriction window.
function maxTxBps() external pure returns (uint16) {
return MAX_TX_BPS;
}
/// @notice Length, in blocks, of the post-launch buy-restriction window.
function restrictionBlocks() external pure returns (uint32) {
return RESTRICTION_BLOCKS;
}
/// @dev Every launch starts with a single WALLET recipient (the creator) holding 100% of the
/// creator fee share. Copied from {BondingCurveFactory._defaultCreatorSplit}.
function _defaultCreatorSplit(
address creator
) private pure returns (IFeeShareRegistry.Recipient[] memory recipients) {
recipients = new IFeeShareRegistry.Recipient[](1);
recipients[0] = IFeeShareRegistry.Recipient({
kind: IFeeShareRegistry.RecipientKind.WALLET,
id: bytes32(uint256(uint160(creator))),
bps: 10_000
});
}
}
Chain explorer1691msChain node88ms