BTC $79,406 ETH $2,453 SOL $101.38 STABLECOIN FLOAT $311.1B NATIVE USDC ON BASE $4.25B DEFI TVL $88.2B THE PUBLIC LEDGER OF MACHINE MONEY — AGENTICFINANCEGRAPH.COM

Home/Research/BindingRecord spec

Specification · v0.1 · the join key

BindingRecord: the missing link between an agent and its money.

Every explorer in this sector draws the same arrow — NFT → wallet → money — and none of them specify it. This is our specification: a versioned, evidence-ranked bind, with money stored as typed actions underneath it. Without this object, a ledger of machine money is a status page.

Agentic Finance Graph · Published 2026-09-05 · Layer 1 schema · comments to agenticfinancegraph@proton.me

Direct answer A BindingRecord is the versioned, evidence-ranked join from an agent identity to the key that spends for itagent_gid → wallet → controller, stamped with the method that proved it, a deterministic confidence, and the block range in which it was valid. Every payment, deposit, delegation and incident in the graph hangs off a binding. Ranking uses bindings proved at method 3 or above.

Why an index without the bind is not a ledger

The sector has four public facts about any given agent and no published way to glue them together: an ERC-8004 identity NFT, the controller code behind a wallet, the key that actually signs, and the money observed moving. Explorers show the first. Payment scanners show the last. Nobody publishes the join.

That gap is not cosmetic. Every claim above L7 — “this agent paid”, “this agent is funded”, “this agent has repeat counterparties” — is a claim about a wallet, not about an NFT. If the bind is wrong, the funnel is fiction: the right wallet can be attached to a dead identity, or the wrong wallet to a live one, and every downstream number (vault maps, incident attribution, any paid API) inherits the error silently.

So we are publishing the join before we publish the graph that depends on it. Argue with the schema; that is the point of shipping it as a document.

The object

BindingRecord
  binding_id        ulid
  agent_gid         eip155:{chainId}:{registry}:{agentId}
  wallet            chain-qualified address
  controller        7702_impl | safe_module | eoa | 4337 | unknown
  policy_id         nullable  → Mandate
  bind_method       0..5      (ranked, see below)
  confidence        0..1      (deterministic function of evidence)
  valid_from_block  uint
  valid_to_block    uint | null
  superseded_by     binding_id | null
  evidence[]        {type, uri, tx, body_hash, observed_at}
  cluster_id        operator cluster

Two rules make this a ledger rather than a snapshot. First, a binding is born, dies and can be superseded — never edited in place. Second, every row that spends money references a binding_id, so attribution is replayable at any block height.

bind_method: ranked, never averaged

A confidence score that blends a cryptographic signature with a guess is worse than no score, because it hides which one you have. We rank the evidence and publish the table:

Bind methods and default confidence
RankMethodDefault confidenceForgeability
5Wallet signs a challenge over agent_gid + nonce (on-chain or EIP-712)0.95Hard
4A EIP-7702 authorization or session policy whose signed payload embeds the agent id0.90Hard
3On-chain agentWallet metadata set by the NFT owner, later matched by the observed spender0.70Medium
2URI JSON agentWallet matches an observed spender, URI still live0.45Easy
1Clustering only: shared funder, same implementation, same user agent0.20Very easy
0Name or ticker similarity (“Trading Agent #N”)0.00Spam

Ranking rule: public L7+ rankings use bindings at bind_method ≥ 3 only. Methods 1–2 still draw in the graph, rendered dashed, and researchers can toggle them on. Confidence itself is a published function, not a model:

c = 1 − Π(1 − wᵢ)      # wᵢ = fixed weight of evidence i, from the table above

If we later train a model, it may only propose a binding that a rule still has to accept.

Transfer closes the bind

An ERC-721 transfer moves the identity, not the history. On Transfer we close valid_to_block and require a fresh proof before any new attribution:

on identity.Transfer(from, to, tokenId):
    close  binding.valid_to_block = tx.block
    open   new BindingRecord (unproved) for the new owner
    keep   all prior Actions attached to the closed binding

This is the rule that stops a bought or stolen badge from inheriting somebody else’s volume — the single most common way an agent leaderboard lies.

Action: the atom of machine money

Payments, swaps, deposits and mints are not the same object and must never share a column. One typed table fixes that:

Action
  action_id
  binding_id        nullable (unbound spend is still recorded, never ranked)
  kind              x402_pay | vault_deposit | vault_withdraw | swap
                    | bridge | gas_sponsor | mint | validation | review | incident
  counterparty
  notional_usd      at scrape fx
  asset
  tx_or_receipt
  policy_id_in_force
  economic          bool     # after the vanity filter
  as_of_block
  producer          self | x402scan | keyrock | 8004scan | …

The producer field is not bookkeeping politeness. It is what stops imported research from being mistaken for our own crawl — a distinction the data panels already make visually, and which the machine-readable state API carries as kind: live | cited.

The ladder becomes SQL

Once bindings and actions exist, the liveness ladder stops being a second definition maintained by hand. It is a set of queries over the same two tables, so a tile and the graph cannot disagree:

L5 = last Probe returned 2xx or a well-formed 402
L6 = MCP initialize or A2A agent-card handshake succeeded
L7 = ∃ Action(kind ∈ {x402_pay, swap, vault_deposit}, economic = true)
L8 = balance_usd > 1 at as_of_block
L9 = count(distinct cluster_of(counterparty)) ≥ 3 over 30 days

Note cluster_of() in L9. Counting raw counterparties is Sybil-cheap: three facilitator addresses you control, or three endpoints that pay each other, satisfy a naive rule. Counterparties inside one operator cluster count once.

Mandate: the object that makes an incident definable

“Policy-bounded spend” is a slogan until somebody writes down who authorised what:

Mandate
  mandate_id
  principal      HumanOperator | org DID | unknown
  binding_id
  caps           {per_tx, per_day, assets[], targets[]}
  expires
  kill_switch    address or session expiry
  attestation    EIP-712 | TEE quote | KYC vendor | none

With a Mandate, an incident is a predicate rather than a blog category — action outside mandate — and detectors become queries:

retry_loop    := same payload_hash, ≥8 failures, within 10 min
god_key_spend := L8 wallet, no delegation, transfer to a first-seen target
policy_bypass := action.asset ∉ mandate.assets
bind_hijack   := identity.Transfer, then URI wallet rewrite < 100 blocks

Each fires an Action(kind = incident), so incidents live on the same tape as the money that caused them. See the security checklist for the god-key case in practice.

Cluster: computed, not asserted

  • Common funding parent across the first N inbound transfers.
  • Same delegated implementation plus an overlapping session-key factory.
  • Same agentURI host or TLS leaf certificate.
  • Same Safe owner set.

Clusters are recomputed, versioned and published alongside the dumps. One operator running fifty identities is one economic actor with fifty badges, and the graph should say so.

Two clocks, two graphs

Two clocks: wall-clock time for probes, block height for money. Never mixed in one column — a probe at 14:02 UTC and a payment at block 34,882,109 are different kinds of fact.

Two graphs: the declared graph is what registration files claim (services, self-reported wallets); the observed graph is probes, transactions and code state. We rank only the observed graph and render the declared one as an overlay. That single separation is the difference between an index of intentions and a ledger of behaviour.

The test that proves the bind exists

A stranger, using only the public dumps, must be able to:

  • Take bindings.parquet, actions.parquet and probes.parquet for a given day.
  • Recompute the Base L5 rate and land inside the published interval — 6.8% (95% CI 5.5–8.4%) for the 2026-08-16 crawl.
  • Pick one L9 agent, follow identity → binding (method ≥ 3) → wallet → three distinct counterparty clusters, and find every transaction on a block explorer.
  • See a transfer appear as a bind close and a bind open — never as inherited volume.

If step three fails, we do not have a graph of machine money. We have a liveness blog with DeFi wallpaper. That test is the acceptance criterion for Layer 1, and we will publish the dumps that let anyone run it.

Status and what ships next

ObjectStatusShips as
BindingRecordSpec published, this pagebindings.parquet + as-of API
ActionSpec publishedactions.parquet, typed by kind
ProbeRunning (liveness crawler)Append-only time series
MandateSpec publishedPartner-supplied + public mandates
ClusterSpec publishedRecomputed daily, versioned
State tapeLive/api/state · OpenAPI

Until bindings.parquet exists, we do not sell a query API and we do not publish per-agent rankings. The panels stay what they are: live float and vault reads, plus clearly-labeled cited research. That restraint is the product.

FAQ

What is a BindingRecord?

The versioned, evidence-ranked join from an agent identity to the wallet that spends for it, carrying the method that proved it, a deterministic confidence, and the block range in which it was valid. Every payment, deposit and incident in the graph hangs off a binding.

How do you prove a wallet belongs to an AI agent?

By ranked methods, never by averaging: a wallet signature over the agent id (5), a delegation or session policy embedding the agent id (4), on-chain agentWallet metadata later matched by the spender (3), a URI JSON claim (2), clustering alone (1), name similarity (0 — proves nothing). Rankings use method ≥ 3.

What happens when an agent NFT is transferred?

The binding closes at the transfer block and a new one must be proved. Old receipts stay on the old binding, so a purchased identity cannot inherit someone else’s payment history.

Why does the liveness ladder need the bind?

Because L7 and above are claims about money, and money belongs to wallets, not NFTs. With bindings in place the ladder becomes queries over bindings and actions, so the tile and the graph cannot disagree.

Can I use this schema in my own indexer?

Yes — it is published to be copied, and it is better for the sector if several indexers converge on one join. If you implement it, tell us what breaks; the version number on this page exists so disagreements can be dated.

The Agentic Desk · opening soon

One email. One ping when the desk opens.

A private terminal for operators — labeled wallets, alerts, the graph served over MCP and x402. Until it opens, everything we track stays public, minute by minute.

You’re on the list. We write once, when the desk opens. Until then, the panels are live.

No spam · No token · Reply “stop” to leave