Hey everyone ![]()
Ran into a problem that feels like a real gap in the EVM, curious if it’s been discussed before.
The core problem
We’re building a trustless AI agent system. Every claim an agent makes — verification results, settlement outcomes, audit records — should be independently verifiable by anyone with access to the data, without interacting with the chain, without chain-specific logic. You get the data. You verify. No RPC, no indexer, no “trust the contract.”
To do that, every on-chain record needs an identifier that a third party can recompute purely from the data they already hold. But here’s the catch: there is currently no way for a contract to generate a unique, reorg-resistant, independently-recomputable identifier purely from within the EVM — no external data, no tx hash, no log index, no off-chain oracle.
Concretely: we need to produce a credential inside the contract and hash it in the same execution — the identifier must be independently recomputable from data alone, without touching a chain endpoint. The difficulty is that the EVM gives us no primitive that is simultaneously unique per execution position, resistant to reorg, and derivable without external references. We cannot use txHash or log index — they are not accessible from within Solidity.
Today, nothing in Solidity gives you all three properties at once:
- Unique per execution position. Two calls at different code positions in the same transaction — different call depths, different storage states — need different identifiers. Not a per-transaction value, not a block-level value. Tied to where the code is executing and what state it observes.
- Reorg-resistant. If the chain reorgs, the same execution position on the new fork must produce a different identifier. A per-contract nonce fails here: it replays identically.
- Independently recomputable. A third party must be able to re-derive the same identifier from on-chain data alone, without access to the tx hash or log index (neither is accessible in Solidity anyway).
Existing primitives each miss at least one:
block.timestamp,block.number— don’t change on reorg at the same heightblock.prevrandao— block-level, same for every tx in the block; can’t disambiguate calls within a block, and identical at the same slot across a reorgBLOCKHASH(block.number)— always returns 0- Per-contract nonce — based on call count, not execution position; replays identically on reorg. Worse: inserting one extra
verify()shifts every subsequent nonce, so previously generated identifiers can no longer be independently recomputed without knowing the exact call order. - Transaction hash / log index — not accessible from within a contract
The gap is real — a system built on portable, independently-recomputable identifiers can’t anchor them properly without this.
What would help
Two levels, which could be solved together or separately:
- Block-level: a value that’s deterministic within a block but changes on reorg — e.g. a
BLOCKIDopcode hashing(chainid, block.number, block.timestamp, block.basefee, block.gaslimit). - Execution-position level: within the same block, two calls at different code positions in the same contract must diverge — achieved by binding the identifier to the contract’s current state at each call site (e.g. folding in a relevant storage slot or the contract’s state root), not to a call counter. The state changes naturally as the transaction runs; no nonce is needed, and a reorg replays the state, producing fresh identifiers.
Whether this belongs in the protocol or at the application layer is what I’d love the community’s take on. Is a BLOCKID-like primitive worth a hard fork, or is the right answer to keep every digest bound to its event and never detach it — accepting that independent recomputation from data alone isn’t always possible today?