Build a Confirmation-Aware Verifier for On-Chain Agent Work
On-chain data can prove that a transaction was included, but inclusion is not the same as finality, identity, or a completed business action. A verifier that reports only “found” turns a useful cryptographic signal into an unreliable operational decision.
This article explains how to build a small confirmation-aware verification step for an agent workflow: define the claim, inspect the transaction, wait for a policy-defined confirmation state, and return a result an operator can safely act on.
Start with the claim, not the transaction
The first design mistake is asking an agent to “verify the blockchain record” without defining what success means. A transaction hash is an input to verification; it is not the claim itself.
Write the claim as a compact object before calling a node or explorer. For example:
``text claim: network: <known network> transaction: <transaction identifier> expected_sender: <address, if relevant> expected_recipient: <address or contract, if relevant> expected_event: <contract event or action> expected_value: <value and units, if relevant> minimum_confirmations: <policy value> ``
Not every claim needs every field. A payment check may care about sender, recipient, amount, and token. A contract-action check may care about the contract address and an event payload. The important property is that the verifier has an explicit comparison target.
This also separates two questions that agents often blur together:
- What does the chain say happened?
- Does that satisfy the workflow’s claim?
The first is observation. The second is a decision against policy. Keeping them separate makes a failure explainable instead of forcing an operator to interpret a paragraph of agent prose.
Treat inclusion and finality as different states
A transaction can move through several useful states. Names vary by network, but the operational distinction is stable:
- Unknown: the identifier cannot be found or the network is not established.
- Located: a transaction or receipt was returned, but the result is not yet sufficient for the workflow.
- Included: the transaction is in a block and its execution result is available.
- Confirmed: the block has enough descendants for the configured policy.
- Accepted: the confirmed transaction matches the claim.
- Rejected: the transaction is confirmed but does not match, or execution failed.
- Recheck required: the observation is temporarily incomplete, contradictory, or vulnerable to a chain reorganization.
Do not collapse these into a Boolean verified. A Boolean loses the most important operational distinction: whether the agent should stop, wait, investigate, or reject.
A practical result can stay small:
``json { "status": "confirmed", "claim_match": true, "network": "<network>", "transaction": "<identifier>", "block": "<height or hash>", "confirmations": "<observed count>", "checked_at": "<timestamp>", "next_action": "continue" } ``
The exact schema is less important than making the next action explicit and preserving the observed values used to reach it.
Make confirmation a policy input
“Wait until it is safe” is not an implementation. Confirmation requirements should be supplied by the workflow, not improvised by the model while it is browsing.
A low-risk informational lookup might accept inclusion. A release of funds, shipment, credential, or access permission may require a stronger threshold. The verifier should receive that threshold as configuration and report both the required and observed values.
The comparison should be deterministic:
``text if receipt_missing: return recheck_required if execution_failed: return rejected if observed_confirmations < minimum_confirmations: return located_or_included, next_action=wait if claim_fields_do_not_match: return rejected return accepted, next_action=continue ``
This sequence prevents a common failure mode: an agent sees a successful receipt and immediately triggers an external effect, even though the workflow required additional confirmation or a specific event.
The threshold itself is not universal. It is a business and network policy that should be documented alongside the action it protects. Changing it should be visible in configuration or review, not hidden in an agent prompt.
Verify the event payload, not just the hash
A transaction hash identifies a transaction. It does not prove that the transaction performed the expected action for the expected parties.
For contract interactions, inspect the receipt and decoded logs when available. Compare the contract address, event name, indexed identities, amounts, token identifiers, and other fields that define the claim. For a native transfer, compare the relevant transaction fields and execution result. For a token transfer, distinguish the token contract from the recipient and verify the amount using the token’s units.
Be conservative about missing data. If the verifier cannot decode an event or cannot establish the network context, it should return recheck_required or rejected according to policy—not fill the gap with a likely interpretation.
This is where typed intermediate results help. Instead of handing raw node output to the language model, normalize it first:
```text observation: receipt_found: true execution_success: true contract_match: true event_match: false confirmation_count: 18 minimum_required: 12
decision: status: rejected next_action: stop reason: expected event was not observed ```
The model may explain that result, but it should not be the component that silently decides whether an event is present.
Design the wait and recheck path
A confirmation-aware verifier is incomplete if it only handles success and failure. Most real calls happen in the middle: the transaction is pending, the indexer is behind, the receipt is incomplete, or the observation needs another check.
Give every nonterminal result a bounded next step:
- record the last observation;
- wait for a defined interval or workflow checkpoint;
- query the same network again;
- compare the new block and receipt data;
- stop after a maximum number of attempts or a deadline;
- escalate with the reason and the latest observation.
A recheck must not mutate the protected external state merely because the first observation was ambiguous. If the verifier protects a payout or permission change, the safe default is to hold that action until a terminal result is reached or a human approves an exception.
Also preserve the network and query context. A transaction lookup on the wrong network can look exactly like a missing transaction. The result should make that possibility visible rather than reporting a generic “not found.”
Put the verifier at the action boundary
The best place for this component is immediately before the consequential action. Earlier checks are useful for progress updates, but they should not be treated as authorization to release value or publish a claim.
At the boundary, require a terminal result whose policy permits the requested action. Log the claim, normalized observation, decision, and next action together. Keep the log useful to an operator without pretending it is an immutable proof: the chain data provides the external evidence; the local record explains how the system used it.
For a first implementation, test four paths explicitly:
- a confirmed transaction that matches every claim field;
- a confirmed transaction with the wrong recipient, amount, or event;
- an included transaction below the confirmation threshold;
- a missing, failed, or undecodable receipt.
Then test the workflow behavior, not only the parser: matching data may continue, incomplete data must wait, and mismatched data must stop. That is the difference between a blockchain lookup and an operational verifier.
Conclusion: make “verified” earn its meaning
On-chain verification becomes dependable when it is treated as a small state machine rather than a search result. Define the claim, separate inclusion from confirmation, compare decoded fields deterministically, and give ambiguous observations a bounded recheck path.
The next practical step is to choose one existing workflow and write its verification policy in plain language: which network, which fields, what confirmation state, what deadline, and which result permits the external action. Implement the verifier against those cases, keep the normalized result inspectable, and require the action boundary to honor it. That modest discipline lets agents use on-chain facts without turning partial evidence into premature commitment.