EquifoldV4StockLaunchModuleFactoryV3
0x934b878273a0becae9bdbad279d83856d36f762a
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
16,577 bytes
ABI entries
37
16 read · 7 write
License
none
Contract information
- Address
- 0x934b878273a0becae9bdbad279d83856d36f762a
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0xd03543d97B…F446Cd1418
- Creation tx
- 0xc09a89c8c2…e2f8a32365
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (16)
FACTORY_VERSION() → uint32
POOL_FEE() → uint24
TICK_SPACING() → int24
isModule(address) → bool
moduleByProject(uint256) → address
owner() → address
pendingOwner() → address
poolKeyOf(address) → tuple
poolManager() → address
projectByModule(address) → uint256
router() → address
splitRegistry() → address
standardTokenFactory() → address
stockOf(address) → address
stockRegistry() → address
treasury() → address
Events (6)
OwnershipTransferStartedOwnershipTransferredRouterConfiguredStandardTokenFactoryConfiguredV4StockModuleCreatedV4StockPoolCreated
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "initialOwner",
"type": "address"
},
{
"internalType": "contract IPoolManager",
"name": "poolManager_",
"type": "address"
},
{
"internalType": "address",
"name": "stockRegistry_",
"type": "address"
},
{
"internalType": "contract CreatorSplitRegistryV2",
"name": "splitRegistry_",
"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": "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": "RouterAlreadySet",
"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": "address",
"name": "router",
"type": "address"
}
],
"name": "RouterConfigured",
"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": "address",
"name": "stock",
"type": "address"
}
],
"name": "V4StockModuleCreated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "projectNumber",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "address",
"name": "stock",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "module",
"type": "address"
}
],
"name": "V4StockPoolCreated",
"type": "event"
},
{
"inputs": [],
"name": "FACTORY_VERSION",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POOL_FEE",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_SPACING",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"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": "address",
"name": "stock",
"type": "address"
},
{
"internalType": "int24",
"name": "launchTick",
"type": "int24"
}
],
"name": "createModule",
"outputs": [
{
"internalType": "address",
"name": "module",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "module",
"type": "address"
}
],
"name": "isModule",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"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"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "key",
"type": "tuple"
}
],
"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": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "poolKeyOf",
"outputs": [
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"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": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "router",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "router_",
"type": "address"
}
],
"name": "setRouter",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "standardTokenFactory_",
"type": "address"
}
],
"name": "setStandardTokenFactory",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "splitRegistry",
"outputs": [
{
"internalType": "contract CreatorSplitRegistryV2",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "standardTokenFactory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "stockOf",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "stockRegistry",
"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"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { IPoolManager } from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import { PoolKey } from "@uniswap/v4-core/src/types/PoolKey.sol";
import { PoolId, PoolIdLibrary } from "@uniswap/v4-core/src/types/PoolId.sol";
import { EquifoldV4StockLaunchModuleV3 } from "../dex/EquifoldV4StockLaunchModuleV3.sol";
import { CreatorSplitRegistryV2 } from "./CreatorSplitRegistryV2.sol";
/// @title Equifold stock-paired v4 launch module factory (gen4 v3, hookless)
/// @author Equifold
/// @notice The creation boundary for {EquifoldV4StockLaunchModuleV3}s: only the standard-token factory
/// may create modules, each is recorded ({isModule}) and registers its project's creator once
/// with the shared {CreatorSplitRegistryV2} (this factory holds `REGISTRAR_ROLE`), and the
/// shared launch router is wired once by the owner (it deploys after the modules). Hard-codes
/// the vertical's v4 economics (pool fee, tick spacing). This factory is also the vertical's
/// indexer-discovery source: a module calls back once its pool actually exists and the factory
/// emits {V4StockPoolCreated} with the real token + pool.
/// @dev A byte-faithful clone of {EquifoldV4StockLaunchModuleFactoryV2} with the gen4 deltas: it holds
/// an immutable {CreatorSplitRegistryV2} and calls `registerProject` at module creation (so the
/// module's creator fee leg can be redirected without a redeploy), and it constructs the gen4 v3
/// module. The event name/layout {V4StockPoolCreated} is UNCHANGED from v2 so the indexer reuses
/// its decoder; the factory ADDRESS distinguishes gen4. Launch pools carry NO hook
/// (`hooks == address(0)`) — externally routable from block one; anti-snipe discipline lives
/// entirely on the launch token ({EquifoldRestrictedTokenV4}).
contract EquifoldV4StockLaunchModuleFactoryV3 is Ownable2Step {
using PoolIdLibrary for PoolKey;
error AlreadyConfigured();
error InvalidAddress();
error NotModule();
error NotStandardTokenFactory();
error RouterAlreadySet();
uint32 public constant FACTORY_VERSION = 1;
/// @notice Hard-coded stock-paired v4 economics (binding launch parameters).
uint24 public constant POOL_FEE = 10_000;
int24 public constant TICK_SPACING = 200;
IPoolManager public immutable poolManager;
address public immutable stockRegistry;
CreatorSplitRegistryV2 public immutable splitRegistry;
address payable public immutable treasury;
address public standardTokenFactory;
/// @notice The shared launch router every module routes its initial buy through. Owner-set once.
address public router;
mapping(uint256 projectNumber => address module) public moduleByProject;
mapping(address module => uint256 projectNumber) public projectByModule;
mapping(address module => bool) public isModule;
mapping(address module => address stock) private _stockByModule;
mapping(address token => PoolKey key) private _poolKeyOf;
mapping(address token => address stock) private _stockOf;
event StandardTokenFactoryConfigured(address indexed standardTokenFactory);
event RouterConfigured(address indexed router);
event V4StockModuleCreated(
uint256 indexed projectNumber,
address indexed module,
address indexed creator,
address stock
);
/// @notice Indexer-discovery event: emitted once a module's v4 pool actually exists.
event V4StockPoolCreated(
uint256 indexed projectNumber,
address indexed token,
bytes32 indexed poolId,
address stock,
address module
);
constructor(
address initialOwner,
IPoolManager poolManager_,
address stockRegistry_,
CreatorSplitRegistryV2 splitRegistry_,
address payable treasury_
) Ownable(initialOwner) {
if (
initialOwner == address(0) || address(poolManager_) == address(0)
|| stockRegistry_ == address(0) || address(splitRegistry_) == address(0)
|| treasury_ == address(0)
) revert InvalidAddress();
if (
address(poolManager_).code.length == 0 || stockRegistry_.code.length == 0
|| address(splitRegistry_).code.length == 0
) revert InvalidAddress();
poolManager = poolManager_;
stockRegistry = stockRegistry_;
splitRegistry = splitRegistry_;
treasury = treasury_;
}
/// @notice One-time wiring of the standard-token factory allowed to create modules.
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_);
}
/// @notice One-time wiring of the shared launch router (it deploys after this factory).
function setRouter(
address router_
) external onlyOwner {
if (router != address(0)) revert RouterAlreadySet();
if (router_ == address(0) || router_.code.length == 0) revert InvalidAddress();
router = router_;
emit RouterConfigured(router_);
}
/// @notice Creates a launch module for `projectNumber`, quoted in `stock`, opening at `launchTick`,
/// and registers the project's creator with the shared split registry.
/// @dev Standard-token-factory-only.
function createModule(
address creator,
uint256 projectNumber,
address stock,
int24 launchTick
) external returns (address module) {
if (msg.sender != standardTokenFactory) revert NotStandardTokenFactory();
if (creator == address(0) || stock == address(0)) revert InvalidAddress();
if (projectNumber == 0 || moduleByProject[projectNumber] != address(0)) {
revert InvalidAddress();
}
EquifoldV4StockLaunchModuleV3 created = new EquifoldV4StockLaunchModuleV3(
EquifoldV4StockLaunchModuleV3.Initialization({
moduleFactory: address(this),
standardTokenFactory: standardTokenFactory,
creator: creator,
treasury: treasury,
poolManager: poolManager,
stockRegistry: stockRegistry,
stock: stock,
splitRegistry: splitRegistry,
projectNumber: projectNumber,
poolFee: POOL_FEE,
tickSpacing: TICK_SPACING,
launchTick: launchTick
})
);
module = address(created);
moduleByProject[projectNumber] = module;
projectByModule[module] = projectNumber;
isModule[module] = true;
_stockByModule[module] = stock;
splitRegistry.registerProject(projectNumber, creator);
emit V4StockModuleCreated(projectNumber, module, creator, stock);
}
/// @notice Called once by a module this factory created, right after its pool is initialized, to
/// record the token->pool/stock mapping and emit the indexer-discovery event.
/// @dev Restricted to modules this factory actually created.
function notifyPoolInitialized(
address token,
PoolKey calldata key
) external {
uint256 projectNumber = projectByModule[msg.sender];
if (projectNumber == 0) revert NotModule();
address stock = _stockByModule[msg.sender];
_poolKeyOf[token] = key;
_stockOf[token] = stock;
emit V4StockPoolCreated(projectNumber, token, PoolId.unwrap(key.toId()), stock, msg.sender);
}
/// @notice The launch pool key for a launched token (zero-valued until its pool exists).
function poolKeyOf(
address token
) external view returns (PoolKey memory) {
return _poolKeyOf[token];
}
/// @notice The stock a launched token's pool is quoted in (zero until its pool exists).
function stockOf(
address token
) external view returns (address) {
return _stockOf[token];
}
}
Chain explorer696msChain node95ms