Reference

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 keyAddress
LaunchpadCurveaddresses.launchpadCurveDeployed. Read the value from /config
LaunchpadLensaddresses.launchpadLensDeployed. Read the value from /config
CreatorFeeRegistryaddresses.creatorFeeRegistryDeployed. Read the value from /config
CreatorFeeVaultaddresses.creatorFeeVaultDeployed. Read the value from /config
PveVaultaddresses.pveVaultDeployed. Read the value from /config
MotoToken (MOTO)addresses.motoTokenDeployed. Read it from /config; the addresses status table prints it
MotoSwapFactoryaddresses.factoryDeployed. Read the value from /config
FeeRouteraddresses.feeRouterDeployed. Read the value from /config
Collectoraddresses.collectorDeployed. Read the value from /config
WETHaddresses.weth0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 (canonical)
LP burn sinknone0x000000000000000000000000000000000000dEaD

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.
  • pveVault zero means PvE is off on that chain.
  • motofunEnabled: false on the app backend means the service is up but no curve is set for that chain. Its indexer and keeper idle, and POST /intents answers 503.
  • 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.

ConceptWhere it comes from
Whether moto.fun is livemotofunEnabled on /config (Ethereum only at launch)
The app originmotofunUrl on the Motoswap /config
The per-network path segmentThe chain slug, the same one that appears in share links as ?chain=
The chain idchainId on the app backend's /config
The EIP-712 domaineip712 on the app backend's /config, plus the chain id and the served curve

Public endpoints

SurfaceBase
Motoswap APIhttps://api.motoswap.org
OpenAPI spechttps://api.motoswap.org/openapi.json
Interactive referencehttps://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:

DomainWireNotes
wei, uint256decimal stringExact. Never a number
priceX18decimal string1e18 fixed point. ETH per whole coin
Scaled columns (*_gwei, *_milli)numberLossy, for sorting and display only
Candle low / highnumberFloats. The one place prices are numbers
Candle open / closestring, optionalTreat as possibly absent
Addresseslowercase stringNot always regex-validated on input
TimestampsnumberUnix seconds, except curveReadAt which is milliseconds
Statusnumber0 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

ComponentValue at shipCapWhere
Protocol fee70 bps (0.70%)MAX_PROTOCOL_FEE_BPS = 100LaunchpadCurve.protocolFeeBps
Creator fee50 bps (0.50%)MAX_CREATOR_FEE_BPS = 50LaunchpadCurve.creatorFeeBps
Total per trade120 bps (1.20%)150 bpsBoth 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

ConstantValueMeaning
SUPPLY1e27 (1,000,000,000)Fixed supply per coin
FLOOR2e26 (200,000,000)Parameter set 0. The graduation floor for a coin whose paramsId is 0
VIRTUAL_TOKENS66_666_667e18Parameter set 0. Virtual token reserve
VIRTUAL_ETH4 ether / 3Parameter set 0. Virtual ETH reserve
SELL_REOPEN_DELAY15 minutesFrozen coin sell reopen
MIN_TWAP_WINDOW / MAX_TWAP_WINDOWnot publishedMOTO reference window. Read them from the contract
MAX_BLOCKED_QUOTESnot publishedPre-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

ConstantValue at shipCap
graduationBounty0.015 etherMAX_BOUNTY = 0.08 ether, and clamped to 2% of the pot at graduation
maxTwapDeviationBpsnot publishedThe 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:

  • ethAmount is 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 mean
  • priceX18 is the spot price after the trade, 1e18 fixed point, the same number LaunchpadLens.currentPrice returns
  • protocolFee is the literal WETH transfer to the Collector; creatorFee the literal deposit into the vault. Both are amounts, not rates, so a fee-schedule change needs no special handling
  • isBuy is 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), OwnershipTransferStarted and OwnershipTransferred, and UpgradesRenounced(). The deploy block carries Upgraded, OwnershipTransferred, UpgradeAuthoritySet and Initialized. Do not let an unknown-topic handler treat them as errors
  • LaunchParamsSet means coins launched from that block on are priced by a new parameter set. Coins already launched are unaffected
  • FeeRecipientSet fires 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"
  • CreatorFeeRoutedToCollector is 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 change
  • CreatorFeeUnregistered is 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 reverts RegistryRefused, so no coin launches unregistered. Keep the event in your ABI only to decode logs from before that change. CreatorFeeRoutedToCollector is 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 reverts RegistryUnreadable. The empty-slot refusal above is the only outcome that lets a coin launch unregistered
  • PveRecordFailed means the escrow is still held and awaiting a recordPveLate retry
  • LaunchesPausedSet(false) and AllBuysPausedSet(false) also fire from renounceOwnership, 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 TokenCreated on the curve. There is no factory to enumerate and no PairCreated sweep 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 Frozen coin that nobody graduates reopens for sells after 15 minutes, and a sell returns it to Trading. 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 Trade event is the trade-level truth. After graduation, trades are ordinary pair and FeeRouter.Swap events; see the DEX event catalog

On this page