TaifoonTAIFOON
Onboard a protocol

Guide · Onboard

Onboard a protocol

The layer knows chains, blocks, receipts and logs, not your protocol. Onboarding is three things: proving what your contracts did, checking those proofs, and registering your contracts and events so the layer attributes and decodes them.

What is proven, and on which chains

TierChainsWhat exists for a transaction of yoursRoute
Proven per transaction16, all EVM, read over the layer’s RPC rotationThe receipt, one log and the block, checked against the superrootGET /v1/proof/tx/:chain/:tx
Committed at the header level61 at the last sync; the live list is chains_included of GET /v1/rootA proof that the block is under the superroot; the transaction inside it is not proven through the public routesGET /v1/root/proof/:chain/:block
Not readEvery other chainNothing; the answer names the chains that are read404 chain_not_read

The 16 chains /v1/proof/tx accepts: Base (8453), Arc (5042), Ethereum (1), BSC (56), Avalanche C-Chain (43114), Arbitrum One (42161), Polygon (137), Optimism (10), Robinhood Chain (4663), Celo (42220), Taifoon devnet (36927), Taifoon mainnet (3692781), Abstract (2741), X Layer (196), Monad (143), Gnosis (100).

Get the proof of one of your transactions

shell
curl https://coord.taifoon.dev/v1/proof/tx/8453/0x0f7f432e5fb3e650618387cdfbe99e94c105675f7729cc85a9a61b6c678500ff
curl "https://coord.taifoon.dev/v1/proof/log/8453/0x0f7f432e5fb3e650618387cdfbe99e94c105675f7729cc85a9a61b6c678500ff?log_index=527"
curl "https://coord.taifoon.dev/v1/proof/blocks/8453?blocks=51419742,51419743"

The first answers proof_state (proven or not_final), checks and the blob; the second proves one log by its index in the block; the third proves up to 256 blocks of one chain in one compact proof. Over MCP, the tools taifoon_proof and taifoon_proof_tx on https://coord.taifoon.dev/mcp return the same, without a key.

Check it off chain

For the reference block of a chain, the walk from its leaf to the superroot is a few lines of keccak:

typescript
// the reference block of a chain: GET /v1/proof/verifiable/8453 → blob
import { keccak256, encodePacked, concat } from 'viem';
const h = blob.chainHeaders[0];
let node = keccak256(encodePacked(['uint64', 'uint64', 'bytes32', 'bytes32', 'uint64'],
  [BigInt(h.chainId), BigInt(h.blockNumber), h.blockHash, h.miniMmrRoot, BigInt(h.twigCount)]));
let index = blob.superrootProof.targetChainIndex;
for (const sibling of blob.superrootProof.siblings) {
  node = keccak256(index & 1 ? concat([sibling, node]) : concat([node, sibling]));
  index >>= 1;
}
const anchored = node === blob.superroot.superrootHash;   // the block is committed by this superroot

For any other block, fold its block path to the chain's tree root first, then walk the anchor the same way:

typescript
// any block: GET /v1/proof/blocks/8453?blocks=<a>,<b> → r
// 1. fold the batch to the chain's block-tree root: multiRoot(r.blockTree) === r.anchor.root
// 2. walk the chain's leaf up to the superroot, as above:
let node = keccak256(encodePacked(['uint64', 'uint64', 'bytes32', 'bytes32', 'uint64'],
  [BigInt(r.chainId), BigInt(r.anchor.tipBlockNumber), r.anchor.tipBlockHash, r.anchor.root, BigInt(r.anchor.twigCount)]));
let index = r.anchor.chainIndex;
for (const s of r.anchor.l3Siblings) { node = keccak256(index & 1 ? concat([s, node]) : concat([node, s])); index >>= 1; }
// node === r.anchor.superrootHash
// multiRoot: sorted (index, leaf) pairs; at each level adjacent siblings pair up, otherwise the next sibling is
// EMPTY[level] when the next bit of empty_flags (LSB first) is 1, else the next hash of nodes.
// EMPTY[0] = 32 zero bytes, EMPTY[l+1] = keccak(EMPTY[l] ‖ EMPTY[l]). On chain: TaifoonMerkle.multiRoot, about 18k gas.

Then compare the superroot with GET /v1/root/latest. /proof runs the same check in your browser.

Check it on Base

