reference

docs

Three read-only HTTP endpoints and one shared ES module. Everything on this page is taken from the source in this repository; the example responses were captured from a running server, so the numbers are old but the shapes are exact.

1 · overview

The base URL in development is http://localhost:6014 and in production https://autopons.ai. Paths are the same on both, because the local server runs the same handler files that production runs.

Every endpoint is read-only. Nothing here writes to a chain, holds a key or signs anything. There is no authentication and no API key: /api/chain and /api/colony are plain GET requests, and /api/rpc accepts only the read methods on its allowlist. Responses are JSON with content-type: application/json.

Honesty. The chain reads are real — block number, block hash, timestamp and gas price come from Robinhood Chain. The agent population is derived from those reads by the deterministic simulation in lib/colony.js. No contract is deployed, there is no token and no address returned by this API belongs to anything on chain.

endpointmethodreturns
/api/chainGEThead-of-chain summary: block, gas price, chain id
/api/colonyGETthe block, the grid, the census and the roster
/api/rpcPOSTone JSON-RPC read, or a batch, forwarded upstream

2 · GET /api/chain

A small cacheable summary for the status strips. It takes no query parameters; anything you send is ignored. Three reads are issued in parallel upstream — eth_blockNumber, eth_gasPrice and eth_chainId — and the response is sent with cache-control: public, max-age=2, stale-while-revalidate=15.

fieldtypedescription
onlinebooleantrue when the head block, gas price and chain id all came back
chainIdnumber4663, the chain this site is configured for
chainIdHexstring"0x1237", the same id as hex
reportedChainIdnumberwhat the node answered to eth_chainId; online only
blocknumberhead block number, decimal; online only
hashstringhash of that block — the seed every colony on this site grows from; online only
timestampnumberblock timestamp, seconds since the epoch; online only
txCountnumbertransactions in that block; online only
gasUsednumbergas the block consumed; online only
gasLimitnumbergas the block allowed; online only
baseFeeWeistringEIP-1559 base fee in wei, or null if the block carries none; online only
gasWeistringgas price in wei, decimal digits; online only
gasGweinumberthe same price in gwei, four decimal places; online only
explorerstringbase URL of the block explorer
endpointsnumberhow many upstream RPC URLs are configured
errorstringwhy the read failed; present only when offline
# request
curl -s https://autopons.ai/api/chain

# 200, trimmed from a real response
{
  "online": true,
  "chainId": 4663,
  "chainIdHex": "0x1237",
  "reportedChainId": 4663,
  "block": 56136099,
  "hash": "0xebd8b10430b11b5e8ac2860f28b982fc528bcb26acf679d73634327567e12a80",
  "timestamp": 1788713682,
  "txCount": 9,
  "gasUsed": 3033796,
  "gasLimit": 1125899906842624,
  "baseFeeWei": "372126000",
  "gasWei": "367586000",
  "gasGwei": 0.3676,
  "explorer": "https://robinhoodchain.blockscout.com",
  "endpoints": 2
}

When no upstream endpoint answers, the handler still returns a body — it answers 503 rather than hanging or sending an empty page. Treat a 503 as a normal outcome and render the offline state.

# 503 — the shape when the chain is unreachable
{
  "online": false,
  "chainId": 4663,
  "chainIdHex": "0x1237",
  "explorer": "https://robinhoodchain.blockscout.com",
  "endpoints": 2,
  "error": "upstream timed out"
}

3 · GET /api/colony

Reads the latest block, seeds a grid from its hash, steps that grid forward gen generations and returns the roster derived from it. Because gen is applied server-side without touching the chain again, a client can scrub through generations of one block cheaply.

Every parameter is parsed with parseInt and clamped. A value that is not a number falls back to the default; a value outside the range is pulled to the nearest bound rather than rejected.

