Pre-ERC: Launch Observations, the check an agent reads before it buys a token

An agent about to buy a token should be able to make one read: “what did a detector I trust see about this launch, and at which block?” This Pre-ERC makes that a standard on-chain record. latestFinding(chainId, token, detector, kind, param) returns a typed value and the block it held at, so an agent, a wallet guard or a router can refuse a missing or stale reading inside the same transaction that buys.

It is the observed half of ERC-8462 (Token Launch Declarations). That proposal lets a launchpad declare facts at launch, so that “detectors, wallets and lenders can compare what a launchpad declared with what they observe”. Observations use the same ERC-8376 launch id, or chain id + token when no directory lists the launch.

Sketch

  • One call per token and detector: latestByToken and latestFinding. A missing observation or finding comes back as “not read”, never as a 0 that looks clean. The reader sets how old a reading may be.
  • A small typed core: first-block share, early-buyer share, the creator’s prior launches, share held by wallets linked through funding, a product label, and any ERC-8376 base signal by its field name.
  • Plus a tally (against / careful / clear) and the hash and URI of the full report.
  • Any address can be a detector. Append-only: a newer observation supersedes the old one, and both stay readable.
  • ERC-8004: the detector is an 8004 identity, lists the registry in its registration file, and agents that relied on a reading can rate it in 8004 reputation.

Facts at a block, not verdicts. Unlike ERC-8376 reports there is no score, no abuse pattern and no bond.

One correction since my reply on the 8462 thread: liquidity is now read through ERC-8376’s lpLockedShare / lpLockRemaining signals (which is how an 8463 lock shows up), not a finding of our own.

Status

Draft, reference contract and tests are written. A full observation costs about $0.04 in gas on Robinhood Chain. We run Toll on Robinhood Chain, which reads launches straight from the chain and can be called by agents over MCP and x402. We would be the first detector writing to the registry.

Questions

  1. Is one read per (token, detector) enough for guards, or do they also need “latest by any detector in a list”?
  2. Staleness: should the reader choose the bound (as drafted), or should the registry set one per kind?
  3. ERC-8004: is reputation feedback from agents the right link, or should observations also answer 8004 validation requests?
  4. Is the typed core right: the five kinds above plus the 8376 signals?

@Cybercentry: with 8376 heading to Review, we reuse its signals by field name. Corrections welcome.

Reader-chosen freshness makes sense. Could you share the schema and reference contract? One case I’d test: a reading is still within the age limit, but its observed block has been reorged out. Would the record bind a block hash, or would readers rely on a stated finality policy?

Thanks. The draft and the reference contract are in the PR: Add ERC: Launch Observations by soli0x · Pull Request #2121 · ethereum/ERCs · GitHub (contract and
Foundry tests under assets/).

On reorgs, today it is a stated policy: a detector SHOULD observe only finalized blocks, and if an observed block is reorged out it records a correction at a final block. The old record stays and readers see both.

Your case is a fair hole, though: a guard can’t tell from the record alone that its block is still canonical. Checking on chain doesn’t really work, since blockhash() only reaches back 256 blocks (about 25 seconds on Robinhood Chain at 0.1 s blocks). So I’ll add the observed block hash to the ObservationRecorded event. It costs no storage, and any reader or indexer can compare it with the canonical chain. The finality policy stays the main rule, and the hash makes a broken one visible. On an Arbitrum Orbit chain like Robinhood Chain, “finalized” means the block’s batch is final on the parent chain, so a detector reads at the finalized tag rather than the head.

Facts at a block, with “not read” instead of a clean looking 0, is the right default for a guard. I read the reference contract in the PR and two things stood out.

  1. On Arbitrum chains block.number is the parent chain’s block (Ethereum, for Robinhood Chain) and blockhash returns a pseudo-random value, not the L2 hash. Right now Robinhood Chain’s finalized block is about 99.9M while block.number there reads about 26.3M, so _checkOwnChainBlock reverts with InvalidObservedBlock for an own-chain observation at any recent block, which includes every ERC-8376 keyed one, unless your deployed version already differs from the PR. The guard example in the spec has the same mismatch: once an observation exists, block.number - at underflows and the buy reverts. The Foundry tests run on a plain EVM, so they can’t see it. ArbSys(100).arbBlockNumber() gives the L2 number, and the EIP-2935 history contract is deployed on Robinhood Chain and serves L2 hashes over a longer window than 256 blocks, which may also help with the reorg case ted0x raised. Probably worth a line in the spec on which block number a registry and a guard read on an Arbitrum chain.

  2. setDetectorIdentity checks ownerOf or getAgentWallet once, and every later observation copies that link. Copying at record time makes sense, but if the 8004 agent is transferred or its wallet changes, the old detector address keeps stamping that agentId, and the new holder can link the same agentId from another address. Guards that pin a detector address are fine. Anything that trusts the agentId and its reputation should recheck the link when it reads, or recordObservation could recheck it before copying.

On your first question, I’d add the list read. A guard that trusts three detectors otherwise makes three calls and runs its own staleness check for each.

Thanks, both are real and I checked the first one on chain. Inside the EVM, Robinhood Chain’s block.number reads about 26.29M (the parent chain), while the chain’s own head is about 99.93M. So the own-chain check and the guard example were wrong on every Arbitrum chain, and plain-EVM Foundry tests could not see it.

Fixed in the PR:

  1. The spec now defines the block number as the chain’s own (ArbSys.arbBlockNumber() on Arbitrum chains), and the
    registry and the guard example both use it.
  2. The observed block hash is checked against the EIP-2935 history contract where it is deployed. On Robinhood Chain
    it is, and it returned the exact L2 hash in my test, so a reorged-out block is caught over about 8k blocks, not 256.
    That also closes ted0x’s case.
  3. recordObservation rechecks the 8004 link each time. If the agent was transferred or its wallet changed, the
    observation carries no agentId. Readers that trust an agentId should still recheck when they read.
  4. Added latestFindingFrom(chainId, token, detectors[ ], kind, param), which returns the first existing finding in list
    order.

Tests now mock ArbSys and the 2935 contract.