moto.fun Addresses, Config and Events
Addresses and discovery, chain config, fee constants, and every moto.fun event signature with its indexed fields.
1. Deployed Contract Addresses
Mainnet (chain 1)
moto.fun is deployed with the DEX. Read every address from /config; a key that reads the zero
address is unset for the current phase. This table names the keys, and /config holds the values.
| Contract | /config key | Address |
|---|---|---|
LaunchpadCurve | addresses.launchpadCurve | Deployed. Read the value from /config |
LaunchpadLens | addresses.launchpadLens | Deployed. Read the value from /config |
CreatorFeeRegistry | addresses.creatorFeeRegistry | Deployed. Read the value from /config |
CreatorFeeVault | addresses.creatorFeeVault | Deployed. Read the value from /config |
PveVault | addresses.pveVault | Deployed. Read the value from /config |
MotoToken (MOTO) | addresses.motoToken | Deployed. Read it from /config; the addresses status table prints it |
MotoSwapFactory | addresses.factory | Deployed. Read the value from /config |
FeeRouter | addresses.feeRouter | Deployed. Read the value from /config |
Collector | addresses.collector | Deployed. Read the value from /config |
| WETH | addresses.weth | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 (canonical) |
| LP burn sink | none | 0x000000000000000000000000000000000000dEaD |
LaunchpadToken's implementation address has no key on the Motoswap /config. Read it from the
moto.fun backend's /config.addresses.tokenImplementation if you need it for CREATE2 derivation, or
skip derivation and call LaunchpadLens.predictToken.
Discovering live addresses at runtime
Two endpoints, both worth reading once at startup. Full field lists on addresses and discovery.
// 1. The Motoswap API: is moto.fun live, and where is the app?
const cfg = await fetch("https://api.motoswap.org/config").then((r) => r.json());
if (!cfg.motofunEnabled) return; // === cfg.addresses.launchpadCurve !== ZeroAddress
const { launchpadCurve, launchpadLens } = cfg.addresses;
// 2. The app backend: the rest of the deployment, per chain.
const funCfg = await fetch(`${cfg.motofunUrl}/backend-fun/${slug}/config`).then((r) => r.json());
const { curve, lens, tokenImplementation, pveVault, weth, moto } = funCfg.addresses;Rules that fall out of it:
- A zero address is a declared value, never an omitted key. Branch on the value.
- The curve and the lens gate independently. A non-zero curve with a zero lens means the read surface is unavailable, so do not quote.
pveVaultzero means PvE is off on that chain.motofunEnabled: falseon the app backend means the service is up but no curve is set for that chain. Its indexer and keeper idle, andPOST /intentsanswers503.- If a network's upstream is not configured, the config path answers with the app's HTML rather than JSON. That is the signal that moto.fun is not live there, not an outage.
Deriving a coin address offline
deployer = the curve
initCode = EIP-1167 minimal proxy over tokenImplementation
create2Salt = keccak256(abi.encode(creator, salt))The CREATE2 salt is not the salt field from the intent. It is bound to the creator, so a salt
replayed from another wallet lands at a different address and a signed intent cannot be hijacked into
someone else's advertised address.
Local development
Local addresses are not listed and pinning them would be wrong: a local stack mints fresh addresses
every run. Start the stack and read its /config.
2. Chain / Network Config
moto.fun launches on Ethereum with the DEX. Other networks are planned for later. When they arrive,
each will get its own curve deployment, coin set and app backend behind a per-network path segment,
so read the network from /config rather than hardcoding it.
| Concept | Where it comes from |
|---|---|
| Whether moto.fun is live | motofunEnabled on /config (Ethereum only at launch) |
| The app origin | motofunUrl on the Motoswap /config |
| The per-network path segment | The chain slug, the same one that appears in share links as ?chain= |
| The chain id | chainId on the app backend's /config |
| The EIP-712 domain | eip712 on the app backend's /config, plus the chain id and the served curve |
Public endpoints
| Surface | Base |
|---|---|
| Motoswap API | https://api.motoswap.org |
| OpenAPI spec | https://api.motoswap.org/openapi.json |
| Interactive reference | https://api.motoswap.org/docs |
| The app backend | <motofunUrl>/backend-fun/<chain-slug> |
WebSockets
None. Realtime on the app backend is Server-Sent Events at /stream; the Motoswap API has no
realtime surface for moto.fun. See realtime.
3. Domain / Wire Types
The Motoswap API uses the standard Motoswap wire types; see the cheat sheet.
The app backend is looser and needs its own rules:
| Domain | Wire | Notes |
|---|---|---|
| wei, uint256 | decimal string | Exact. Never a number |
priceX18 | decimal string | 1e18 fixed point. ETH per whole coin |
Scaled columns (*_gwei, *_milli) | number | Lossy, for sorting and display only |
Candle low / high | number | Floats. The one place prices are numbers |
Candle open / close | string, optional | Treat as possibly absent |
| Addresses | lowercase string | Not always regex-validated on input |
| Timestamps | number | Unix seconds, except curveReadAt which is milliseconds |
| Status | number | 0 None, 1 Trading, 2 Frozen, 3 Graduated |
null policy
ethUsd is nullable on /stats and /pve/value and non-nullable on /tokens,
/tokens/{address} and /creators/top. This is intentional: the first pair uses a real-price-only
read so a null means "no real price", never a fabricated one; the second falls back to a configured
rate. Do not assume one rule across the service.
metadata is null rather than wrong when its hash does not match the on-chain commitment. curve
is null when the chain read failed past its stale window, with curveLive: false marking both that
and a stale-but-served state. pairWeth and pairMoto are null until graduation.
4. Fee Schedule and Curve Constants
Curve fee
| Component | Value at ship | Cap | Where |
|---|---|---|---|
| Protocol fee | 70 bps (0.70%) | MAX_PROTOCOL_FEE_BPS = 100 | LaunchpadCurve.protocolFeeBps |
| Creator fee | 50 bps (0.50%) | MAX_CREATOR_FEE_BPS = 50 | LaunchpadCurve.creatorFeeBps |
| Total per trade | 120 bps (1.20%) | 150 bps | Both sides, buys and sells |
Both are owner-tunable state, not constants. The creator fee ships at its cap, so it can only be lowered. Read them at trade time. This curve rate differs from the DEX, where a swap pays 1% (1.3% on a graduated moto.fun coin).
The fee comes off the ETH leg: off the input on a buy, off the output on a sell. Only the net, post-fee ETH enters the curve's reserve.
Split: the protocol share transfers to the Collector in WETH, at the same rate and to the same
address as the DEX FeeRouter. The creator share transfers to CreatorFeeVault in WETH and is
credited per coin. If the registry cannot be read, or the curve is no longer an authorised depositor,
the entire fee goes to the Collector and CreatorFeeRoutedToCollector fires. The trader-facing
rate never changes.
Points and Rakeback parity
Because the curve's protocol fee is the same rate, in the same asset, to the same Collector, and the
Trade event carries the literal transfer amount, a curve trade credits exactly what a DEX swap
of the same size credits. Same Points, same Rakeback, same balances, same weekly epochs, same
0.20% Rakeback share. There is no separate curve pot.
The curve's creator fee is a different leg entirely: it reaches the CreatorFeeVault, never the
Rakeback pool.
Curve constants
| Constant | Value | Meaning |
|---|---|---|
SUPPLY | 1e27 (1,000,000,000) | Fixed supply per coin |
FLOOR | 2e26 (200,000,000) | Parameter set 0. The graduation floor for a coin whose paramsId is 0 |
VIRTUAL_TOKENS | 66_666_667e18 | Parameter set 0. Virtual token reserve |
VIRTUAL_ETH | 4 ether / 3 | Parameter set 0. Virtual ETH reserve |
SELL_REOPEN_DELAY | 15 minutes | Frozen coin sell reopen |
MIN_TWAP_WINDOW / MAX_TWAP_WINDOW | not published | MOTO reference window. Read them from the contract |
MAX_BLOCKED_QUOTES | not published | Pre-bond blocklist quote assets. Read it from the contract |
The three set 0 rows are in force for every coin stamped 0, which is what a fresh deployment
stamps until the owner appends a set. They are not a property of the contract. Each coin is priced by the
parameter set it was stamped with at launch: read it with paramsOf(token), which returns
(virtualEth, floorSupply, virtualTokens). See
each coin has its own curve parameters.
Derived: about 4 ETH of net buying sits in a curve at graduation, at a market cap of about 20 ETH.
Graduation constants
| Constant | Value at ship | Cap |
|---|---|---|
graduationBounty | 0.015 ether | MAX_BOUNTY = 0.08 ether, and clamped to 2% of the pot at graduation |
maxTwapDeviationBps | not published | The MOTO buyback tolerance band. Read it from the contract. There is no "off" setting |
The pot after the bounty splits 50/50 between the TOKEN/WETH pool leg and the MOTO buyback. Both
pools seed at the final curve price. Both LP positions mint to 0x…dEaD.
5. Events - Integrator Reference
LaunchpadCurve
The five an indexer needs:
event TokenCreated(
address indexed token,
address indexed creator,
string name,
string symbol,
bytes32 metadataHash,
address indexed quoteToken
);topic0 is 0x6223a619ba904a9914dac4461d25f8f27e372c0a030791c3a5c012768765ef45, and the event has
four topics: the hash, token, creator, quoteToken. quoteToken is the curve's WETH address; see
which asset a coin is quoted in.
The discovery event. Fires at materialization on every launch, whichever path. creator is the
signer, which is also creatorOf[token] and the PvE beneficiary. It is not necessarily the fee
recipient.
event Trade(
address indexed trader,
address indexed token,
bool isBuy,
uint256 ethAmount,
uint256 tokenAmount,
uint256 priceX18,
uint256 protocolFee,
uint256 creatorFee
);Every buy and every sell. Field notes:
ethAmountis the curve-leg gross: fee-inclusive on buys, pre-fee on sells. Do not sum it as one consistent notion of volume without deciding which you meanpriceX18is the spot price after the trade, 1e18 fixed point, the same numberLaunchpadLens.currentPricereturnsprotocolFeeis the literal WETH transfer to the Collector;creatorFeethe literal deposit into the vault. Both are amounts, not rates, so a fee-schedule change needs no special handlingisBuyis not indexed. Buys cannot be topic-filtered from sells; decode and branch
event FloorReached(address indexed token, uint256 realEth);The coin froze. tokenReserve is exactly the coin's own floorSupply from this point, and all trading is paused until
graduation or the sell-reopen delay expires.
event Graduated(
address indexed token,
address pairWeth,
address pairMoto,
uint256 wethLp,
uint256 motoLp,
uint256 tokensLpWeth,
uint256 tokensLpMoto,
uint256 dustBurned,
address indexed keeper
);The pair addresses are not indexed. Pair discovery requires decoding the data, not a topic
filter. keeper is whoever sent the transaction and collected the bounty. dustBurned is the
coin-side remainder sent to 0x…dEaD.
event PveFill(address indexed token, address indexed contributor, uint256 tokens);Every escrow fill, from a launch slice or a public buyPve. contributor is who paid.
Everything else on the curve:
event FeeRecipientSet(address indexed token, address indexed recipient);
event CreatorFeeRoutedToCollector(address indexed token, uint256 amount);
event CreatorFeeUnregistered(address indexed token, address indexed creator);
event LaunchParamsSet(uint16 indexed id, uint128 virtualEth, uint128 floorSupply, uint256 virtualTokens, uint256 impliedRaise);
event RaiseBandSet(uint128 minRaise, uint128 maxRaise);
event UpgradeAuthoritySet(address indexed previous, address indexed next);
event PveRecorded(address indexed token, uint256 tokens);
event PveRecordFailed(address indexed token, uint256 tokens);
event BountyOwed(address indexed keeper, uint256 amount);
event BountyClaimed(address indexed keeper, uint256 amount);
event PriceCheckpointed(uint256 cumulative, uint32 timestamp);
event FeesSet(uint256 protocolFeeBps, uint256 creatorFeeBps);
event GraduationBountySet(uint256 bounty);
event CollectorSet(address collector);
event MaxTwapDeviationBpsSet(uint256 bps);
event BlockedQuoteAssetsSet(address[] quotes);
event LaunchesPausedSet(bool paused);
event AllBuysPausedSet(bool paused);
event TokenBuysPausedSet(address indexed token, bool paused);
event PveVaultSet(address vault);
event PveKeeperSet(address keeper);Semantics worth catching:
- The curve is a proxy, so it also emits the standard proxy and ownership events from its own address:
Upgraded(address indexed implementation),Initialized(uint64 version),OwnershipTransferStartedandOwnershipTransferred, andUpgradesRenounced(). The deploy block carriesUpgraded,OwnershipTransferred,UpgradeAuthoritySetandInitialized. Do not let an unknown-topic handler treat them as errors LaunchParamsSetmeans coins launched from that block on are priced by a new parameter set. Coins already launched are unaffectedFeeRecipientSetfires only when the recipient differs from the creator. Its absence means the creator is the recipient. Do not treat a missing event as "no fee recipient"CreatorFeeRoutedToCollectoris the creator leg failing over. A merely disabled creator fee is silent, so this event means a read failure or a revoked depositor, not a policy changeCreatorFeeUnregisteredis declared and no longer emitted. A registry refusal with the coin's slot still empty used to launch the coin without a creator fee and fire this event. It now revertsRegistryRefused, so no coin launches unregistered. Keep the event in your ABI only to decode logs from before that change.CreatorFeeRoutedToCollectoris a different thing and still live: it fires per trade, on a coin that does have a creator, when the creator leg could not be paid- A launch does not fail open in general. A slot held by another address reverts
CreatorAlreadyClaimed, and an unreadable registry revertsRegistryUnreadable. The empty-slot refusal above is the only outcome that lets a coin launch unregistered PveRecordFailedmeans the escrow is still held and awaiting arecordPveLateretryLaunchesPausedSet(false)andAllBuysPausedSet(false)also fire fromrenounceOwnership, which clears both before renouncing so a pause cannot latch forever
LaunchpadToken
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);Standard ERC-20. The launch mint appears as Transfer(0x0, curve, 1e27). There is no bonded event:
the bonded flag is set inside the graduation transaction and read as state.
LaunchpadLens
No events. It is a pure read surface.
CreatorFeeRegistry
event TokenRegistered(address indexed token, address indexed creator, address indexed registrar);
event CreatorSet(address indexed token, address indexed creator);
event CreatorSetBySelf(address indexed token, address indexed previousCreator, address indexed creator);
event TokenDisabled(address indexed token, bool disabled);
event RegistrarSet(address indexed registrar, bool allowed);registrar distinguishes which surface registered a coin. CreatorSet fires on an owner takeover
and on a creator's own redirect (CreatorSetBySelf fires only on the second). Either one
re-attributes the coin's full accrued history, so a creator dashboard folded on the latest
CreatorSet never splits earnings by ownership period.
CreatorFeeVault
event Accrued(address indexed token, address indexed quoteAsset, uint256 amount);
event Claimed(address indexed token, address indexed creator, address indexed quoteAsset, uint256 amount);
event DepositorSet(address indexed depositor, bool allowed);Accrued is the only accrual writer, and notifyDeposit is the vault's single deposit chokepoint,
so this event covers every depositor: the DEX FeeRouter, the moto.fun curve, and anything wired
next. Claimed.creator is who was actually paid.
Do not conflate the two creator-fee representations. Accrued and Claimed are the vault ledger and
cover both launch surfaces. Trade.creatorFee is the curve's own leg and is what venue-level curve
revenue is measured from.
PveVault
External to this repo. Its events are documented with the DEX contracts.
6. Indexing Rules
- Discovery is one address. Watch
TokenCreatedon the curve. There is no factory to enumerate and noPairCreatedsweep until a coin graduates - Key on
(blockNumber, txHash, logIndex)and keep raw events append-only. Every official insert is idempotent on(txHash, logIndex), so a re-scan is a no-op and over-rewinding a reorg is free - Do not store a derived status column. Fold it from the latest surviving lifecycle event, so a rollback is a range delete with nothing to rebuild. This is what both official indexers do
- Confirmation depth on the app backend defaults to 3 blocks, with hash-based reorg detection and a 200-block wedge limit
- Freeze is reversible. A
Frozencoin that nobody graduates reopens for sells after 15 minutes, and a sell returns it toTrading. Model the lifecycle as a state machine with that back edge, not as a one-way ratchet - Graduation market cap should be snapshotted once and never re-priced. A graduation lost to a reorg must clear it
- The curve's
Tradeevent is the trade-level truth. After graduation, trades are ordinary pair andFeeRouter.Swapevents; see the DEX event catalog