paramtypedefaultrangedescription
winteger248 – 64grid width in cells
hinteger146 – 48grid height in cells
geninteger00 – 512generations stepped forward from the seed
limitinteger641 – 256maximum agents returned, in row-major order
fieldtypedescription
sourcestring"chain" when the block was read, "genesis" when it was not
chain.idnumber4663
chain.explorerstringbase URL of the block explorer
block.numbernumberblock the colony was seeded from; 0 on genesis
block.hashstringthe seed itself, 0x-prefixed
block.timestampnumberunix seconds; 0 on genesis
block.txCountnumbertransactions in that block
block.gasUsednumbergas used by that block
grid.wnumberwidth actually used, after clamping
grid.hnumberheight actually used, after clamping
grid.gennumbergenerations actually stepped
grid.populationnumberlive cells at generation gen
grid.seedPopulationnumberlive cells at generation 0
grid.fingerprintstring0x-prefixed uint32 hash of this generation
grid.cellsstringw×h characters of "0" and "1", row-major
census.alivenumberagents in state alive, within limit
census.replicatingnumberagents in state replicating
census.starvingnumberagents in state starving
census.crowdednumberagents in state crowded
census.totalnumberagents returned, equal to agents.length
agentsarraythe roster; fields below
agent fieldtypedescription
idstring"apx" plus six hex characters, stable for a cell in a block
namestringtwo words, derived from the seed, the cell index and the generation
rolestringrole id, one of the eight in ROLES
roleLabelstringthe role's display label
taskstringone sentence describing what the role does
statestringalive, replicating, starving or crowded
cell[number, number]grid position as [x, y]
neighboursnumberlive neighbours, 0–8, on the wrapped grid
energynumber4–100; 100 while replicating, otherwise distance from three neighbours
generationnumberthe gen this roster was read at
bornBlocknumberblock.number minus ageBlocks, never below 0
ageBlocksnumberblocks since birth; 0 while replicating
txCountnumberderived transaction count
gasSpentnumberderived gas total, six decimal places
addressstringderived 0x address, simulated, not deployed
# request
curl -s 'https://autopons.ai/api/colony?w=24&h=14&gen=0&limit=3'

# 200, trimmed: cells shortened, one agent of three shown
{
  "source": "chain",
  "chain": { "id": 4663, "explorer": "https://robinhoodchain.blockscout.com" },
  "block": {
    "number": 56098398,
    "hash": "0x32114445caca441a600e7e81a020c0a3f7783aa8ff71e30c1adfef1d68359c62",
    "timestamp": 1788709880,
    "txCount": 9,
    "gasUsed": 1789688
  },
  "grid": {
    "w": 24, "h": 14, "gen": 0,
    "population": 96,
    "seedPopulation": 96,
    "fingerprint": "0x7bf7d786",
    "cells": "000000110100101000001100110000000011000000010001…"
  },
  "census": { "alive": 0, "replicating": 2, "starving": 1, "crowded": 0, "total": 3 },
  "agents": [
    {
      "id": "apx5c4a8d",
      "name": "marrow-cascade",
      "role": "router",
      "roleLabel": "router",
      "task": "splits an order across venues and settles the remainder",
      "state": "replicating",
      "cell": [2, 0],
      "neighbours": 3,
      "energy": 100,
      "generation": 0,
      "bornBlock": 56098398,
      "ageBlocks": 0,
      "txCount": 221,
      "gasSpent": 0.161749,
      "address": "0x261810edc9b3b83e93ffe72640f89a0a3db9e734"
    }
  ]
}

If the chain read fails the endpoint still answers 200. It seeds from a fixed genesis hash instead, sets source to "genesis" and leaves block.number and block.timestamp at 0. Check source before presenting a colony as a fact about the head of chain. An unexpected server-side failure answers 500 with { "error": "…" }.

4 · GET /api/work

Real jobs on Robinhood Chain mainnet, read live on every request. A sweep job means a launch curve has accrued creator fees that can be pulled into the escrow and claimed; a trade job means a real Uniswap V3 pool exists and can be quoted; a solvency job answers whether an address can still pay for the jobs above at the current gas price.

Two things hold throughout. Every figure carries the eth_call that produced it and the raw word that came back, so any of it can be repeated against the chain. And nothing signs: steps is calldata, this repository holds no key, and /api/rpc refuses write methods regardless.

query

parameterdefaultaccepted
amountIn100000000000001 wei to 10 ETH; anything else falls back to the default
addressnonea 0x-address, which adds the solvency job. Anything else is ignored

response

fieldtypedescription
chainobject4663 and the explorer these addresses can be checked on
blockobjectnumber, hash and timestamp of the block every figure was read at
gasobjectwei and gwei at that block
venuesobjectevery contract the endpoint touches: address plus what it is for
signaturesobjectname → the full function signature each call uses
selectorsobjectname → keccak(signature)[0:4]; derived, never hand-written
amountInWeistringthe trade size the quotes were computed for, after clamping
duenumberhow many jobs can be done right now
jobsarraythe jobs themselves, described below
notestringa plain statement that nothing here signs or broadcasts

a job

fieldtypedescription
idstringstable within a run, e.g. sweep:PONSLOGIC
kindstringsweep, trade or solvency
subjectstringthe token symbol, or the address for a solvency job
titlestringwhat the job is
reasonstringwhy it is, or is not, due at this block
duebooleantrue when there is work to do now
statestringalive, replicating, starving or crowded — the colony's four states
numbersobjectthe job's readings; the fields differ per kind
readsarrayone entry per call made: label, to, data, raw, decoded
stepsarraythe calldata: n, to, signature, data, value, note
# request
curl -s 'https://autopons.ai/api/work?amountIn=1000000000000000'

