Addresses, config and events
Combined reference - deployed addresses, chain config, wire types, fee schedule, and event signatures.
1. Deployed Contract Addresses
Ethereum (chain 1)
Public API base URL: https://api.motoswap.org.
Deployment status is per contract, never per network. MOTO, the launch farm (VampChef),
UniswapZap, MotoStaking, RewardVestingEscrow, TreasuryVesting, the Motocat collection and
MotocatStaking went out first. The DEX contracts (factory, router, FeeRouter, Collector,
QuoteAssetRegistry, RakebackV2, MasterChef, zap, the creator fee pair, PveVault,
BuybackBurner, the moto.fun contracts) went out with the DEX. Contracts in the source tree that
are not deployed are called out where they appear below.
Fetch addresses from GET /config rather than hard-coding them. The status table, with every
address the site pins and every key that reads 0x0, lives on
Contract Addresses, so one page owns it and it cannot drift. This page documents
the /config keys themselves:
factory, router, permit2, motoStaking, motoToken, masterChef,
tokenLauncher, feeRouter, collector, quoteRegistry, rakeback, zap,
vaultCompounder, usdc, usdt, weth, creatorFeeRegistry,
creatorFeeVault, motocatNft, motocatStaking, pveVault, pveVaultV2,
buybackBurner, buybackDistributor, rewardVestingEscrow,
treasuryVesting, launchpadCurve, launchpadLens, motoUniswapPair,
vampChef, uniswapZap, uniswapV2Router, uniswapV2Factory, plus the vestigial
vaults[0..3] array (MotoStaking holds every lock option in one contract).
Not exposed by /config. Snapshotter is the only contract in the source tree with no key on
the endpoint. There is no getter for it; if it ships, its address will be published separately.
An address that reads back as 0x0 from /config means the contract is unset for that chain in
the current launchPhase, or its feature is off. It does not mean the key is missing. Never send
a transaction to it. tokenLauncher belongs to a retired contract and stays 0x0.
rewardVestingEscrow also has an on-chain getter, vestingEscrow() on the chef, which is worth
preferring since it survives a redeploy that /config has not caught up with.
Read /config.addresses.masterChef rather than assuming what is behind it. While launchPhase
was vamp it held the same address as vampChef, and the contract behind that key is a VampChef
(one-step withdraw, its own event set, listed below).
The seeded MOTO/WETH pool for the current phase is served as motoUniswapPair
(during launch farming it was a Uniswap V2 pair). Derive any other pair from
factory.getPair(tokenA, tokenB) rather than hard-coding it.
/config also carries two moto.fun keys, launchpadCurve and launchpadLens. Both read back as
0x0 unless that surface is enabled for the chain, so gating on the address alone is correct. The
top-level motofunEnabled / motofunUrl fields flag the same surface.
Contracts published under Motoswap-adjacent repos are sibling experiments, not the Motoswap DEX.
If an address is not in /config and not discoverable from a /config contract's getters, do not
integrate against it.
Local development
Local addresses are not listed here. The local stack described below is Motoswap's own
development environment and is not public. As an outside integrator, fork Ethereum mainnet
with your own node tooling and use the chain 1 addresses from
GET https://api.motoswap.org/config.
Discovering addresses at runtime
The recommended integrator path: call GET /config on the backend
(https://api.motoswap.org/config). It returns the current chain's contract addresses, so you
never hard-code them. The answer below is a complete chain 1 capture taken on 2026-09-28
after the DEX opened, with launchPhase at dex. Any 0x0000000000000000000000000000000000000000
is a contract that is not deployed. Call the endpoint for the values you use:
{
"chainId": 1,
"addresses": {
"factory": "0x81c9cbc47d700da1777abd831d8da3f526dfae24",
"router": "0x6f0b6a0d84c3cff6f273d5294cda147885225d5e",
"permit2": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"motoStaking": "0xce88f2c6b49efbb92555ee5475311f0e250c2528",
"motoToken": "0xbd965230588eaa536de6aa45e8ebbc01638535e0",
"masterChef": "0x939f348b6658ce4db7cde088341b00de341fe085",
"tokenLauncher": "0x0000000000000000000000000000000000000000",
"feeRouter": "0x9f846ef584fd44d075b5e8df00ddb4416da61a80",
"collector": "0xc13307272bbf73f2191ce57d0fb714c2a9200cf3",
"quoteRegistry": "0xfb8ca710b9b242e9f5027272592ef4c19d37e200",
"rakeback": "0x89e2e1819fc373e3cd840a0ceb2ef1eae79e2db8",
"zap": "0x26bffd661c3d97b3285afa2a9f05232526a89129",
"vaultCompounder": "0x0000000000000000000000000000000000000000",
"usdc": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"usdt": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"weth": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"creatorFeeRegistry": "0x6cdec7f49036ccf1bba940011040fa044cfe6d08",
"creatorFeeVault": "0x8cc7c8e76d9698e5b1026c720a94c1807e613e1f",
"motocatNft": "0x0b024b3d4ad38bff1acd303cd308d2c49c936e0a",
"motocatStaking": "0x11890ee9ee6099ac33fd4dfb702f8e8f6d651f17",
"pveVault": "0x84303a885717c4dd9effbdd2179bbe4b01756740",
"pveVaultV2": "0x0000000000000000000000000000000000000000",
"buybackBurner": "0x85b5a2800d7e2d948f21cb8f6e610d2c9b19ae3d",
"buybackDistributor": "0x0000000000000000000000000000000000000000",
"rewardVestingEscrow": "0xb94bfadc535d96462854175d776afc168196976b",
"treasuryVesting": "0x5730abdf3c06c37940fb8b5291277e62d2c15278",
"launchpadCurve": "0xb65b67e986d7b097652920d72202f2c78319bc2c",
"launchpadLens": "0xb631697124c8e6bb6c221cc017e47d4168bd3411",
"motoUniswapPair": "0xad1a21f61d653c6101b92c335f61140459c81c79",
"vampChef": "0x51e648f08a9a08a724591938d9cbc483c809aca4",
"sustainChef": "0x939f348b6658ce4db7cde088341b00de341fe085",
"uniswapZap": "0x532bfb5b8c9e3cef20d36910b90cfbd919b18c31",
"uniswapV2Router": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"uniswapV2Factory": "0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f",
"vaults": [
"0x0000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000"
]
},
"splitSwapEnabled": true,
"motofunEnabled": true,
"bridgeMotoEnabled": false,
"motofunUrl": "https://moto.fun",
"pveComboEnabled": false,
"launchPhase": "dex",
"dexCutover": false,
"misconfigured": []
}There is no rpcUrl field: bring your own RPC endpoint for chain 1.
| Top-level field | Type | Meaning |
|---|---|---|
chainId | number | The chain these addresses live on. |
addresses | object | The keys listed above. Lowercase 0x strings; vaults is an array of four. |
launchPhase | "prelaunch", "vamp" or "dex" | vamp: the farm is deployed and the DEX is not, which is how it read before the DEX opened. dex: the DEX is deployed. |
splitSwapEnabled | boolean | API configuration flag read by the Motoswap app: whether it submits a split fill as one swapExact*Split transaction (true) or as one swap per leg (false). Not an on-chain switch: the FeeRouter split entrypoints have no enable flag. |
motofunEnabled | boolean | Whether the moto.fun surface is on. When false, launchpadCurve and launchpadLens read 0x0. |
motofunUrl | string | The moto.fun site URL, or "". |
bridgeMotoEnabled | boolean | Whether MOTO bridging is switched on in the app. |
pveComboEnabled | boolean | Dead flag. Nothing reads it. |
misconfigured | string[] | Names of transaction-target addresses that are unset for the current phase. Empty in normal operation. |
2. Chain / Network Config
Supported chain IDs
Ethereum mainnet is chain 1, and it is the only chain an integrator targets. The client also
accepts a local development chain id; NEXT_PUBLIC_CHAIN_ID is validated against a fixed
allowlist, so no other value is accepted.
Read the chain you are pointed at from /config.chainId rather than assuming one.
Public endpoints
- REST API base URL:
https://api.motoswap.org - RPC URL: bring your own for chain
1./configdoes not return one. Do NOT ship an API-keyed URL in a browser bundle. - CORS: Simple GET requests work from a browser on any origin. Requests that need a CORS preflight, such as one where your script sets
If-None-Match, only pass for the Motoswap app origin, so do conditional polling and any write from a server. - Endpoint reference: API.
- Chain head heartbeat:
GET /chain/head(block number + timestamp, 1s max-age).
WebSockets
No WebSocket subscriptions are exposed to integrators. Use the REST API (cache-friendly, ETag-versioned; 304s are near-free) or watch on-chain events directly via your own RPC / event-log provider.
Fill-in reference (from source)
The environment variables that decide which chain and backend a client talks to:
NEXT_PUBLIC_CHAIN_ID # the target chain id
NEXT_PUBLIC_RPC_URL # browser/wallet-safe (keyless)
RPC_URL # server-side (may be keyed; not shipped)
NEXT_PUBLIC_API_BASE_URL # absolute backend URL for the chain
NEXT_PUBLIC_SENTRY_DSN # optional, disabled locally3. Domain / Wire Types
All branded types live in packages/domain/src/*.ts and are re-exported from
@motoswap/shared/domain. The rule: one runtime type end-to-end (DB, backend,
frontend, chain), a separate WIRE type only when the runtime type is not
JSON-serialisable (i.e. bigint).
| Domain type | Runtime | Wire (JSON) | Zod schema | Example |
|---|---|---|---|---|
Address | `0x${string}` (always lowercase) | string | addressSchema | "0xa0b86991…eb48" |
TxHash | `0x${string}` (32-byte) | string | txHashSchema | "0x00…01" |
Wei | bigint | string (decimal) via weiWire | weiSchema | "1000000000000000000" |
Uint256 | bigint | string (decimal) | uint256Schema | "1000000000000000000" |
Usd | bigint (µ-USD, 6dp integer) | string (decimal dollars) via usdWire | usdSchema | "2500.50" (= 2 500 500 000 µ$) |
Seconds | number (Unix seconds) | number | secondsSchema | 1723456789 |
Percent | number (float, e.g. 10.5 = +10.5%) | number | percentSchema | 10.5 |
Apr | number (non-negative integer basis points, 1250 = 12.50% APR) | number | aprSchema | 1250 |
Ratio | bigint (18-dp fixed point, 1.0 = 10^18) | string (decimal) via ratioWire | ratioSchema | "0.000300" |
There is no Bps brand. Basis-point fields (swapFeeBps, protocolFeeBps,
toleranceBps, priceImpactBps, and so on) are plain integer numbers on the
wire, or bigint where they feed SDK amount math.
Schema shape (example)
// Address - lowercase, 0x-prefixed, hex-only, length 42
const addressSchema = z
.string()
.transform((v) => v.toLowerCase())
.refine((v) => v.startsWith("0x") && v.length === 42 && /^[a-f0-9]+$/.test(v.slice(2)))
.brand("Address")
.meta({ "x-type": "Address" });
// Wei - accepts bigint | string | number at parse time; runtime is bigint
const weiSchema = z
.union([z.bigint(), z.string(), z.number()])
.transform((v) => (typeof v === "bigint" ? v : BigInt(v)))
.refine((n) => n >= 0n && n <= 2n ** 256n - 1n)
.brand("Wei");Wire conversion helpers
usdToWire(usd: Usd): string- bigint µ$ → decimal dollar string.usdFromWire(s: string): Usd- decimal dollar string → bigint µ$.weiToString(wei: Wei): string- bigint → decimal string.weiSchema.parse(v)- accepts bigint / string / number; returns brandedWei.
null policy for numeric fields
usdValue,tvlUsd,volume*Usd,fees*Usd-"0"when zero, nevernull. (Coerced at the handler edge to keep client math trivial.)priceUsd,aprBps,feeAprBps, delta*Pcts,rank,decimals- kept asnullwhen genuinely unknown. Rendering0would mislead (a guessed18for unknown decimals mis-scales a token by10^(18-d)).
4. Fee Schedule & Rakeback Constants
The constants in this section's fee tables are read from the Solidity source the contracts deploy from. Where a value is settable, read it live from the contract rather than trusting a number here.
Protocol-level fee (FeeRouter)
| Constant | Value | Meaning |
|---|---|---|
LAUNCH_PROTOCOL_FEE_BPS | 70 | The launch fee (0.70%). A reference constant: initialize does not apply it. |
MAX_PROTOCOL_FEE_BPS | 100 | Owner-settable ceiling (1.00%). |
protocolFeeBps (live) | Read it from the contract | protocolFeeBps is an initialize argument with no default, capped at MAX_PROTOCOL_FEE_BPS, and owner-settable afterwards. It launched at 70. Read it live from the feeRouter address /config serves rather than trusting a number here. See the fee model. |
BPS_DENOM | 10 000 | Basis-point denominator. |
MAX_SPLIT_LEGS | 4 | Max legs in one swap*Split call. |
| Fee side | Quote asset only | Charged on the quote leg per QuoteAssetRegistry.quoteRank (WETH > USDT > USDC > MOTO). Both-quote routes charge the higher-ranked leg, and a tie goes to the output. A route with no quote asset at either end takes the fee at the first quote asset inside the path. NoQuoteSide() reverts only when no asset anywhere in the path is a quote asset. See which leg pays. |
Pair swap fee (MotoSwapFactory)
| Constant | Value | Meaning |
|---|---|---|
MAX_SWAP_FEE_BPS | 200 | Hard ceiling on swapFeeBps (2.00%). |
swapFeeBps (launched) | 30 | 0.30% LP fee (entirely to LPs; feeTo is zero, so _mintFee no-ops). |
Total swap cost at the planned launch values: 0.30% LP per hop + 0.70%
protocol = 1.00% on a one-hop route with no registered moto.fun token,
when protocolFeeBps == 70. Each path endpoint with an active creator adds
the creator fee.
While protocolFeeBps is 0 the protocol part is zero.
Collector split (Collector.buckets)
Default seed on init: 2 : 2 : 2 : 1 (owner-reweightable).
| Bucket | Recipient | Weight | Notify? |
|---|---|---|---|
| 0 | MotoStaking (staking revshare) | 2 | Yes (IStakingRewards.notifyReward) |
| 1 | Treasury | 2 | No |
| 2 | Rakeback | 2 | No |
| 3 | Buyback and burn | 1 | No |
Rounding dust accrues to the last bucket. Held earmarks (a bucket whose
payout reverted) are excluded from distributable(token) so a failing bucket
never re-weights the healthy ones.
Rakeback
| Constant | Value |
|---|---|
CLAIM_WINDOW | 30 days, measured from a slice's activation |
MIN_ACTIVATION_DELAY / MAX_ACTIVATION_DELAY | Bounds on the delay between postRoot and activate. Values are not published here; read them, and the current activationDelay(), from the contract. |
MAX_TOKENS_PER_EPOCH | 8 |
| Vest schedule (baked into the leaf at post time) | Week 0: 50% of R[week] · Week 1: +25% · Week 2: the remainder |
| Epoch length | Sunday-anchored week - epoch = floor((seconds − 259200) / 604800) |
| Quote assets | WETH, USDT, USDC, MOTO |
Payout is a merkle claim. One root per weekly epoch covers every token, posted by postRoot (poster-gated, capped at the token's measured Collector inflow), then opened per (epoch, token) slice by the permissionless activate. A leaf is keccak256(abi.encodePacked(index, account, token, amount)) and is all-or-nothing: one bitmap bit, full amount, no partial claim and no cumulative counter. Unclaimed remainders leave via expireRoot after the window and become sweepable to the treasury.
Creator fee (optional; per-token via CreatorFeeRegistry)
| Constant | Value |
|---|---|
MAX_CREATOR_FEE_BPS | 50 (0.50% ceiling) |
creatorFeeBps (live) | 0 to 50 (owner-settable); the deploy script writes 20 and the owner sets 30 at launch |
Applied on the same quote leg as the protocol fee. Both fees are subject to
the caller's amountOutMin (net enforced).
VampChef emission (launch farming)
| Constant | Value | Notes |
|---|---|---|
DURATION | 14 days | One flat emission window. rewardPerSecond = rewardBudget / DURATION. |
emissionStart() / emissionEnd() | read from the contract | Unix seconds. Accrual is zero outside the window. One campaign, no restart. |
| Withdraw cooldown | none | withdraw(pid, amount) settles rewards and returns principal in one call. No claimWithdrawal. |
| Deposit fee | none | PoolAdded and PoolSet carry no depositFeeBps. |
MAX_START_DELAY | 90 days | Bounds a scheduled startTime. |
HARVEST_GRACE | 30 days | Window after emissionEnd before recoverUndistributedRewards opens. |
MAX_POOLS | 500 | Hard cap. |
ACC_PRECISION | 1e12 | Reward-per-share scaler. |
| Reward split | 50% liquid / 50% vested (RewardVestingEscrow, 180 days) |
MasterChef emission
Deployed; no emission campaign is running and none is planned. These are the contract's constants, for reference.
| Constant | Value | Notes |
|---|---|---|
HALVING_PERIOD (default) | 42 days | Time-based, not block-based |
NUM_PERIODS (default) | 8 | ~336 days total |
MAX_START_DELAY | 90 days | Bounds scheduled startTime |
HARVEST_GRACE | 30 days | Post-emissionEnd window before recoverUndistributedRewards |
COOLDOWN | 24 hours | Withdraw cooldown while the pool earns. None when no campaign is running or the pool's weight is 0. Withdraw is queue, then claim |
MAX_DEPOSIT_FEE_BPS | 1000 (10%) | Per-pool ceiling |
MAX_POOLS | 500 | Hard cap |
ACC_PRECISION | 1e12 | Reward-per-share scaler |
| Reward split | 50% liquid / 50% vested (RewardVestingEscrow, 180 days) |
MotoStaking locks
| Duration | boost bps | Multiplier |
|---|---|---|
| 3 months | 0 | 1.0x |
| 6 months | 10 000 | 2.0x |
| 12 months | 30 000 | 4.0x |
| 24 months | 70 000 | 8.0x |
effectiveStake = amount × (10 000 + boostBps) / 10 000. Principal vests
linearly over the lock; requestUnstake moves the vested amount into a
30-day linear drip (UNSTAKE_DRIP).
Motocat staking
MotocatStaking V3 distributes no rewards on its own, so it has no reward
constants. It tracks a per-wallet staked cat count as a checkpoint history:
each Checkpoint packs a uint32 block number and a uint224 value into one
slot, so a count can be read at any historical block. A write above either
bound reverts with CheckpointOverflow; two writes in the same block overwrite
rather than append.
Rakeback vest math (concrete example)
- A user earns
Rof a quote token in epoch 42. - Epoch 42: 0.5 × R claimable this week.
- Epoch 43: +0.25 × R cumulative.
- Epoch 44: +0.25 × R cumulative - total 100%.
- After the window: each slice can be claimed for 30 days from its own activation, not from the epoch's end. Past that, anyone can call
expireRoot(epoch, token), the unclaimed remainder leaves the liability record, and the owner cansweepthat surplus to the treasury.sweeptakes no signature and can never touch a live claim.
Each epoch settles independently - there's no FIFO across weeks.
Points ledger (indexer-side)
- Every fee-paying swap is scored by the season engine into
points_events. The scoring internals (rates, curves, tiers, milestone bases, boost formula) are not published; do not reverse-engineer or state them. - Referral rate: 10% of the referee's points to the referrer, one level.
- Motocat boost: the wallet's STAKED Motocat count (staked in
MotocatStaking; a merely-held cat does not count), read at the block before the swap. Formula unpublished. - Balances publish once a day at the 00:00 UTC drop; per-swap deltas are never observable via the API.
5. Events - Integrator Reference
Grouped by contract. Watch them through your own RPC or event-log provider. Signatures below are
the Solidity form, checked against the contract source. Hash the canonical form (types only, no
names, no indexed) with keccak256 to get topic0, or run cast sig-event "<signature>".
Filter on the address /config serves for each contract, never on a hard-coded one. The
deployed implementations can trail the source: where an event below is in the source but not yet
in the deployed code, the text says so.
VampChef (launch farming, ended)
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
event RewardPaid(address indexed user, uint256 indexed pid, uint256 amount);
event RewardShortfall(address indexed user, uint256 indexed pid, uint256 owed, uint256 paid);
event RewardForfeited(address indexed user, uint256 indexed pid, uint256 amount);
event PoolAdded(uint256 indexed pid, address indexed lpToken, uint256 allocPoint, uint256 totalAllocPoint);
event PoolSet(uint256 indexed pid, uint256 allocPoint, uint256 totalAllocPoint);
event PoolUpdated(uint256 indexed pid, uint256 accRewardPerShare, uint256 lastRewardTime);
event EmissionStarted(uint256 budget, uint256 rewardPerSecond, uint64 startTime, uint64 endTime);
event RewardTokenSet(address rewardToken);
event TrustedZapperSet(address trustedZapper);
event UndistributedRewardsRecovered(address to, uint256 amount);RewardShortfall is in the source but not in the deployed implementation, which has no such
topic0 in its bytecode. It arrives with a contract upgrade. Every other event above is in the
deployed code.
Emitter: the vampChef address from /config. It is not MasterChef: there is no
WithdrawQueued or WithdrawClaimed, and PoolAdded / PoolSet / EmissionStarted have
different arguments, so their topic0 values differ from MasterChef's.
| Event | topic0 |
|---|---|
Deposit(address,uint256,uint256) | 0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15 |
Withdraw(address,uint256,uint256) | 0xf279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568 |
RewardPaid(address,uint256,uint256) | 0xd6f2c8500df5b44f11e9e48b91ff9f1b9d81bc496d55570c2b1b75bf65243f51 |
PoolUpdated(uint256,uint256,uint256) | 0x17b8644f386d1c7c7138ef98b3c8035622bbe94d7be9b26f71d2654a547c2943 |
RewardPaid.amount is the full harvest. Half of it moves to the wallet and half is deposited into
RewardVestingEscrow, which emits its own Deposited.
UniswapZap
event Zapped(
address indexed sender,
address indexed pair,
address tokenIn,
uint256 amountIn,
uint256 swapAmount,
uint256 lpAmount
);Emitter: the uniswapZap address from /config. A zap that stakes also produces a
VampChef.Deposit whose user is the zapping wallet, not the zap contract.
MotoToken
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);Standard ERC-20 events. MOTO has 18 decimals, a fixed supply, ERC-2612 permit, no owner and no
mint. The token also emitted a handful of one-time events when it launched. They do not
recur, and tradingOpen() returns true, so transfers are unrestricted.
FeeRouter (canonical trade event)
event Swap(
address indexed user,
address indexed tokenIn,
address indexed tokenOut,
uint256 amountIn,
uint256 amountOut,
address feeToken, // quote asset the fee was charged in
uint256 feeAmount // exact protocol fee (quote-side)
);
event ProtocolFeeSet(uint256 protocolFeeBps);
event CreatorFee(
address indexed token,
address indexed creator,
address quoteAsset,
uint256 amount
);
event Permit2Set(address permit2);
event CreatorFeeConfigSet(address registry, address vault, uint256 creatorFeeBps);
event Rescued(address indexed token, address indexed to, uint256 amount);FeeRouter.Swap is the canonical trade - one row per fee-paying swap (multi-hop = 1 event). The pair Swap fires N times for N hops. user is msg.sender, the payer. It is never the to recipient. The two coincide for an ordinary wallet swap and diverge when a contract calls FeeRouter: an aggregator's settlement contract is user on every swap it routes, and Points, Rakeback and referral attribution all follow user.
MotoSwapPair
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
event Sync(uint112 reserve0, uint112 reserve1);
event Transfer(address indexed from, address indexed to, uint256 value); // LP token
event Approval(address indexed owner, address indexed spender, uint256 value);Sync fires after every state-changing pair call and is the canonical
reserves snapshot for indexers.
MotoSwapFactory
event PairCreated(address indexed token0, address indexed token1, address pair, uint256 allPairsLength);
event SwapFeeSet(uint256 swapFeeBps);
event QuoteRegistrySet(address indexed quoteRegistry);
event SwapAllowedSet(address indexed account, bool allowed);
event FeeToSet(address indexed previousFeeTo, address indexed newFeeTo);
event FeeToSetterTransferStarted(address indexed previousFeeToSetter, address indexed newFeeToSetter);
event FeeToSetterTransferred(address indexed previousFeeToSetter, address indexed newFeeToSetter);MasterChef
event PoolAdded(uint256 indexed pid, address indexed lpToken, uint256 allocPoint, uint16 depositFeeBps, uint256 totalAllocPoint);
event PoolSet(uint256 indexed pid, uint256 allocPoint, uint16 depositFeeBps, uint256 totalAllocPoint);
event PoolUpdated(uint256 indexed pid, uint256 accRewardPerShare, uint256 lastRewardTime);
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event WithdrawQueued(address indexed user, uint256 indexed pid, uint256 amount, uint64 cooldownEnd);
event WithdrawClaimed(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
event RewardPaid(address indexed user, uint256 indexed pid, uint256 amount);
event RewardShortfall(address indexed user, uint256 indexed pid, uint256 owed, uint256 paid);
event RewardForfeited(address indexed account, uint256 indexed pid, uint256 amount);
event EmissionStarted(uint256 totalReward, uint256 initialPeriodReward, uint64 startTime, uint64 endTime);
event EmissionScheduleSet(uint256 halvingPeriod, uint256 numPeriods);
event FeeRecipientUpdated(address feeRecipient);
event RewardTokenSet(address rewardToken);
event VestingEscrowSet(address vestingEscrow);
event TrustedZapperSet(address trustedZapper);
event UndistributedRewardsRecovered(address to, uint256 amount);MotoStaking
event Staked(address indexed account, uint256 amount, uint8 durationOption);
event Imported(address indexed account, uint256 amount, uint8 durationOption);
event Appended(address indexed account, uint256 added, uint256 total, uint8 durationOption, uint64 start);
event RewardNotified(address indexed token, uint256 amount, uint256 accRewardPerEffShare);
event RewardForwardedToTreasury(address indexed token, uint256 amount);
event Claimed(address indexed account, address indexed token, uint256 amount);
event ClaimSkipped(address indexed account, address indexed token);
event ClaimedAsMoto(address indexed account, address indexed token, uint256 rewardIn, uint256 motoOut);
event UnstakeRequested(address indexed account, uint256 amount, uint64 start);
event Unstaked(address indexed account, uint256 amount);
event BoostSet(uint8 durationOption, uint256 bps);
event RewardNotifierSet(address rewardNotifier);
event FeeRouterSet(address feeRouter);
event VestAnchorSet(uint64 anchor);
event RewardTokenAdded(address indexed token);
event RewardTokenRemoved(address indexed token);
event RewardTokenPurged(address indexed token);Every event above is in the deployed implementation. Two of them stay silent until a contract
upgrade lands: the deployed MotoStaking has no reward notifier registered, and its feeRouter()
is a 0x…dEaD placeholder, so claimAsMoto reverts and ClaimedAsMoto and RewardNotified do
not fire.
RakebackV2
event RootPosted(uint256 indexed epoch, bytes32 root, uint64 postedAt);
event SlicePosted(uint256 indexed epoch, address indexed token, uint256 declaredTotal);
event Activated(uint256 indexed epoch, address indexed token, uint64 activatedAt);
event Claimed(
uint256 indexed epoch,
address indexed token,
uint256 index,
address account,
uint256 amount
);
event ClaimedAsMoto(
address indexed user,
address indexed token,
uint256 indexed epoch,
uint256 amountIn,
uint256 motoOut
);
event Expired(uint256 indexed epoch, address indexed token, uint256 remainder);
event PendingSliceExpired(uint256 indexed epoch, address indexed token, uint256 declared);
event RootCancelled(uint256 indexed epoch);
event InflowSynced(address indexed token, uint256 delta, uint256 lifetimeInflow);
event Swept(address indexed token, uint256 amount);
event PosterSet(address poster);
event GuardianSet(address guardian);
event ActivationDelaySet(uint64 activationDelay);
event TreasurySet(address treasury);
event FeeRouterSet(address feeRouter);RakebackV2.Claimed has 2 indexed args (epoch, token) and 5 total;
MotoStaking.Claimed has 2 indexed (account, token) and 3 total. Different
signatures, so their topic0 values do not collide - but both are named
Claimed, so filter by emitter address, not by name. Note that the claimant
is the un-indexed account field, so you cannot filter claims by wallet in a
topic.
MotocatStaking (NFT staking)
event Staked(address indexed wallet, uint256[] tokenIds);
event Unstaked(address indexed wallet, uint256[] tokenIds);
event Seeded(address indexed wallet, uint256[] tokenIds);
event SeedingClosed();
event SeederSet(address indexed seeder);
event SeedSourceSet(address indexed seedSource);
event OutboxAdded(address indexed outbox);
event OutboxRemoved(address indexed outbox);
event Broadcast(address indexed wallet, uint256 stakedCount);
event BroadcastFailed(address indexed outbox, address indexed wallet);
event ERC20Rescued(address indexed token, address indexed to, uint256 amount);
event ERC721Rescued(address indexed token, address indexed to, uint256 tokenId);
event ETHRescued(address indexed to, uint256 amount);
event UpgradesRenounced();V3 pays no rewards, so it emits no Reward* events. Staked / Unstaked are
the only events that move a wallet's cat count; Broadcast mirrors that count
to a registered outbox and BroadcastFailed records an outbox that reverted
without blocking the stake.
CreatorFeeRegistry / CreatorFeeVault
// Registry
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);
// Vault - emits Accrued on every deposit, Claimed on every payout, DepositorSet on config
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);Collector
event BucketConfigured(uint256 indexed index, address recipient, uint96 weight, bool notify);
event BucketRecipientSet(uint256 indexed index, address recipient, bool notify);
event MinIntervalSet(uint256 minInterval);
event Distributed(address indexed token, uint256 amount);
event BucketPaid(address indexed token, uint256 indexed index, address recipient, uint256 amount);
event BucketSkipped(address indexed token, uint256 indexed index, bytes reason);
event BucketHeld(address indexed token, uint256 indexed index, address recipient, uint256 amount, uint256 totalHeld);
event HeldReleased(address indexed token, uint256 indexed index, address recipient, uint256 amount);
event HeldForceReleased(address indexed token, uint256 indexed index, uint256 amount);BuybackBurner / BuybackDistributor
// Burner
event KeeperSet(address keeper);
event FeeRouterSet(address feeRouter);
event BoughtBack(address indexed tokenIn, uint256 amountIn, uint256 motoOut);
event Burned(uint256 amount, uint256 totalBurned);
event PinnedPathSet(address indexed tokenIn, address[] path);
event NothingToDo(address indexed tokenIn);
event TwapToleranceSet(uint256 bps);
event CheckpointPoked(address indexed tokenIn, address indexed pair, uint256 priceCumulative, uint32 timestamp);
event TwapFloorBinding(address indexed tokenIn, uint256 keeperFloor, uint256 twapFloor);
// Merkle distributor
event Claimed(uint256 indexed index, address indexed account, uint256 amount);
event Swept(address indexed to, uint256 amount);
event SweptAll(address indexed to, uint256 amount);The three TWAP events belong to the burner's price floor, which guards a buyback against a
manipulated pool. Their configured values are not published here. BuybackDistributor is not
deployed and nothing schedules it.
RewardVestingEscrow / TreasuryVesting / Snapshotter
event Deposited(address indexed user, uint256 amount, uint256 scheduleTotal, uint64 start);
event Claimed(address indexed user, uint256 amount);
event DepositorSet(address indexed depositor, bool allowed);
event Released(uint256 amount, uint256 totalReleased); // TreasuryVesting
event SnapshotTaken(uint256 indexed blockNumber, uint256 timestamp); // SnapshotterRewardVestingEscrow.Deposited has topic0
0x19e7166e374f41f05d851e7f5774e0d8424541e4b4353728a88a4c84fe7ba133 and
RewardVestingEscrow.Claimed(address,uint256) has
0xd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a.
PveVault
event LauncherSet(address launcher);
event ExtraLauncherSet(address indexed launcher, bool allowed);
event MotocatStakingSet(address staking);
event PveCredited(address indexed token, uint256 amount, uint256 totalStaked, uint256 creditBlock);
event PveHeld(address indexed token, uint256 amount);
event PveClaimed(address indexed token, address indexed wallet, uint256 amount);
event PveReceived(address indexed token, address indexed creator, uint256 amount);
event Forwarded(address indexed token, address indexed to, uint256 amount);
event ExcessRescued(address indexed token, address indexed to, uint256 amount);
event PendingArrivalNoted(address indexed token);
event PendingArrivalCleared(address indexed token);
event PendingArrivalClearedByOwner(address indexed token, address indexed by);QuoteAssetRegistry
event QuoteSet(address indexed token, bool status);
event QuoteRankSet(address indexed token, uint256 rank);Integration Checklist
- Fetch addresses from
GET /config. Treat0x0000000000000000000000000000000000000000as unset and never send to it. - Add all contract addresses to your event-log filter (if watching on-chain events yourself).
- Decode via the canonical ABIs. The deployed contracts have verified source, and the event signatures on this page match it.
- The canonical trade event is
FeeRouter.Swap. The pairSwapis one-per-hop. - Rakeback claims need a merkle proof (
GET /rakeback/{address}, orGET /rakeback/{address}/prooffor proofs alone). - Points are indexer-derived and publish once a day at the 00:00 UTC drop - read via
GET /points/{address}, not on-chain. - All USD values are decimal-dollar strings on the wire (e.g.
"2500.50"); runtime µ-USD bigints exist only server-side. - All
bigintvalues are decimal strings on the wire. - All addresses are lowercased 0x-prefixed strings.
- The API caches heavily - send
If-None-Matchwith the previous ETag; expect frequent304s. - List endpoints use cursor pagination; the cursor is HMAC-scope-bound (endpoint + filters). Do not reuse a cursor across queries.
Motoswap REST API - Full Inventory
Complete REST API reference for integrators - every endpoint, params, response shape, cache tier.
moto.fun Contracts - Full Inventory
Complete API reference for external integrators - every moto.fun Solidity contract, function, event, error, constant, access rule and invariant, plus the canonical contracts the curve depends on.