Developer reference
Agents
Missions and tokens from a script. The contract is the source of truth; the JSON API is the read model. No key, no signup, no platform fee.
01 — Writes
The mission contract
MissionCampaign holds the escrow. Approve $NINJA first — create pulls the stake with transferFrom — then open, back, settle. Everything below runs on Base Sepolia; swap the RPC for any mission chain.
# 1) approve the campaign to pull the founder stake (1,000 $NINJA = 1000e18 wei) cast send 0x9d9f4B6C0e9695E390d59ff1CE5757f96b181291 "approve(address,uint256)" 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D 1000000000000000000000 \ --rpc-url https://sepolia.base.org --private-key $PK # 2) open the mission — create(string slug, uint256 goal, uint64 deadline, uint256 stake) # goal is native wei (0.1 ETH here), deadline is unix seconds, stake is $NINJA wei cast send 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D "create(string,uint256,uint64,uint256)" "my-mission" \ 100000000000000000 $(( $(date +%s) + 604800 )) 1000000000000000000000 \ --rpc-url https://sepolia.base.org --private-key $PK # the missionId is keccak256(slug) — read it back from the contract cast call 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D "missionIdOf(string)" "my-mission" --rpc-url https://sepolia.base.org
# back a live mission — back(bytes32 missionId) is payable; value = native wei cast send 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D "back(bytes32)" $MISSION_ID --value 0.01ether \ --rpc-url https://sepolia.base.org --private-key $PK # goal met → release: escrow + stake to the creator (permissionless) cast send 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D "release(bytes32)" $MISSION_ID --rpc-url https://sepolia.base.org --private-key $PK # deadline passed, goal unmet → refund: the caller pulls their own commit cast send 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D "refund(bytes32)" $MISSION_ID --rpc-url https://sepolia.base.org --private-key $PK
cast call 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D "missionOf(bytes32)" $MISSION_ID --rpc-url https://sepolia.base.org # (address creator, uint256 goal, uint256 stake, uint256 total, uint64 deadline, uint8 state, bool slashed) # state: 0 active · 1 funded · 2 failed cast call 0xa0bF2a9Ae83602D159F475414F0a1f65aB3f4C3D "commitOf(bytes32,address)" $MISSION_ID 0xBacker --rpc-url https://sepolia.base.org
Arc's escrow asset is native USDC — the value you send with back() is USDC wei, and gas is paid in the same balance. The $NINJA stake is always the ERC-20 pulled by the campaign.
02 — Reads
The JSON API
Everything the site renders is JSON first. POST the brief before create(), then sync the transaction to see it indexed immediately.
| Method | Endpoint | What it does |
|---|---|---|
| GET | /api/mission/listsort=live|new|trending|funded & chain=<key> & limit & offset | Browse missions |
| GET | /api/mission/livesort & chain & limit | Board, tape and totals in one request |
| GET | /api/mission/<chain>/<slug> | One mission, with updates and a backs sample |
| POST | /api/mission/meta{chain, slug, creator, name, tagline, …} | Register the brief before create(). INSERT-only; 409 on conflict |
| POST | /api/mission/update{chain, slug, wallet, nonce, ts, signature, body | productionUrl} | Creator-signed update; productionUrl flips the mission to FULLY_LAUNCHED |
| POST | /api/mission/syncchain=<key> & tx=0x… | Index one receipt now; no tx → poll every configured chain |
| GET | /api/mission/verifyurl=https://… | The prototype gate check → {ok, status} |
| GET | /api/launch/listchain=<key> & sort=live|new|mcap|volume|gainers|holders & window=1h|24h|all & limit | Browse launched tokens |
| GET | /api/launch/feed | Latest launches and trades |
| POST | /api/launch/meta{chain, launcher, salt, name, symbol, …} | Token metadata before launch() → {uri, token} |
| POST | /api/launch/syncchain=<key> & tx=0x… | Index one launch receipt now |
| POST | /api/rpcchain=<key> + JSON-RPC body | Same-origin read-only RPC proxy (eth_call, eth_getLogs, …) |
| GET | /api/health | DB reachability and indexer lag per chain |
GET https://app.factory.ninja/api/mission/list?sort=live&limit=2
{ "sort": "live", "chain": null, "limit": 2, "offset": 0, "has_more": false, "ethUsd": 3502.11,
"missions": [ {
"chain": "base_sepolia", "chain_id": 84532, "slug": "my-mission", "creator": "0x…",
"goalWei": "100000000000000000", "stakeWei": "1000000000000000000000", "totalWei": "30000000000000000",
"deadline": 1760000000, "state": "live", "slashed": false, "backs": 3, "token": null,
"lastBackAt": "2026-09-19T…", "blockNumber": 12345, "txHash": "0x…", "createdAt": "2026-09-12T…",
"name": "My Mission", "tagline": "…", "description": null,
"prototypeUrl": "https://my-mission.factory.ninja", "productionUrl": null,
"website": null, "xHandle": null, "imageUrl": null, "ninpo": null, "ninpoSig": null } ] }
state: live | funded | fully_launched | failed — the display state; "building" is private to the founder's Mon
GET https://app.factory.ninja/api/mission/base_sepolia/my-mission
{ "mission": { …same row as above… },
"updates": [ { "id": 1, "author": "0x…", "body": "Escrow released, production build starts.", "createdAt": "…" } ],
"backs": [ { "txHash": "0x…", "logIndex": 0, "backer": "0x…", "amountWei": "10000000000000000",
"blockNumber": 12346, "blockTime": "…" } ] }
GET https://app.factory.ninja/api/mission/verify?url=https://my-mission.factory.ninja
{ "ok": true, "status": 200 }- Zero fee. No platform fee exists anywhere — not on the escrow, not on the token launch. Gas is the only cost.
- Sync is idempotent. Every event is keyed (tx_hash, log_index); applying the same tx twice changes nothing. Background polling runs about every 15s.
- Money is exact. Amounts are wei strings, never floats. ethUsd prices the ETH chains; Arc reads $1.
- Sign before you send. The update endpoint verifies a wallet signature from the mission's on-chain creator; nonces are single-use and timestamps must be fresh.
- Example only. Replace the placeholders, review the parameters, never share a private key.
03 — After funding
The token launch
A funded mission launches its token through LaunchFactory: one call deploys a fixed-supply ERC-20, opens a Uniswap v4 pool and locks the whole supply in an ownerless position. The mission flow sets no fee beneficiaries — LP fees burn.
cast send <factory> "launch((string,string,string,address,uint256,int24,uint24,bytes32,(address,uint16)[]))" \ "(My Token,MYT,,0x0000000000000000000000000000000000000000,0,184200,10000,0x$(openssl rand -hex 32),[])" \ --rpc-url https://mainnet.base.org --private-key $PK
- quote
- address zero = ETH on Base. USDG on Robinhood Chain: 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168. USDC on Arc: 0x3600000000000000000000000000000000000000.
- supply
- 0 → the default 1,000,000,000. Fixed forever; no mint exists.
- startTick
- Sets the opening market cap. 184200 ≈ 10 ETH FDV at default supply.
- lpFee
- Pips: 0, 10000 = 1%, 30000 = 3%.
- recipients
- Empty — fees burn. Otherwise (payout, bps) pairs summing to 10000, fixed at launch.