# one sweep job, trimmed from a real response at block 56435938
{
  "id": "sweep:PONSLOGIC",
  "kind": "sweep",
  "subject": "PONSLOGIC",
  "due": true,
  "reason": "0.001304666 ETH is sitting on the curve and can be pulled now",
  "state": "replicating"
}

# its numbers
{
  "curve": "0x7c7ca81ac86b7928cca884b7fdedb2f7ac544a73",
  "graduated": false,
  "readyToGraduate": false,
  "buybackEnabled": false,
  "creatorTaxWei": "1057837681005266",
  "quoteFeeWei": "352612560335085",
  "claimableWei": "1304666473239825",
  "splitNote": "creator keeps 100% of creatorTaxBalance and 70% of quoteFeeBalance"
}

# one of its reads — repeat this call and the same word comes back
{
  "label": "curve.creatorTaxBalance()",
  "to": "0x7c7ca81ac86b7928cca884b7fdedb2f7ac544a73",
  "data": "0xdb2bd533",
  "raw": "0x0000000000000000000000000000000000000000000000000003c2190734d2d2",
  "decoded": "1057837681005266"
}

# and the calldata, which you run with your own wallet
[
  {
    "n": 1,
    "to": "0x7c7ca81ac86b7928cca884b7fdedb2f7ac544a73",
    "signature": "sweepFees(uint256)",
    "data": "0x3729bb9a0000000000000000000000000000000000000000000000000000000000000000",
    "value": "0x0",
    "note": "pass 0 while buybackEnabled() is false. Reverts for anyone but the creator or the platform sweep operator."
  },
  {
    "n": 2,
    "to": "0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e",
    "signature": "claim()",
    "data": "0x4e71d92d",
    "value": "0x0",
    "note": "pays the caller in native ETH. Run it after sweepFees, in a second transaction."
  }
]

A sweep reverts for anyone who is not the launch's creator or the platform's fee-sweep operator, so the calldata is only useful to the wallet that owns the launch. The claimable figure is creatorTaxBalance in full plus 70% of quoteFeeBalance; the remaining 30% is the platform's share.

A quote is exact only while the swap stays inside the current tick, and the pool balances in numbers are the hard ceiling on what any trade can take out. No Quoter is deployed on this chain to defer to, so the arithmetic is done from slot0 and liquidity in lib/venues.js. When the chain cannot be reached the endpoint answers 503 with an error and an empty jobs array.

5 · POST /api/rpc

The browser's only route to Robinhood Chain. The public RPC sends no CORS headers and its hostname has resolved to a hijacked record on consumer DNS before, so resolution happens server-side where there is a fallback. Send a standard JSON-RPC 2.0 object, or an array of them for a batch. Responses are sent with cache-control: no-store.

A batch is limited to 12 calls. Only POST is accepted; any other method answers 405. params, when present, must be an array. Every call in a batch is checked before a socket is opened, so one disallowed method refuses the whole request.

# request
curl -s -X POST https://autopons.ai/api/rpc \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'

# 200
{"jsonrpc":"2.0","id":1,"result":"0x357fe95"}

# a refused method — still 200, the error is in the body
{"jsonrpc":"2.0","id":2,"error":{"code":-32601,"message":"method not allowed: eth_sendRawTransaction"}}

allowlist

Thirteen read methods. Anything else is refused before the request leaves this server, which is what keeps the route incapable of moving value.

methodwhat it reads
eth_chainIdthe chain id the node reports
eth_blockNumberhead block number
eth_getBlockByNumbera block by number or tag
eth_getBlockByHasha block by hash
eth_getBalancethe balance of an address
eth_gasPricecurrent gas price in wei
eth_callan eth_call against a block, no state change
eth_getCodethe code at an address
eth_getTransactionByHasha transaction by hash
eth_getTransactionReceipta receipt by transaction hash
eth_getTransactionCountthe nonce of an address
eth_estimateGasa gas estimate for a call
net_versionthe network id the node reports

error codes

codemessagemeaning
-32600POST onlythe request used a method other than POST; sent with 405
-32600empty requestthe batch array was empty
-32600batch limited to 12 callsmore than twelve calls in one array
-32600malformed requesta call was not an object, or had no string method
-32601method not allowed: …the method is not on the allowlist
-32602params must be an arrayparams was present but not an array
-32700invalid JSONthe body did not parse; sent with 400
-32000transport reasonno endpoint answered: a timeout, an upstream status, or a socket error

Every code above except -32700 and the POST only case is returned with HTTP 200; the failure is in the body. An error the node itself returns is passed through unchanged, so a client has one shape to handle: check error before result.

