Status: a checklist, not a decision. Prepared 13 September 2026. It executes the token record (decided by the product owner, 12 September 2026, Robinhood Chain included). It follows the deployment plan and the operator policy, both still proposed. The treasury creation and signer record are complete. It establishes no approved public CompanySeries deployment, no configured CURB/CreditDesk deployment and no independent review.
Nodes.
- —The tools live in
contracts/scripts/and run fromcontracts/. - —They read Robinhood Chain (chain id 4663, the
robinhood-mainnetprofile) throughCURB_RPC_URLif set in the shell. Otherwise they read the public nodehttps://rpc.mainnet.chain.robinhood.com. - —The deployment tool prints the node's host on its first line. A keyed URL is never printed or written into a record.
- —The tools read state only, except row 12's
priceSource.fromBlockcheck, which reads the pool's logs before that block. On the public node that is one query (any width under 10,000 matches). On a width-capped endpoint it reads at most sixty-four pages and says how far it read.
Measured 13 September 2026:
- —dRPC with a key (
lb.drpc.live): full archive state;eth_getLogsup to 100,000 blocks, with no cap on results. The site reads everything through it first (CURB_RPC_URLandCURB_RPC_ROUTE=allon Railway, set 21 September 2026), with the public node as the fallback. Unset,CURB_RPC_ROUTEsends head reads to the public node first to spare the balance. - —dRPC without a key: about 200 blocks a query. Alchemy's unpaid tier: ten. Enough for the tools, not the tick.
- —The site's readers halve a query refused for width, for matching too much, or for taking too long.
Who
- —The product owner decides the signers, the launch and the pool. Decisions are recorded by name and date; the site never upgrades one itself.
- —The signers: three people with their own Robinhood Chain wallets. They own the operator's Safe, 2-of-3 as the policy proposes: the treasury every top-up goes to.
- —The reviewer: a named person, not the record's author. They check the tool's output against the chain and sign with name and date. Without that name the deployment tool refuses.
- —The operator holds the tools' shell, the deployer key and the Railway app service's variables. The key is ETH-funded on Robinhood Chain for gas, and never in this repository or a chat.
One person may hold several roles; the records say which.
Where it stands
Done: everything under what exists today in the token record, plus the Safe tools and the local-chain rehearsal in CI. Two adversarial site-code reviews closed their confirmed findings (commits 54ab36e, fc3df1d). A deployed desk is verified against contracts/evidence/CreditDesk.build.json, from source commit 06623ed.
Done 13 September 2026: rows 1 and 2 (contracts/evidence/safes/safe.4663.json), owners and threshold read back through the operator's endpoint and the public node. The deployer wallet exists and is funded.
Not done: rows 3 onward. Until then the site shows NOT_CONFIGURED on /services and /api/credits, and not done in the order of work.
The public stage line (13 September 2026). The product owner asked for it the day the treasury went live. The front page's kicker reads Stage · mainnet · Robinhood Chain. The line beneath is derived from the record by lib/launch/status.ts: the Safe's evidence file, the CURB_CREDITS line, the last code and rate reads, credited top-ups. It states what is on chain (the treasury) and what is not (the token, the desk, the position series). It advances as rows are done:
- —a valid treasury creation record;
- —a configured desk;
- —matching code for the configured token and treasury;
- —a rate read;
- —a credited top-up tied to the current chain and desk index.
Code and rate reads older than thirty minutes do not advance it: a display rule, not an uptime promise. Missing evidence means unconfirmed, not proof that nothing exists on chain. The footer (BRAND.stage) and the series' stage line say the same. None of it makes the eight refused claims or says anything is sold.
The rows
Order. Before row 3, finish what TOKEN and the mainnet dossier require: the independent-review scope and its actual funding, launchpad/terms/authority review, and the approved proceeds formula. Rows 1–2 are completed treasury actions; do not create another Safe to repeat them. Run rows 3–10 after their decisions, then 12 → 13 → 11 → 14 → 15. A supported, configured, freshly read pool precedes the first public-chain payment test. Unpriced-payment recovery belongs in an isolated rehearsal. Row numbers are stable references, not permission to skip these dependencies.
| № | Who | What | Needs first | Command or action | Refused when | Done shows as |
|---|---|---|---|---|---|---|
| 1 | Product owner, signers | Name the Safe's owners (at least three) and threshold (2, per the policy) | Three or more people with own Robinhood Chain wallets, none a signer for any issuer or custodian of a series' components; addresses in the operator policy by name and date, published (register A4) | A decision, not a command | plan-safe.ts refuses fewer than three owners, an owner listed twice, an address that is not 20 bytes of hex or fails its EIP-55 checksum, a threshold under 2 or over the count | Policy names them; A4 closes. Done 13 September 2026: thecurb 1 0xC33DAfAC1c65a92e956519Aa73E29a0b8c39214B, thecurb 2 0x5657dF9f072C536FcdE00BE2eF86c2240E4f8a53, thecurb 3 0x5cc762A11b05F9cCdaE3839C56F5Beb14C40F15D |
| 2 | Operator | Create the Safe | Row 1; a funded wallet | node scripts/plan-safe.ts <owner> <owner> <owner> --threshold 2 simulates on the node, then prints the to and data of createProxyWithNonce on Safe's factory and the predicted address. Any funded wallet can send them. Or add --send --reviewed, with DEPLOYER_PRIVATE_KEY in the shell, to send, await the receipt, read owners and threshold back, compare them with what was asked and write evidence/safes/safe.4663.json. Check the explorer too (getOwners(), getThreshold()) | The node does not answer, or answers another chain id; Safe's factory, singleton or fallback handler has no code; a contract already sits at the predicted address (that --nonce was used with these owners — choose another); the simulation disagrees with the prediction; with --send: no --reviewed off the local chain, no key in the shell, a sender with no ETH, an evidence file from an earlier creation, a receipt that did not succeed, a Safe that answers other owners or another threshold than asked | Done 13 September 2026: 0x4E69723F9Ba9fA2C9842d77b240b2C0F601ac219, block 61,691,826; three owners, threshold 2, read back as asked. Acts through safe-tx.ts --safe … --to … --data …: owners send approveHash; with --approved-by <owner>,<owner> after two approvals, anyone sends execTransaction (details) |
| 3 | Product owner, operator | Launch the token on PONS v2 (decided 17 September 2026) | Venue terms pinned: node scripts/pons-preflight.ts --launcher <the sender> writes evidence/pons/preflight.4663.<block>.json (launchFeeWei, config 0, expectedEconomics digest). Pair: native ETH, decided by the product owner 17 September 2026 (config 0; pairToken the zero address; ETH / USD feed quote). The product owner's three open decisions (TOKEN): counsel on Terms of Use §11 and §3; who sends; a venue unaudited by its own statement. Sender funded with fee and gas. Launch record reviewed by a person | node scripts/pons-launch.ts records/pons-launch.4663.json checks the record against the token record: no creator tax, no buyback, the fee recipient the operator's Safe per evidence/safes/safe.4663.json (owners and threshold re-read), a pinned fee, a pinned non-zero digest. It re-reads the venue in one block (launching open for the sender, fee and digest unchanged, hook and PoolManager as recorded, pair approved, sender's balance) and simulates the launch. Nothing is sent or written. --calldata --reviewed journals the intent (the Safe as sender, predicted token and curve) in evidence/pons/launch.4663.json and prints bytes and value for the Safe to send via safe-tx.ts; --receipt <hash> then reads the launch back and completes the evidence. --send --reviewed with DEPLOYER_PRIVATE_KEY in the shell journals the intent (sender, nonce, prediction) before broadcasting, sends exactly the pinned fee, records the hash before awaiting the receipt, and compares the token the factory records field by field with what was asked | A record naming the example; an empty name, symbol or description; a fee recipient other than the recorded Safe, or a Safe whose owners or threshold moved; a creator tax, a buyback, a zero digest; a reviewer not named; off the local chain, no --reviewed, or uncommitted launch tooling; a venue term that moved since the preflight (the fee or the digest); canLaunch(sender) false; a sender holding less than the fee; an existing journal — completed, or an intent the chain does or does not yet show (never a second send over it); a receipt without exactly one TokenLaunched from the factory; a token the factory records differently from what was asked (never reported "as asked" unless every field is) | evidence/pons/launch.4663.json: plan, transaction, token as the factory recorded it; then row 4 |
| 4 | Operator | Read the token into the record | Rows 2–3. During the curve, node scripts/pons-status.ts <token> gives phase and quote collected against the graduation threshold; the desk reads no curve and quotes nothing until the pool exists | node scripts/record-token.ts <token> --treasury <safe> reads name, symbol, decimals, supply, code hash, the EIP-1967 proxy slots, owner() and paused() at a stated block. It writes them (tokenAsRead) into records/credit-desk.4663.json, with rpcUrl the public node, priceSource null, reviewedBy empty. Without --treasury the treasury is the zero address, which the deployment tool refuses | The node does not answer, or answers another chain id; the token has no code; it does not answer symbol(), decimals() and totalSupply(); a function the node would not answer is a refusal, never a fact written down | Record file; a note if the token is behind a proxy (its admin can change its code; the desk does not control it) |
| 5 | Reviewer | Review the actual record | Row 4; the independently reviewed contract/build | Check tokenAsRead and source/authority facts against the chain. Verify the current treasury Safe identity, owners, quorum and controls against row 2's record. Attach review evidence; only then fill reviewedBy and reviewedAt | Blank review fields are refused. A typed name alone cannot prove review; nonempty treasury code alone cannot prove a Safe | Reviewed record linked to source/build and report; alone, it does not close A2's independent contract review |
| 6 | Operator | Dry run | Row 5 | node scripts/deploy-credit-desk.ts records/credit-desk.4663.json --dry-run --reviewed (off a local chain, --reviewed is needed even for the dry run, on top of the record's review) | In the tool's order: a misspelt flag (--dryrun), a --flag=value, an unknown flag — refused before the record is opened, never a real deployment; a record that cannot be read as JSON; no reviewer (row 5); a network that is not a profile the site knows, or a chain id that is not that profile's; a token or treasury address missing or zero, or the two the same; decimals outside 0–36; a malformed priceSource; on any chain but 31337, no --reviewed; then the node: it does not answer, or answers another chain id; the token has no code, does not answer symbol(), decimals() and totalSupply(), or answers other decimals than the record; the treasury has no code (the treasury is the multisig, not a key); the pool, if recorded, fails row 12's checks; the build record is missing, or the compiled contract is not the committed build record (rebuild with npm run build; npm run check:build says whether the record is this source's) | dry run: every check passed; nothing was sent, with the constructor arguments (the token, the treasury) printed |
| 7 | Operator | Deploy | Row 6; DEPLOYER_PRIVATE_KEY in the shell: ETH-funded, operator-held, never in this repository or a chat | node scripts/deploy-credit-desk.ts records/credit-desk.4663.json --reviewed writes evidence/deployments/credit-desk.4663.json with the transaction hash and a pending note before awaiting the receipt (a cut-off leaves a note, not a second deployment). It then rewrites the file with address, block, transaction and constructor arguments, and prints the CURB_CREDITS line on stdout | Everything row 6 refuses; evidence/deployments/credit-desk.4663.json already exists (a desk was deployed from this record before — read it first); DEPLOYER_PRIVATE_KEY unset or not 0x plus 64 hex digits, or holding no ETH on the chain (checked last, after every read, before anything is sent); the transaction did not succeed — the file then still holds the pending note with the transaction hash, and the next run is refused until a person has read it and moved it aside | Evidence file, committed; CURB_CREDITS line for row 8 |
| 8 | Operator | Configure the site | Row 7; store schema current (npm run db:migrate with CURB_POSTGRES_URL, idempotent) | Set CURB_CREDITS on the Railway app service to the printed line (a bare JSON object; priceSource null until row 13). A Railway variable change redeploys the service and is live when that deployment is. Set CURB_TICK_SECRET on the deployment (without it production /api/tick refuses requests with 503; local development may omit it), and the same value, with CURB_TICK_URL, in the repository's secrets for the scheduler. CURB_RPC_URL on the deployment is the keyed dRPC endpoint (lb.drpc.live, confirmed on Railway 21 September 2026) and CURB_RPC_ROUTE=all: every read goes to it first and falls back to the public node (see the header) | /api/credits answers CONFIG_INVALID with the reason: not JSON, not an object, an unknown network, a malformed token, desk or treasury address, the token and the desk sharing an address, the treasury equal to the token or the desk, a fromBlock that is not a non-negative integer, a priceSource that is not an object or null. A second desk in the same line (a redeployment) restarts the index from its own fromBlock; the earlier desk's credits stand under its address | /api/credits: CONFIGURED, with desk, token, treasury; /services: configured; code not yet verified until the next tick |
| 9 | The tick | Verify the code | Row 8; the scheduler (GitHub Actions, every five minutes, CURB_TICK_SECRET) | Nothing to do: the next tick compares the desk's code outside the two immutable slots with contracts/evidence/CreditDesk.build.json, and the slots with the record's token and treasury | A MISMATCH is a DARK condition on /api/state and, one tick later, on the webhook (a tick delivers alerts before it runs the desk); a MISMATCH, a code read the node did not answer (UNREAD) or a missing build record (NO_BUILD) all hold the index — nothing is indexed or credited from the desk — and /services composes no top-up bytes and says do not send a top-up on the strength of this page | MATCHES the build at commit 06623ed…; the immutables hold the token and the treasury above on /services; step 3 of the order of work done |
| 10 | Operator | Verify the source on the explorer | Row 7 | Verify contracts/src/CreditDesk.sol at commit 06623ed (the build record's sourceCommit; unchanged since) on https://robinhoodchain.blockscout.com: solc 0.8.30, EVM version prague, viaIR, optimizer on with 200 runs, bytecodeHash: none (the settings in contracts/hardhat.config.ts), and the printed constructor arguments. A third place to look, for a reader who trusts neither this site nor the repository | — | Verified source at the desk's address |
| 11 | Operator | First small payment and service proof | Rows 9, 10, 12, 13; current code/config/rate readiness; separate transaction authorization and test budget | Create a desk key and keep it private. Pay from an operator-owned funded wallet other than the treasury. On /services, review the exact amount, quote age/buffer limits, approval and top-up bytes. After the authorized transaction, reconcile receipt, treasury balance delta, index confirmations and credited dollars. Complete a paid HTTP call and a successful webhook with that key | DeltaWrong, transfer/allowance failures, absent or stale readiness hold the experiment. A missing index row is investigated, not treated as a zero balance. One transfer proves its observed path/amount, not every future token behaviour | Transaction and confirmed credited row on the current chain/desk; HTTP/debit/webhook evidence. Any UNKNOWN settlement recorded, reconciled without blind retry |
| 12 | Product owner, operator | The pool | A pool the reader knows on Robinhood Chain: constant-product pair (uniswap-v2-pair: getReserves(), Sync); concentrated-liquidity pool (uniswap-v3-pool: slot0(), liquidity(), Swap); or PONS v2's graduation, a Uniswap v4 pool (uniswap-v4-pool: no address; PoolManager 0x8366a39cc670b4001a1121b8f6a443a643e40951; StateView 0xf3334192d15450cdd385c8b70e03f9a6bd9e673b; key from the pool's Initialize: native ETH or the pair token as currency0, CURB the other, fee 0, tick spacing 200, hook 0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044). Creation block from the chain: the factory's PairCreated / PoolCreated event, the pool's first explorer transaction, or for v4 the PoolManager's Initialize, beside the venue's PoolGraduated in the same transaction | Write priceSource into the record. For the decided pool (CURB / native ETH on Uniswap v4, the venue's graduation; 17 September 2026) it is what node scripts/pons-pool.ts <token> prints after reading the pool three ways: { "kind": "uniswap-v4-pool", "poolManager": "0x8366a39cc670b4001a1121b8f6a443a643e40951", "stateView": "0xf3334192d15450cdd385c8b70e03f9a6bd9e673b", "key": { "currency0": "0x0000000000000000000000000000000000000000", "currency1": "<CURB>", "fee": 0, "tickSpacing": 200, "hooks": "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044" }, "poolId": "<derived>", "fromBlock": <the Initialize block>, "quote": { "kind": "chainlink-feed", "feed": "0x78F3556b67E17Df817D51Ef5a990cDaF09E8d3A9" } } (ETH / USD; a native-ETH quote is never taken as dollars). The reader also knows a constant-product pair (uniswap-v2-pair, "quote": { "kind": "usd-stable" } for a dollar pair such as USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168) and a v3 pool, should the record change. The reviewer signs again. Then node scripts/deploy-credit-desk.ts records/credit-desk.4663.json --dry-run --reviewed checks the pool, sending nothing; it notes the evidence file exists and a real run would be refused, which is the point. It checks fromBlock too, refusing any pool log before it (paged on a width-capped endpoint, saying how far). The site repeats the check once per pool on its first tick with the pool, and holds the index while it disagrees. Two checks are the reviewer's alone: that kind is the pool's real interface (a v2 pair and a v3 pool both answer token0(); a wrong kind surfaces only at the tick, where a pool whose price moved without a Swap the reader recognises is refused as UNREAD, never priced from its first price); and that a usd-stable quote's other side really is USDG (the reader takes whatever it is, at its own decimals, as dollars) | The pool has no code; it does not answer token0() and token1(); it holds neither side as the token; a feed does not answer decimals() and latestRoundData(); fromBlock is not a non-negative integer, is past the head, or is later than a log the pool emitted | The dry run passes with pool: … holds the token against … |
| 13 | Operator | Configure the pool | Row 12 | Set CURB_CREDITS with the reviewed priceSource and deploy it. Get a fresh successful tick; rate, config and code identities must agree before row 11 | Invalid configuration, absent/currently unread or stale code/rate, wrong identity, insufficient price history or liquidity holds payment readiness | Dated quote, source and buffer limits on /api/credits and /services; a rate alone is not a successful payment. Older top-ups reconciled by their recorded pricing basis |
| 14 | Product owner | Decide public invitations and paid beta | Row 11's end-to-end result; current readiness; reviewed terms; assigned operations; budget/capacity; recorded scoped decisions | Review the mainnet dossier and readiness evidence; when authorized, publish the actual service scope and limits. Milestones come from observations; a token or paid beta does not pass position gates | Missing required evidence or owner signoff remains HELD | Published invitation/beta decision tied to release and evidence; service milestones shown apart from the position prototype |
| 15 | Operator | Operate and verify recovery | Assigned primary/backup responders; monitoring; backup schedule; actual operating evidence | The tick indexes, verifies, prices and delivers on its schedule. Monitor missed runs outside GitHub's failure domain, durable logs, pending payments and delivery backlog. Follow RUNBOOK. Take an off-provider backup on the agreed routine and before migration; prove isolated restore. Record incidents and actual response/recovery times | Missing or unread monitoring is not a healthy status. CreditDesk has no pause: disabling invitations does not stop direct transfers | Dated health/backup/restore/incident evidence; measured beta outcomes; known gaps visible |
What is refused by design, and stays refused
- —A record nobody reviewed by name; a public chain without
--reviewed; a key from anywhere but the operator's shell; a second deployment over an existing evidence file, which only a person moves aside (rows 5–7). - —An address copied from a documentation example. Every address comes from the chain via a tool, or the explorer via the named reviewer.
- —Any edit to
contracts/src/CreditDesk.solor the compiler settings after deployment. CI refuses it until the build record is re-recorded (check:build: the record is not this source's bytecode). Re-recording turns a correct desk into a MISMATCH on/servicesand/api/state. A change is a new desk, record and deployment. - —A typed-in price (the conversion). Until row 13 nothing is quoted; a top-up meanwhile is indexed and waits.
- —A promise the code does not keep: the eight claims refused for the position are refused for the token (the token record). The services page says what the desk does, not what a launch might.
What this checklist does not do
It decides nothing. The signers (A4), the reviewer (A2), the launch and the pool are people's decisions, recorded when made. It does not launch the token or choose where. It does not cover the position product's own deployment (rows 1–6 of the deployment plan), which waits on its own gate decision. It does not hold, make or ask for a key.