etherflow.fun
reference10 min read

Contract reference

Three contracts, around 600 lines of Solidity between them. Everything public is listed here.

LanguageSolidity 0.8.26, via-ir, optimizer at 44,444,444 runs to match v4-core
DependenciesUniswap v4-core and v4-periphery, OpenZeppelin Contracts v5.4.0, solmate
Tests26 Foundry tests, including a full rehearsal of the deploy path
AuditNone

FlowToken

OpenZeppelin ERC20 plus two metadata strings. No mint, no owner, no pause, no transfer hook, no blocklist. Deployed fresh for every launch.

constructor(name, symbol, supply, mintTo, image, bio)Mints the entire supply to mintTo, which is always the launchpad.
launchpad() → addressImmutable. Provenance only, it confers no rights.
image() → stringCreator supplied. Usually a data: URI written at launch, so the coin renders with no external host.
bio() → stringCreator supplied, up to 280 characters through the UI.
banner(), website(), x(), telegram(), discord()The rest of the metadata, all optional, all set once at construction.
meta() → MetaAll seven strings in one call, which is what the front end reads.

FlowHook

One singleton hook serving every Etherflow pool, deployed at a CREATE2 address whose low 14 bits encode its permissions: beforeInitialize, beforeSwap, afterSwap, and the return-delta flag for each swap hook.

Constants

MAX_FEE_BPS = 2002%, the hard ceiling on any pool's fee. Checked at registration, no way around it.
BPS = 10000Denominator.

State

poolManagerImmutable. The v4 PoolManager this hook is bound to.
launchpadSettable once, by admin, right after deployment. The only address allowed to register pools.
treasuryReceives the platform share. Movable by admin, which never affects balances already credited.
adminMay set the launchpad once, move the treasury, and hand the role on. Nothing else.
earned(address) → uint256Claimable ETH per address.
feesByPool(PoolId) → uint256Lifetime fee taken by one pool.
feesTotal() → uint256Lifetime fee across every pool.

Functions

register(key, beneficiary, feeBps, shareBps)Launchpad only. Writes a pool's terms. Reverts if that pool is already registered, so terms are written exactly once.
configOf(PoolId) → PoolConfigThe beneficiary, fee and split for a pool.
claim(address to) → uint256Sends the caller's whole balance to to. Reverts if the balance is zero or the transfer fails.
beforeInitialize(...)Pool manager only. Reverts unless the pool was registered first.
beforeSwap(...)Pool manager only. Charges the ETH leg when ETH is the named currency.
afterSwap(...)Pool manager only. Charges the ETH leg when it is not.
setLaunchpad / setTreasury / setAdminAdmin only. Cannot touch pools, terms, or balances.

Events

event PoolRegistered(PoolId indexed id, address indexed beneficiary,
                    uint16 feeBps, uint16 beneficiaryShareBps);
event FeeTaken(PoolId indexed id, address indexed beneficiary,
               uint256 toBeneficiary, uint256 toTreasury);
event Claimed(address indexed account, address indexed to, uint256 amount);

Etherflow

The launchpad. Holds every launch position and the registry.

Constants

POOL_FEE = 100001% LP fee on every Etherflow pool.
TICK_SPACING = 200Tick spacing on every Etherflow pool.
MIN_LIQUIDITY = 0.005 etherFloor on the ETH seeded at launch.
BURN = 0x…dEaDWhere rounding dust goes.

Launching

struct LaunchParams {
    string  name;                 // 1 to 64 characters
    string  symbol;               // 1 to 16 characters
    uint256 supply;               // in wei, 18 decimals
    address beneficiary;          // receives the fee share
    uint16  feeBps;               // 1 to 200
    uint16  beneficiaryShareBps;  // 0 to 10000
    string  image;
    string  bio;
}

function launch(LaunchParams calldata p)
    external payable
    returns (address token, PoolId poolId);

// msg.value = launchFee + liquidity, and liquidity >= MIN_LIQUIDITY

Reading

tokensCount() → uint256How many coins have launched.
recent(offset, limit) → address[]Newest first, so page zero is the front page.
launchOf(address) → LaunchCreator, beneficiary, timestamp, fee terms, seeded ETH, supply, pool id.
tokensOf(address) → address[]Everything one creator has launched.
allTokens(uint256) → addressRaw registry access, oldest first.

Admin

setLaunchFee(uint256)Changes the flat fee for future launches. Cannot touch a launch that already happened.
setTreasury(address)Where future launch fees go.
setAdmin(address)Hands the role on.

What is not there

No function that decreases liquidity. No function that transfers a position. No function that changes a pool's fee, split or beneficiary. No pause. No upgrade path or proxy. Search the file yourself.

Deploying your own

The repository is a Foundry project. ./install.sh pins every dependency to the commit this was built against, then forge test runs the suite. script/Preflight.s.sol checks the target chain read-only before you spend anything, and script/Deploy.s.sol mines the hook salt, ships a CREATE2 deployer if the chain lacks the canonical one, deploys both contracts and wires them.

terminal
cd contracts
./install.sh
forge test

cp .env.example .env          # POOL_MANAGER, ADMIN, TREASURY
forge script script/Preflight.s.sol --rpc-url robinhood
forge script script/Deploy.s.sol   --rpc-url robinhood --broadcast --verify

Full source on GitHub.