Overview
Invite-gated token launches on Uniswap v4. Creators open a Continuous Clearing Auction (CCA); after graduation, proceeds seed a locked v4 pool. This repo adds invite validation and hook-based fee distribution on top of Uniswap’s Liquidity Launchpad.
Mechanics
Launches use Uniswap’s CCA: bidders set a budget and max price; each block, a release schedule allocates tokens to active bids at a uniform clearing price. Early participation is rewarded and last-minute sniping is ineffective — see the CCA overview and whitepaper.
Flow in this stack:
- Launch —
CcaLaunchFactorymints a fixed-supplyLaunchToken, seeds invite codes, and opens a CCA via LiquidityLauncher → LBPStrategy. - Bid — Participants
submitBidwith invitehookData, checked byInviteValidationHook/InviteRegistry(referral weight is recorded for later fee claims). - Migrate — After the auction ends, anyone can call LBP
migrateto seed an ETH/token v4 pool at the discovered price, withLaunchFeeHookattached. - Trade & claim — Swaps accrue hook fees;
harvestpushes balances toFeeDistributorfor creator / referrer / platform claims. Auction winners claim tokens after the configured claim block.
Fees
Defaults (overridable at launch where noted):
| Parameter | Default | Notes |
|---|---|---|
| Token split | 50% auction / 50% LP | auctionSupplyBps; remainder reserved for the post-migrate position |
| Pool LP fee | 0.1% | Accrues to the locked LP NFT; autocompounded back into the pool via CompoundingClaimRecipient |
| Hook fee | 0.4% | Taken by LaunchFeeHook on the unspecified swap amount (afterSwap) |
| Hook fee split | 20% creator / 75% referrers / 5% platform | Fixed in FeeDistributor; referrers share pro-rata by invite count |
Example — $10M trading volume
Assume a $50k auction raise seeds the locked LP (default 50/50 auction/LP split).
| Fee | Rate | On $10M volume |
|---|---|---|
| Pool LP fee (autocompounded) | 0.1% | $10,000 reinvested into the position → liquidity $50k → $60k |
| Hook fee (distributed) | 0.4% | $40,000 harvested into FeeDistributor |
Hook fee split of that $40,000:
- Creator claims $8,000 (20%)
- Platform claims $2,000 (5%)
- Referrer pool gets $30,000 (75%). Shared pro-rata across referrers by invite count — with 100 equal-weight referrers, each can claim $300.
Contracts
| Contract | Role |
|---|---|
CcaLaunchFactory | Create token + CCA/LBP launch |
LaunchToken | Fixed-supply ERC-20 + metadata |
InviteRegistry | Invite codes + referral weights |
InviteValidationHook | CCA bid gate (hookData) |
LaunchFeeHook | Post-migrate swap hook fee |
FeeDistributor | Claimable 20/75/5 fee split |
IReferralSource | Referral weight interface |
Upstream Uniswap pieces used at runtime (not in this repo’s src/): LiquidityLauncher, LBPStrategy, Continuous Clearing Auction, CompoundingClaimRecipient.
CcaLaunchFactory
Creates LaunchToken + CCA/LBP distribution with invite gating and fee hook.
Constants
DEFAULT_POOL_LP_FEE
uint24 public constant DEFAULT_POOL_LP_FEE = 1_000
DEFAULT_HOOK_FEE
uint24 public constant DEFAULT_HOOK_FEE = 4_000
DEFAULT_TICK_SPACING
int24 public constant DEFAULT_TICK_SPACING = 60
DEFAULT_AUCTION_SUPPLY_BPS
uint16 public constant DEFAULT_AUCTION_SUPPLY_BPS = 5_000
DEFAULT_AUCTION_TICK_SPACING
uint256 public constant DEFAULT_AUCTION_TICK_SPACING = 100 << FixedPoint96.RESOLUTION
DEFAULT_FLOOR_PRICE
uint256 public constant DEFAULT_FLOOR_PRICE = 1000 << FixedPoint96.RESOLUTION
launcher
ILiquidityLauncher public immutable launcher
lbpStrategy
ILBPStrategy public immutable lbpStrategy
ccaFactory
IDistributorFactory public immutable ccaFactory
invites
InviteRegistry public immutable invites
distributor
FeeDistributor public immutable distributor
feeHook
LaunchFeeHook public immutable feeHook
positionRecipient
address public immutable positionRecipient
State Variables
launchCount
uint256 public launchCount
launches
mapping(uint256 => Launch) public launches
launchIdByToken
mapping(address => uint256) public launchIdByToken
launchIdByAuction
mapping(address => uint256) public launchIdByAuction
Functions
constructor
constructor(
ILiquidityLauncher launcher_,
ILBPStrategy lbpStrategy_,
IDistributorFactory ccaFactory_,
InviteRegistry invites_,
FeeDistributor distributor_,
LaunchFeeHook feeHook_,
address positionRecipient_
) ;
createLaunch
function createLaunch(CreateParams calldata params)
external
returns (uint256 launchId, address token, address auction);
getLaunch
function getLaunch(uint256 launchId) external view returns (Launch memory);
_buildSteps
function _buildSteps(uint64 auctionBlocks) internal pure returns (bytes memory steps);
_poolKey
function _poolKey(address currency, address token, uint24 fee, address hook)
internal
pure
returns (PoolKey memory key);
Events
LaunchCreated
event LaunchCreated(
uint256 indexed launchId,
address indexed creator,
address token,
address auction,
uint64 endBlock,
uint128 minRaise,
uint24 poolLpFee,
uint24 hookFee
);
Errors
EmptyName
error EmptyName();
InvalidDuration
error InvalidDuration();
InvalidSupplyBps
error InvalidSupplyBps();
InvalidFee
error InvalidFee();
NeedInvites
error NeedInvites();
Structs
Metadata
struct Metadata {
string name;
string symbol;
string description;
string image;
string website;
string twitter;
string telegram;
}
CreateParams
struct CreateParams {
Metadata metadata;
uint64 auctionBlocks;
uint128 minRaise;
uint16 auctionSupplyBps;
uint24 poolLpFee;
uint24 hookFee;
bytes32 salt;
bytes32[] inviteCodes;
}
Launch
struct Launch {
address creator;
address token;
address auction;
uint64 startBlock;
uint64 endBlock;
uint64 claimBlock;
uint128 minRaise;
uint24 poolLpFee;
uint24 hookFee;
}
LaunchToken
Inherits: ERC20
Fixed-supply ERC-20 with immutable launch metadata.
Constants
TOTAL_SUPPLY
uint256 public constant TOTAL_SUPPLY = 1_000_000_000 ether
factory
address public immutable factory
State Variables
description
string public description
image
string public image
website
string public website
string public twitter
telegram
string public telegram
Functions
constructor
constructor(
string memory name_,
string memory symbol_,
string memory description_,
string memory image_,
string memory website_,
string memory twitter_,
string memory telegram_,
address recipient
) ERC20(name_, symbol_);
InviteRegistry
Inherits: IReferralSource
Invite codes and referral weights.
Deployed once per chain; shared across all auctions (keyed by auction address). Authority for bid gating + fee attribution (offchain DB is UX-only):
- Factory registers each auction and seeds creator invite codes (bytes32 → issuer).
- InviteValidationHook calls useInvite on first CCA bid; unknown/self invites revert.
- Codes are reusable; only the first participation per bidder credits the issuer.
- Creator or prior participants may createInvites; FeeDistributor reads referral counts.
State Variables
factory
CcaLaunchFactory — only caller allowed to registerAuction / seedInvites.
address public factory
validationHook
address public validationHook
creatorOf
mapping(address => address) public creatorOf
auctionOfToken
mapping(address => address) public auctionOfToken
inviteIssuer
mapping(address => mapping(bytes32 => address)) public inviteIssuer
participated
mapping(address => mapping(address => bool)) public participated
referralCountOf
mapping(address => mapping(address => uint256)) public referralCountOf
totalReferralCountOf
mapping(address => uint256) public totalReferralCountOf
invitesCreated
mapping(address => uint256) public invitesCreated
Functions
setFactory
function setFactory(address factory_) external;
setValidationHook
function setValidationHook(address hook_) external;
registerAuction
function registerAuction(address auction, address token, address creator) external;
seedInvites
function seedInvites(address auction, address issuer, bytes32[] calldata codes) external;
createInvites
function createInvites(address auction, bytes32[] calldata codes) external;
_createInvites
function _createInvites(address auction, address issuer, bytes32[] calldata codes) internal;
useInvite
Called by InviteValidationHook during CCA submitBid.
function useInvite(address auction, address bidder, bytes32 code) external;
referralCount
function referralCount(address auction, address referrer) external view returns (uint256);
totalReferralCount
function totalReferralCount(address auction) external view returns (uint256);
Events
FactorySet
event FactorySet(address indexed factory);
ValidationHookSet
event ValidationHookSet(address indexed hook);
AuctionRegistered
event AuctionRegistered(address indexed auction, address indexed token, address indexed creator);
InviteCreated
event InviteCreated(address indexed auction, bytes32 indexed code, address indexed issuer);
InviteUsed
event InviteUsed(address indexed auction, address indexed bidder, address indexed referrer, bytes32 code);
Errors
AlreadySet
error AlreadySet();
NotAuthorized
error NotAuthorized();
InvalidInvite
error InvalidInvite();
InviteExists
error InviteExists();
ZeroAddress
error ZeroAddress();
InviteValidationHook
Inherits: IValidationHook
CCA bid validation: require a valid invite code in hookData.
Constants
registry
InviteRegistry public immutable registry
Functions
constructor
constructor(InviteRegistry registry_) ;
validate
Validate a bid
MUST revert if the bid is invalid
function validate(uint256, uint128, address owner, address, bytes calldata hookData) external;
Parameters
| Name | Type | Description |
|---|---|---|
<none> | uint256 | |
<none> | uint128 | |
owner | address | The owner of the bid |
<none> | address | |
hookData | bytes | Additional data to pass to the hook required for validation |
Errors
InvalidHookData
error InvalidHookData();
LaunchFeeHook
Inherits: InitializerHook, IUnlockCallback
LBP-compatible InitializerHook that charges an afterSwap hook fee into FeeDistributor.
Constants
MAX_HOOK_FEE
uint24 internal constant MAX_HOOK_FEE = 1e6
distributor
FeeDistributor public immutable distributor
defaultHookFee
uint24 public immutable defaultHookFee
State Variables
hookFeeOf
mapping(PoolId => uint24) public hookFeeOf
feeConfigured
mapping(PoolId => bool) public feeConfigured
Functions
constructor
constructor(IPoolManager poolManager_, address authorized_, FeeDistributor distributor_, uint24 defaultHookFee_)
InitializerHook(poolManager_, authorized_);
setPoolHookFee
function setPoolHookFee(PoolId poolId, uint24 fee) external;
getHookPermissions
function getHookPermissions() public pure override returns (Hooks.Permissions memory);
_afterSwap
function _afterSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
BalanceDelta delta,
bytes calldata
) internal override returns (bytes4, int128);
harvest
function harvest(PoolKey calldata key) external;
unlockCallback
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory);
receive
receive() external payable;
Events
PoolHookFeeSet
event PoolHookFeeSet(PoolId indexed poolId, uint24 fee);
HookFee
event HookFee(bytes32 indexed poolId, address indexed sender, uint128 feeAmount0, uint128 feeAmount1);
Errors
InvalidFee
error InvalidFee();
HookFeeTooLarge
error HookFeeTooLarge();
FeeDistributor
Accrues hook fees and pays creator / referrers / platform via claim.
Split: 20% creator, 75% referrers (pro-rata by referral count), 5% platform.
Constants
PLATFORM
address public constant PLATFORM = 0xBb6f397d9d8bf128dDa607005397F539B43CD710
CREATOR_BPS
uint16 public constant CREATOR_BPS = 2_000
REFERRERS_BPS
uint16 public constant REFERRERS_BPS = 7_500
PLATFORM_BPS
uint16 public constant PLATFORM_BPS = 500
BPS_DENOM
uint16 public constant BPS_DENOM = 10_000
State Variables
referrals
IReferralSource public referrals
hook
address public hook
registrar
address public registrar
pools
mapping(PoolId => PoolInfo) public pools
creatorOwed
mapping(PoolId => mapping(address => uint256)) public creatorOwed
platformOwed
mapping(PoolId => mapping(address => uint256)) public platformOwed
referrerPool
mapping(PoolId => mapping(address => uint256)) public referrerPool
referrerClaimed
mapping(PoolId => mapping(address => mapping(address => uint256))) public referrerClaimed
Functions
onlyHook
modifier onlyHook() ;
receive
receive() external payable;
setReferrals
function setReferrals(address referrals_) external;
setHook
function setHook(address hook_) external;
setRegistrar
function setRegistrar(address registrar_) external;
registerPool
function registerPool(PoolId poolId, address auction, address creator) external;
notifyFee
function notifyFee(PoolId poolId, address currency, uint256 amount) external payable onlyHook;
claimCreator
function claimCreator(PoolId poolId, address currency) external returns (uint256 amount);
claimPlatform
function claimPlatform(PoolId poolId, address currency) external returns (uint256 amount);
claimReferrer
function claimReferrer(PoolId poolId, address currency) external returns (uint256 amount);
pendingReferrer
function pendingReferrer(PoolId poolId, address currency, address referrer) external view returns (uint256);
_pay
function _pay(address currency, address to, uint256 amount) internal;
Events
ReferralsSet
event ReferralsSet(address indexed referrals);
HookSet
event HookSet(address indexed hook);
RegistrarSet
event RegistrarSet(address indexed registrar);
PoolRegistered
event PoolRegistered(PoolId indexed poolId, address indexed auction, address indexed creator);
FeeNotified
event FeeNotified(PoolId indexed poolId, address indexed currency, uint256 amount);
Claimed
event Claimed(PoolId indexed poolId, address indexed currency, address indexed to, uint256 amount);
Errors
NotAuthorized
error NotAuthorized();
NotHook
error NotHook();
NotRegistered
error NotRegistered();
NothingToClaim
error NothingToClaim();
InvalidAmount
error InvalidAmount();
TransferFailed
error TransferFailed();
AlreadySet
error AlreadySet();
Structs
PoolInfo
struct PoolInfo {
address creator;
address auction;
bool registered;
}
IReferralSource
Read referral weights for FeeDistributor claims.
Functions
referralCount
function referralCount(address auction, address referrer) external view returns (uint256);
totalReferralCount
function totalReferralCount(address auction) external view returns (uint256);