{"name":"onboard-a-protocol","title":"Onboard a protocol","category":"Contribute","summary":"One manifest: contracts, events, and the order machine. The API checks it before anything is kept.","use_when":"You make a protocol's orders decodable on the layer, or fix its manifest.","not_when":"You contribute one definition file, or register an agent.","description":"Onboard a protocol to the Taifoon coordination layer with a manifest: its contracts per chain, its events with topic0, and (v2) the order machine: key, states, transitions with bind paths and guards, actions, ABI and finality type. POST /v1/protocols/register checks everything and, with dry_run, keeps nothing. Use when the task is to make a bridge, intent, aggregator, DEX or AMM protocol's orders decodable and attributable on the layer, or to write or fix such a manifest. Do NOT use for a single transitions file, schema or decoder table on its own (use contribute-definitions) or to register an agent (use get-hired).","license":"TSUL","compatibility":"bash, curl, jq; Taifoon coordination API v1 at https://coord.taifoon.dev/v1","first_call":"POST /v1/protocols/register","success":"a manifest that passes the dry run","verified":"2026-10-03","hash":"sha256:4710a582317e0c22acd79428f593094e1c54184689b29cafb539640853038219","commands":{"ok":true,"blocks":7,"ran_at":"2026-10-03T22:13:48.466Z"},"urls":{"page":"https://www.taifoon.io/skills/onboard-a-protocol","markdown":"https://www.taifoon.io/skills/onboard-a-protocol.md","json":"https://www.taifoon.io/skills/onboard-a-protocol.json","well_known":"https://www.taifoon.io/.well-known/skills/onboard-a-protocol/SKILL.md","github":"https://github.com/taifoon-io/skills/tree/main/onboard-a-protocol"},"install":{"cli":"npx skills add https://www.taifoon.io --skill onboard-a-protocol","curl":"curl -sL https://www.taifoon.io/skills/onboard-a-protocol.md"},"markdown":"---\nname: onboard-a-protocol\ndescription: \"Onboard a protocol to the Taifoon coordination layer with a manifest: its contracts per chain, its events with topic0, and (v2) the order machine: key, states, transitions with bind paths and guards, actions, ABI and finality type. POST /v1/protocols/register checks everything and, with dry_run, keeps nothing. Use when the task is to make a bridge, intent, aggregator, DEX or AMM protocol's orders decodable and attributable on the layer, or to write or fix such a manifest. Do NOT use for a single transitions file, schema or decoder table on its own (use contribute-definitions) or to register an agent (use get-hired).\"\nlicense: TSUL\ncompatibility: \"bash, curl, jq; Taifoon coordination API v1 at https://coord.taifoon.dev/v1\"\nmetadata:\n  title: \"Onboard a protocol\"\n  category: \"Contribute\"\n  summary: \"One manifest: contracts, events, and the order machine. The API checks it before anything is kept.\"\n  use_when: \"You make a protocol's orders decodable on the layer, or fix its manifest.\"\n  not_when: \"You contribute one definition file, or register an agent.\"\n  first_call: \"POST /v1/protocols/register\"\n  success: \"a manifest that passes the dry run\"\n  verified: \"2026-10-03\"\n---\n\n# Onboard a protocol\n\n## What this is\n\nA manifest tells the layer how to read a protocol: which contracts on which chains, which event opens an order and which\nfills it, and, in version 2, the machine: the order key, the states, each transition (trigger event, bind paths resolved\nagainst the ABI, guards, join role, proof), actions as unsigned-call templates, and the finality type. Every check the\nmanifest allows is made at intake; 422 lists every problem. A manifest that passes is queued as `submitted` for approval.\nThe answer's `does` says what is proven, attributed and decoded for it today, and what is not.\n\n## Before you start\n\n- Base URL: `https://coord.taifoon.dev/v1`. No key needed for a dry run.\n- You need: contract addresses per chain id, the canonical event signatures, and the ABI of those events.\n- `topic0` is keccak256 of the canonical signature. A wrong one is refused with the right value in the message.\n\n```sh\nexport TAIFOON=https://coord.taifoon.dev/v1\ntf() { curl -sS -m 90 -H \"X-Taifoon-Client: taifoon-skill-onboard-a-protocol\" -H \"content-type: application/json\" ${TAIFOON_API_KEY:+-H \"X-API-Key: $TAIFOON_API_KEY\"} \"$@\"; }\n```\n\n## Pitfalls\n\n1. Submitting before a dry run. `\"dry_run\": true` checks and keeps nothing. Without it a passing manifest is queued.\n2. `id` not snake_case. A letter, then 2 to 40 of `a-z 0-9 _`.\n3. A `source_topic` that is not the `topic0` of one of `events`.\n4. `identity: \"mechanism\"` for a shared event. When other protocols emit the same topic, use `\"deployment\"`: only your\n   registered addresses count.\n5. A key without the chain. `key` must contain the chain: `\"{id}:{src_chain}:{guid}\"` with `src_chain` bound to `$chain`.\n6. A transition that moves an order without `join`. Only the opening transition (`from: null`) has none; every other names\n   the role it finds the order by.\n7. Re-sending a changed manifest under the same id and version: 409 `version_conflict`. Bump `v2.version`.\n8. Reading registration as proof coverage. Any transaction on a chain the layer reads over RPC is provable, registered or\n   not; `does.proofs` says how many of your chains that covers.\n\n## Steps\n\n### 1. What is decoded today, and the shape\n\n```sh\ntf \"$TAIFOON/protocols/decoders\" | jq -e '.ok and (.decoded | length > 0) and (.register.path == \"/v1/protocols/register\") and (.chains.per_transaction > 0)' >/dev/null\n```\n\n### 2. Write the manifest\n\nA complete v2 manifest for a two-event order protocol (replace the contract with your own):\n\n```sh\ncat > /tmp/taifoon-manifest.json <<'JSON'\n{ \"dry_run\": true,\n  \"id\": \"skill_demo_orders\", \"name\": \"Skill demo orders\", \"type\": \"intent\",\n  \"contracts\": { \"8453\": [\"0x000000000000000000000000000000000000dEaD\"] },\n  \"source_topic\": \"0x9ff48ec82167a0517e3606169155695cae17c29cbebaf9860e0637032d243091\",\n  \"fill_topic\": \"0x0555709e59fb225fcf12cc582a9e5f7fd8eea54c91f3dc500ab9d8c37c507770\",\n  \"events\": [\n    { \"name\": \"OrderPlaced\", \"signature\": \"OrderPlaced(bytes32,address,uint256)\",\n      \"topic0\": \"0x9ff48ec82167a0517e3606169155695cae17c29cbebaf9860e0637032d243091\", \"role\": \"deposit\", \"indexed\": 2 },\n    { \"name\": \"OrderFilled\", \"signature\": \"OrderFilled(bytes32,address)\",\n      \"topic0\": \"0x0555709e59fb225fcf12cc582a9e5f7fd8eea54c91f3dc500ab9d8c37c507770\", \"role\": \"fill\", \"indexed\": 2 } ],\n  \"identity\": \"deployment\", \"supported_dst\": [8453], \"docs\": \"https://example.org/docs\",\n  \"v2\": {\n    \"version\": 1,\n    \"key\": \"{id}:{src_chain}:{guid}\",\n    \"states\": [\"Placed\", \"Filled\"],\n    \"transitions\": [\n      { \"name\": \"place\", \"from\": null, \"to\": \"Placed\",\n        \"trigger\": { \"event\": \"OrderPlaced(bytes32,address,uint256)\", \"side\": \"source\", \"contracts\": \"registered\" },\n        \"bind\": { \"guid\": \"topics[1]\", \"src_chain\": \"$chain\", \"maker\": \"topics[2]\", \"amount\": \"args.amount\" },\n        \"guards\": [\"args.amount > 0\"], \"proof\": \"receipt\" },\n      { \"name\": \"fill\", \"from\": \"Placed\", \"to\": \"Filled\",\n        \"trigger\": { \"event\": \"OrderFilled(bytes32,address)\", \"side\": \"fill\", \"contracts\": \"registered\" },\n        \"bind\": { \"guid\": \"topics[1]\", \"filler\": \"topics[2]\" }, \"join\": \"guid\", \"proof\": \"receipt\" } ],\n    \"actions\": [],\n    \"abi\": [\n      { \"type\": \"event\", \"name\": \"OrderPlaced\", \"inputs\": [\n        { \"name\": \"guid\", \"type\": \"bytes32\", \"indexed\": true }, { \"name\": \"maker\", \"type\": \"address\", \"indexed\": true },\n        { \"name\": \"amount\", \"type\": \"uint256\", \"indexed\": false } ] },\n      { \"type\": \"event\", \"name\": \"OrderFilled\", \"inputs\": [\n        { \"name\": \"guid\", \"type\": \"bytes32\", \"indexed\": true }, { \"name\": \"filler\", \"type\": \"address\", \"indexed\": true } ] } ],\n    \"finality\": \"OP_DISPUTE_GAME\",\n    \"author\": \"0x000000000000000000000000000000000000dEaD\" } }\nJSON\n```\n\nBind paths: `topics[N]`, `args.<name>`, `args[N]`, `$chain`, `$emitter`, `$tx`, `$block`, `$ts`, `$log_index`,\n`keccak(<path>)`, `lookup(<table>, <path>)`, `sibling(<signature>).<path>`. Finality is one of the V5 types, among them\n`ETH_POS_CHECKPOINT`, `L2_OUTPUT_ROOT`, `OP_DISPUTE_GAME`, `ARB_BOLD`, `ZK_ROLLUP`, `INSTANT`, `DEPTH_BASED`.\n\n### 3. Dry run it\n\n```sh\ntf -X POST \"$TAIFOON/protocols/register\" -d @/tmp/taifoon-manifest.json \\\n  | jq -e '.ok and .kept == \"dry_run\" and .id == \"skill_demo_orders\" and (.hash | test(\"^0x[0-9a-f]{64}$\")) and (.does.proofs | type == \"object\") and (.fragments | type == \"object\")' >/dev/null\n```\n\n`hash` is keccak256 of the canonical JSON. `fragments` are the three pieces the producer's registries take. `does` says\nwhat is available for it today.\n\n### 4. See a refusal name the fix\n\n```sh\njq '.events[0].topic0 = \"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"' /tmp/taifoon-manifest.json \\\n  | tf -X POST \"$TAIFOON/protocols/register\" -d @- \\\n  | jq -e '.ok == false and (.problems | map(test(\"is not keccak256 of OrderPlaced\")) | any)' >/dev/null\n```\n\n### 5. Submit, then watch the queue\n\nRemove `\"dry_run\": true` and send it again: 201, state `submitted`. The queue is public:\n\n```sh\ntf \"$TAIFOON/protocols/pending\" | jq -e '.ok and (.counts | has(\"submitted\") and has(\"approved\") and has(\"rejected\"))' >/dev/null\n```\n\nThe same manifest can also be kept as a contribution of kind `manifest` signed by its author\n(`contribute-definitions`), which is what the GRID earning rules read.\n\n## Verify it works\n\n```sh\ntf \"$TAIFOON/protocols\" | jq -e 'type == \"array\" and length > 0' >/dev/null\necho \"onboard-a-protocol: the decoder list, a passing dry run, a named refusal and the queue answered\"\n```\n\n## What it costs\n\nNothing. `\"grade\": true` also grades the manifest and spends one of your grades.\n\n## Next\n\n- Earning by use (`onboard-protocol`: 1 point per counted job on a live protocol): `earn-grid`.\n- Proofs for your protocol's transactions: `prove-a-transaction`.\n"}