The verifier 0x2D475a53eAA8F9AC78B69bD2429275a622C644f8 checks a proof with no gas through eth_call: verifyWithReceipt(blob, receiptInclusion), where the receipt inclusion is the header RLP, rlp(txIndex), the receipt RLP, its trie nodes and the log index, built from eth_getBlockReceipts. To act on a proof, call your contract through the executor 0x785F4701C681dCBa8762FcfaF18db4048215c0ca with executeVerifiedCallV5Strict. The verifier checks against the superroot the caller passes, so compare it with GET /v1/root/latest before you act.

Register your contracts and events

Proofs need no registration. Registration adds attribution (which protocol a log belongs to) and decoding (its typed fields). Check a manifest first with dry_run; nothing is kept. Put your own contract addresses in place of the two placeholders; the answer names the tier of every chain you list. Without it, the manifest is kept for an operator to land, and GET /v1/protocols/pending shows its status.

shell
curl -X POST https://coord.taifoon.dev/v1/protocols/register -H 'content-type: application/json' -d '{
  "id": "your_protocol", "name": "Your Protocol", "type": "bridge",
  "contracts": { "8453": ["0x1111111111111111111111111111111111111111"], "42161": ["0x2222222222222222222222222222222222222222"] },
  "source_topic": "0xccaa6c6104b9424e66da56435eb3f3c7b812d753665ecee1ea57592944473782",
  "fill_topic": null,
  "events": [ { "name": "OrderOpened", "signature": "OrderOpened(bytes32,address,address,uint256,uint256,uint32)", "topic0": "0xccaa6c6104b9424e66da56435eb3f3c7b812d753665ecee1ea57592944473782", "role": "deposit", "indexed": 2 } ],
  "identity": "mechanism", "docs": "https://docs.your-protocol.example",
  "dry_run": true
}'
LayerAvailable todayNot available today
ProofsGET /v1/proof/tx/{chain}/{tx} proves any transaction, registered or not, on the 16 chains the layer reads; a block proof (GET https://api.taifoon.dev/api/v5/proof/blob/{chain}/{block}) exists for every chain the superroot commits (61 at the last sync; GET /v1/root chains_included is the live list); a transaction proof for them is the producer reading that chain over RPC, which registration does not doA transaction proof on a chain the layer only commits; anything on a chain it does not read
Attributionthe manifest is checked here (topics against signatures, addresses, chains, clashes) and kept as a pending manifest the operator lands; the producer attributes a log to a protocol by topic and address from its static table and from a dynamic cache refreshed every 300 s from the search registrya self-serve write into the producer: the dynamic cache has no taker configured and the static table and the JSON file are compiled into the image, so the fragment below is landed by an operator and shipped with a rebuild
Decodingattribution (protocol, event name, chain, block, tx, log index) and the L5 proof of the logtyped fields (amounts, tokens, destination chain, recipient): the producer decodes them in hand-written Rust per protocol; until an arm exists for yours the order card shows PARTIAL DECODE with generic placeholders; the ABI you send is kept with the manifest for that arm

The check refuses a topic0 that is not keccak256 of its canonical signature, more than three indexed inputs, an address that is not 20 bytes, a chain the superroot does not commit, a generic ERC-20 or WETH topic, and a topic that already belongs to one of the 27 decoded protocols (then identity: "deployment" counts it only from your addresses). GET /v1/protocols/decoders lists those protocols with their topics.

Watch the layer live

One server-sent stream carries what the layer observes as it happens: each new superroot, each block it ingests and the protocol events it attributes. It keeps no history; it pushes what happens from the moment you connect.

shell
curl -N https://www.taifoon.io/api/intel/genomes/stream

Each event is one line of JSON with entity, action, chain_id and a short message. Once your protocol is registered, its events can be filtered with ?protocol=<your id>.

Be listed as an agent

A protocol that also sells work registers a card, exactly as an agent does: see Be hired.

What it costs

StepCost
A free key, a card, the ERC-8004 mintFree; the mint is Base gas
A proof on the public routesFree. The x402 walls price the same proof at 0.1 USDC (proof.verify.v5) and 0.5 USDC (settlement-proof)
Checking itFree off chain and through eth_call; acting on it through the executor is Base gas
Registering contracts and eventsFree; landing a decoder is engineering, not a fee