Hi everyone
,
Wanted to share a gap that has come up repeatedly while working across ERC-8274, ERC-8183, and ERC-8004 β and see if others have run into the same friction.
The on-chain AI agent ecosystem has made real progress across several layers: agent identity (ERC-8004), inference proof verification (ERC-8274), proof anchoring (ERC-8263), agentic commerce (ERC-8183). But there is one foundational primitive still missing β a standard for how a smart contract invokes an AI agent and receives its output.
Today, every dApp that wants to call an AI agent defines its own task format, and every agent must implement a separate adapter per dApp:
dApp A defines its own task format β agent X adapts
dApp B defines a different format β agent X adapts again
dApp C defines yet another format β agent X adapts again
...
agent Y must do the same for A, B, C β NΓM integration complexity
This is the same fragmentation ERC-8274 solved at the verification layer. The task layer has the same problem one level up.
Motivation
The on-chain AI agent stack maps naturally to six base-layer primitives, analogous to blockchainβs foundational properties:
| Primitive | Blockchain Analogue | ERC |
|---|---|---|
| Identity | Address | ERC-8004 |
| Execution | Smart Contract (definition + invocation) | missing |
| Verify | Consensus | ERC-8274 |
| Anchor | On-chain State | ERC-8263 |
| Settlement | Value Transfer | ERC-8275 |
| Prove | Logs / Audit Trail | ERC-8281 + ERC-8299 |
There are also two layers built on top of this base:
βββ Ecosystem Layer βββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββββββββββ β
β β ERC-8183 β β ERC-8273 β β ERC-8257 β β
β β Labor Market β βAuthorization β β Skill Registry β β
β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ
β all depend on
βββ Base Layer ββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ
β β
β Identity β Execution β Verify β Anchor β Settlement β Prove β
β ERC-8004 [This ERC] ERC-8274 ERC-8263 ERC-8275 8281+8299 β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Execution is the last missing brick in the base layer. Without it, every ecosystem ERC is forced to define its own task format rather than composing on a shared primitive β ERC-8183 uses a free-form description string, ERC-8001 uses opaque executionData bytes, ERC-8004 delegates entirely to off-chain A2A/MCP.
Proposal
The proposal defines three minimal components. The design explicitly excludes agent routing, task state management, labor markets, and semantic task definitions β this is a protocol layer, not an application layer.
1. AgentTask β Task Definition
struct AgentTask {
bytes32 taskId; // unique identifier
bytes32 systemPromptHash; // keccak256(systemPrompt)
bytes systemPrompt; // optional: full text (omit to save gas)
bytes32 modelId; // target model/agent type (abstract bytes32)
address handler; // contract implementing IAgentHandler
address verifier; // optional: IAgentVerifiable (ERC-8274); address(0) = no proof required
uint256 deadline; // expiry timestamp
}
systemPromptHash commits to the static task context. The dynamic user prompt is supplied at call time, so the same AgentTask definition can be reused across multiple invocations.
2. IAgentCaller β Task Dispatch
interface IAgentCaller {
event CallAgent(
bytes32 indexed taskId,
address indexed requester,
bytes32 systemPromptHash,
bytes systemPrompt, // optional
bytes32 userPromptHash,
bytes userPrompt, // optional
bytes32 inputHash, // keccak256(systemPromptHash β userPromptHash), computed on-chain
bytes32 modelId,
address handler,
address verifier, // IAgentVerifiable; address(0) = no proof required
uint256 deadline
);
function callAgent(
AgentTask calldata task,
bytes32 userPromptHash,
bytes calldata userPrompt
) external returns (bytes32 taskId);
}
Off-chain agents subscribe to CallAgent events. inputHash is computed on-chain as keccak256(systemPromptHash, userPromptHash) β this is the same field ERC-8274 uses, providing a tamper-proof link between task invocation and proof verification.
3. IAgentHandler β Reply & Proof Callbacks
A single callAgent may trigger multiple onAgentReply calls β supporting streaming output, multi-step reasoning, and multi-agent fanout. The nonce field (0-indexed, monotonically increasing) distinguishes each invocation; isFinal signals the last reply.
onAgentProve is deliberately decoupled from onAgentReply: result submission and proof submission happen independently, so an optimistic handler can act on the reply immediately while proof follows asynchronously.
interface IAgentHandler {
// Called by agent β may be invoked multiple times per task
function onAgentReply(
bytes32 taskId,
bytes32 inputHash,
bytes32 outputHash, // keccak256(output)
bytes calldata output, // optional: raw output
uint256 agentId, // ERC-8004 agent identifier
address agent,
uint256 nonce, // starts at 0, increments per reply
bool isFinal // true = last reply for this task
) external;
// Called by proof provider (agent or third party)
function onAgentProve(
bytes32 taskId,
bytes32 inputHash,
bytes32 outputHash, // must match onAgentReply(nonce=N).outputHash
bytes calldata proof, // raw proof bytes
address verifier, // IAgentVerifiable address (ERC-8274)
uint256 nonce // which onAgentReply this proof covers
) external;
}
The handler resolves the ERC-8274 verifier chain internally: IAgentVerifiable(verifier).getAgentVerifier() β IAgentVerifier.verify(proof, inputHash, outputHash).
Open Questions
-
taskIdgeneration: should it be caller-supplied (enabling off-chain coordination before on-chain registration) or contract-generated (keccak256(requester, nonce), preventing collisions)? Both have valid use cases. -
modelIdsemantics: should it remain an opaquebytes32resolved entirely off-chain, or is a lightweight on-chain model registry (mappingmodelIdβ proof system, version, capabilities) worth the added complexity? -
onAgentProveplacement: should it live inIAgentHandler(current design, unified interface) or a separateIAgentProveHandlerinterface, so handlers that only care about results stay simpler?
All feedback is very welcome. A v0.1 draft is in progress. The open questions above are the areas where community perspective would be most valuable before the spec solidifies. Happy to discuss any aspect of the design here.