Addresses and discovery
Resolve the curve, the lens and the coin set at runtime from /config. Nothing here is hardcoded, including by us.
Read addresses at runtime from
GET https://api.motoswap.org/config. That endpoint is the canonical source for both the DEX and moto.fun. This page documents the shape you get back; it does not publish address values, and hardcoding any address you find elsewhere will break your integration silently.
Mainnet (chain 1)
moto.fun is deployed on Ethereum with the DEX. Read its addresses from /config. If a deploy has
moto.fun off, its entries read as the zero address, motofunEnabled is false, motofunUrl is
empty, and every /motofun/* route answers 404. Deployed is not the same as open: launchPhase
in /config reports the phase, and the app backend's publicOpen reports whether moto.fun is open to users. This
table names the keys; /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 the value from /config |
| WETH | addresses.weth | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 (canonical) |
addresses.launchpadCurve is a proxy, and it is the only curve address you ever call. The
implementation behind it can be replaced through a timelock; see
Who can change the curve. Do not pin or call an
implementation address.
LaunchpadToken has no /config key. It is not a contract you call: read the implementation address
from the moto.fun backend's own /config if you need it for CREATE2 derivation, or use
LaunchpadLens.predictToken and skip the derivation entirely.
The two config endpoints
They serve different things and both are worth reading once at startup.
GET https://api.motoswap.org/config
The Motoswap API's config, cached 300s. The moto.fun keys in it:
{
"addresses": {
"launchpadCurve": "0x…", // zero when moto.fun display is off
"launchpadLens": "0x…" // gated independently of the curve
},
"motofunEnabled": true, // boolean
"motofunUrl": "https://…", // the moto.fun app origin, "" to fall back
"launchPhase": "dex" // "prelaunch" | "vamp" (before the DEX) | "dex"
}motofunEnabled is exactly launchpadCurve != 0x0, so branching on the address alone is correct.
The curve and the lens gate independently: a non-zero curve with a zero lens means the read surface
is unavailable, and you should not quote.
GET <motofunUrl>/backend-fun/<chain-slug>/config
The moto.fun service's own config, static per deploy, no DB read and no RPC:
{
"chainId": 1,
"addresses": {
"curve": "0x…",
"lens": "0x…",
"tokenImplementation": "0x…",
"pveVault": "0x…", // zero when PvE is not live on this chain
"weth": "0x…",
"moto": "0x…",
"creatorFeeRegistry": "0x…", // resolved off the curve. null until that first read lands
"creatorFeeVault": "0x…" // same
},
"eip712": { "name": "MotoswapLaunchpad", "version": "1" },
"motofunEnabled": true, // false = the service is up but no curve on this chain
"publicOpen": true, // false = the app shows its launch-pending screen
"launchOpensAt": null // ISO 8601 UTC string, or null when no opening time was set
}publicOpen gates a screen in the app and nothing else. Every route on the service answers
normally while it is false, so do not read it as "the API is down" or "the curve is paused".
motofunEnabled answers a different question: whether a curve exists on this chain at all.
A zero address is a declared value, not a missing key
pveVault is served as the zero address when PvE is not live, never omitted. Branch on the value
being 0x0, not on the key being absent. The same rule holds for every address on both endpoints,
with one exception: creatorFeeRegistry and creatorFeeVault on the app backend are read off the
curve after boot, and serve null until that read has landed. null means "not known yet". It
does not mean the zero address, so retry rather than treating the fee contracts as absent.
The chain slug is per network and the same slug appears in share links as ?chain=. Resolve
motofunUrl from the Motoswap /config rather than assuming a hostname.
Verify before you trade
The app's own rule, worth copying: before any wallet prompt it checks that the served curve address matches the address baked into its build. A served config that disagrees with what you pinned means a redeploy happened, not that you should follow the new one blindly.
If you cache addresses, cache them for minutes and re-read on any unexpected revert. If you pin them, pin them deliberately and fail closed when the served value differs.
Deriving a coin's address
A coin's address is known before the coin exists, which is how a pending launch can be listed and linked. Two ways to get it:
// Preferred: ask the lens.
const predicted = await client.readContract({
address: cfg.addresses.launchpadLens,
abi: parseAbi(["function predictToken(address creator, uint256 salt) view returns (address)"]),
functionName: "predictToken",
args: [creator, salt],
});Deriving it offline is possible. Get these three things right:
- The deployer is the curve, not the creator.
- The init code is an EIP-1167 minimal proxy over
tokenImplementation. - The CREATE2 salt is
keccak256(abi.encode(creator, salt)), not thesaltfrom the intent.
That last one is deliberate: a salt replayed from another wallet lands at a different address, so a signed intent cannot be hijacked into the address someone else advertised.
Where the curve's history starts
To index the curve from chain you need the block its proxy was deployed in. No endpoint serves that
block today. Take it from the explorer's contract-creation record for addresses.launchpadCurve.
It is also the first block at which that address has code, and the block that holds the proxy's
Upgraded and Initialized events. Start your TokenCreated backfill there:
listing moto.fun coins has the details.
Networks
moto.fun opens on Ethereum only. Other networks will come later, each with its own curve deployment
and its own coin set. When they do, ask /config per network rather than assuming which are live; a network whose upstream is not configured answers the config path with the
app's HTML rather than JSON, which is the signal that moto.fun is not live there.
Do not enumerate networks from a hardcoded list. The set changes.
Local development
Local addresses are not listed here, and pinning them would be wrong anyway: a local stack mints
fresh addresses every run. Start the stack and read its /config, which is exactly how the app and
the indexer discover them.