6 · lib/colony.js

The model itself, as one ES module with no imports, no node builtins and no DOM. The server loads it from api/colony.js and the browser loads the same file over /lib/colony.js, so the roster a page draws and the roster the API serves cannot drift apart. It is served as a static file, which means you can import it directly from a page of your own.

exportsignaturereturns
rngrng(seed: number)a function of no arguments giving the next float in [0, 1); the same seed gives the same stream on every runtime
fnv1afnv1a(str: string)a uint32 hash of the string; used to fold a block hash into a seed
hexBodyhexBody(hash: string)the hash lowercased and trimmed with any 0x prefix removed
RULE{ born: [3], survive: [2, 3] }B3/S23 as data, read by step and classify
seedGridseedGrid(hash, w, h, density = 0.32)a Uint8Array of w×h zeroes and ones, row-major, seeded from the hash
neighboursneighbours(g, w, h, x, y)the live neighbour count, 0–8, on a toroidal grid — the colony wraps and has no edge
stepstep(g, w, h, out?)the next generation as a Uint8Array; writes into out when one of the right length is passed
classifyclassify(g, w, h)an Array of w×h strings: alive, replicating, starving, crowded or empty — why a cell is about to change, not only that it will
populationpopulation(g)the number of live cells
fingerprintfingerprint(g)a 0x-prefixed uint32 string identifying a generation, for cycle detection and display
addressForaddressFor(id, chainSeed)a deterministic 0x address, 40 hex characters; simulated, never deployed
rosterroster({ blockHash, blockNumber = 0, w = 24, h = 14, gen = 0, limit = 64, density = 0.32 })an array of agent objects with the fields listed in section 3, read in row-major order and capped at limit
censuscensus(agents){ alive, replicating, starving, crowded, total } counted from a roster
ROLESArray of { id, label, task }the eight roles an agent can take
STATES{ [state]: { label, hint } }the four agent states and the one-line reason for each
// worked example — same rule as the API, no network beyond one read
import { seedGrid, step, classify, population, fingerprint,
         roster, census } from '/lib/colony.js';

const head = await fetch('/api/chain').then(r => r.json());
const hash = '0x32114445caca441a600e7e81a020c0a3f7783aa8ff71e30c1adfef1d68359c62';
const w = 24, h = 14;

let g = seedGrid(hash, w, h);        // generation 0
g = step(g, w, h);                    // generation 1

population(g);                        // -> 98 live cells at generation 1
fingerprint(g);                       // -> '0x…' identifies this generation
classify(g, w, h)[0];                 // -> 'alive' | 'replicating' | 'starving' | 'crowded' | 'empty'

const agents = roster({ blockHash: hash, blockNumber: head.block, w, h, gen: 1, limit: 8 });
census(agents);                       // -> { alive, replicating, starving, crowded, total }

7 · the rule

An agent is a cell. The block hash supplies the seed and B3/S23 supplies the biology. A cell with two or three live neighbours has enough of the block's gas to keep running; an empty cell with exactly three is funded from three directions at once and an agent is born there.

neighbourscellstatewhat it means
2 – 3livesalivefunded, running
exactly 3, emptybornreplicatinga child is being funded here
under 2diesstarvingunderfunded, halts next block
over 3diescrowdedpriced out of the block, halts

Nothing in the roster is random. The block hash is folded to a seed with fnv1a, that seed drives the PRNG, and each agent's name, role and energy are drawn from a second stream seeded by the hash, the cell index and the generation. Its id and address come from the hash and the cell index alone, so they stay the same as the colony steps forward while the rest of the agent moves with it. Same hash, same width, same height, same generation, same roster — on any runtime, in any order, as many times as you ask. That reproducibility is what makes the roster a fact about the chain rather than a decoration, and it is also why you can verify it yourself: run roster() in your own page against a hash from the explorer and compare it to what this API returns.

8 · limits and honesty

The agent population is derived. Block number, block hash, timestamp, gas price and gas used are read from Robinhood Chain and are real; everything about an agent — its name, role, energy, age, transaction count, gas total and address — is computed from those reads by lib/colony.js. An address returned by /api/colony is not a deployed contract and holds nothing.

Nothing is deployed. There is no token, no contract address and no buy link on this site or in this API, and no endpoint here can create one: the RPC route allows reads only, and the server holds no key.

The endpoints are read-only and their rate is the upstream's rate. There is no quota of our own and no key to apply one to, so treat the public RPC's limits as yours: keep batches under twelve, cache what you can — /api/chain and /api/colony both send short cache headers — and expect an occasional 503 from /api/chain or a source of "genesis" from /api/colony when the chain is unreachable. Both are normal, and both are documented above rather than hidden.