September 23, 2026 · AI, Blockchain & Software Architecture
AI agents are beginning to behave less like chat interfaces and more like economic actors. They can discover an API, decide that its data is useful, pay for one request and continue their work without a human opening an account or entering a credit card.
That shift makes agentic commerce today’s most relevant AI-and-blockchain topic for software architects. Two open standards provide much of the emerging foundation: ERC-8004 for agent identity, reputation and validation, and x402 for machine-readable payments over HTTP.
The important engineering question is not whether agents can hold wallets. They can. It is whether another system should trust the identity, output and reputation attached to those wallets.
AI-generated conceptual illustration. The policy boundary matters as much as the model or ledger.
Why this is the top topic for this site today
This is an editorial judgment for Homann Software’s audience—software architecture, Java engineering, AI and blockchain—not a claim about a universal search ranking. The signal is nevertheless strong.
The official Ethereum AI agents guide now treats agent-controlled wallets, x402 payments and ERC-8004 registries as one emerging stack. The x402 project reports active machine-to-machine payment volume and exposes a neutral, open protocol rather than a single vendor checkout. Its public repository was updated as recently as September 21, and now includes TypeScript, Python, Go and Java implementations. x402 source repository
ERC-8004 is already deployed across multiple EVM networks. Its identity registry gives an agent a portable onchain identifier, while its reputation and validation registries provide common places for feedback and independent checks. ERC-8004 specification
Adoption, however, is running ahead of assurance. A 2026 cross-chain empirical study found that only 3% to 15% of registered identities in its Ethereum, BSC and Base dataset exposed a valid registration file with at least one live service endpoint. It also found extensive coordinated reviewer behaviour and cheap reputation manipulation. The lesson is not that open agent registries are useless. It is that registration is not verification, and reputation is not evidence. Can Trustless Agents Be Trusted?
That tension—rapidly usable payment rails paired with immature trust signals—is exactly where good architecture is needed.
What ERC-8004 and x402 actually solve
ERC-8004 and x402 are complementary, but they solve different problems.
ERC-8004 answers “who claims to offer this service?” Its ERC-721-based identity registry associates an agent ID with a registration document. That document can advertise capabilities and endpoints such as HTTPS, MCP or A2A. The reputation registry accepts feedback, and the validation registry coordinates requests for checks such as trusted execution, zero-knowledge machine learning or staked re-execution.
x402 answers “how can software pay for this resource?” A client requests an HTTP resource. The server can answer with 402 Payment Required and structured payment terms. The client signs an authorization and retries. A facilitator verifies and settles the payment, after which the server returns the resource. Version 2 separates the protocol from transports and payment schemes and supports extension points, multiple networks and dynamic routing. x402 v2 specification
Together, these standards let an agent discover a service, evaluate advertised signals, pay and receive a result. They do not prove that the result is correct, that the model followed a particular prompt or that a reputation score represents honest transactions.
The missing layer: evidence-bound interactions
A useful trust record should be bound to a real interaction. At minimum, it should connect:
- the agent identity and software version;
- the exact request, represented by a canonical hash;
- the exact response or artifact, also represented by a hash;
- the payment or settlement reference;
- the applicable policy decision and approval context;
- the validator, test or human acceptance signal; and
- a timestamp and unique receipt identifier.
Do not put prompts, invoices, personal data or model output directly on a public chain. Store the business data in an access-controlled evidence store and anchor only carefully designed digests and public metadata. A hash is still linkable data if an attacker can guess the original value, so sensitive fields need salting, keyed commitments or a private ledger depending on the threat model.
This receipt pattern does not make the AI “truthful.” It makes later substitution detectable. A validator can prove that it assessed the same artifact that was paid for, and a reputation system can reject feedback that is not tied to a unique paid interaction.
Hands-on: anchor a paid AI result on a local EVM
The following example is deliberately small and vendor-neutral. A deterministic stand-in for an AI model assesses invoice risk. The application canonicalizes and hashes the request and response. A Solidity contract records those hashes together with an agent identifier, payer, amount and timestamp.
The deterministic model makes the test reproducible. In production, replace it with a model adapter that returns the same validated structure. Never give a language model direct control of the signing key; the application policy layer decides whether a proposed purchase or commitment is allowed.
1. The receipt contract
The contract rejects unpaid and duplicate receipts. It emits an event instead of storing every field in contract storage, keeping the example simple and cheaper to index.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @notice Minimal evidence registry for paid AI-agent interactions.
/// @dev This is an educational receipt pattern, not an x402 implementation.
contract InteractionReceiptRegistry {
error ReceiptAlreadyCommitted(bytes32 receiptId);
error PaymentRequired();
event InteractionCommitted(
bytes32 indexed receiptId,
bytes32 indexed agentId,
address indexed payer,
bytes32 requestHash,
bytes32 responseHash,
uint256 amount,
uint64 committedAt
);
mapping(bytes32 receiptId => bool committed) public receipts;
function commit(
bytes32 receiptId,
bytes32 agentId,
bytes32 requestHash,
bytes32 responseHash
) external payable {
if (receipts[receiptId]) revert ReceiptAlreadyCommitted(receiptId);
if (msg.value == 0) revert PaymentRequired();
receipts[receiptId] = true;
emit InteractionCommitted(
receiptId,
agentId,
msg.sender,
requestHash,
responseHash,
msg.value,
uint64(block.timestamp)
);
}
}
This uses native value only to make the local example self-contained. A production x402 integration normally settles according to the negotiated scheme and network—often with a stablecoin—and should bind the protocol’s settlement response to the evidence record. The contract above is not an x402 implementation.
2. Canonicalize and hash the evidence
Ordinary JSON objects do not have a reliable semantic byte representation across all producers. Hashing first requires a canonical serialization. This compact implementation sorts object keys recursively, then creates SHA-256 digests.
import { createHash } from "node:crypto";
export function canonicalJson(value) {
if (value === null || typeof value !== "object") {
return JSON.stringify(value);
}
if (Array.isArray(value)) {
return `[${value.map(canonicalJson).join(",")}]`;
}
const entries = Object.entries(value).sort(([left], [right]) =>
left.localeCompare(right),
);
return `{${entries
.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`)
.join(",")}}`;
}
export function sha256(value) {
return `0x${createHash("sha256").update(canonicalJson(value)).digest("hex")}`;
}
export function buildReceipt({ agentId, request, response }) {
const requestHash = sha256(request);
const responseHash = sha256(response);
const receiptId = sha256({ agentId, requestHash, responseHash });
return { receiptId, agentId: sha256(agentId), requestHash, responseHash };
}
export function verifyReceipt(receipt, request, response) {
return (
receipt.requestHash === sha256(request) &&
receipt.responseHash === sha256(response)
);
}
For cross-language production systems, prefer a published canonicalization standard such as RFC 8785 rather than maintaining an informal serializer.
3. Keep the model behind a validated interface
The model output is accepted only if it matches the application contract. Validation is outside the model, just like authorization and spend limits should be.
export class InvoiceRiskAgent {
constructor(model) {
this.model = model;
}
async assess(invoice) {
const result = await this.model.classify(invoice);
if (
!["APPROVE", "REVIEW"].includes(result.decision) ||
!Number.isFinite(result.confidence) ||
result.confidence < 0 ||
result.confidence > 1 ||
typeof result.reason !== "string" ||
result.reason.length === 0
) {
throw new Error("Model returned an invalid assessment");
}
return Object.freeze({
decision: result.decision,
confidence: result.confidence,
reason: result.reason,
});
}
}
export class DeterministicModel {
async classify(invoice) {
const suspicious = invoice.amountCents > 100_000 || !invoice.purchaseOrder;
return suspicious
? {
decision: "REVIEW",
confidence: 0.94,
reason: "High value or missing purchase order",
}
: {
decision: "APPROVE",
confidence: 0.91,
reason: "Amount and purchase order satisfy the policy",
};
}
}
4. Test the complete interaction
The integration test compiles the Solidity contract, starts an in-process EVM, deploys the registry, runs the agent, sends a paid transaction and reads the emitted event. It also verifies canonical hashing, tamper detection, replay rejection and the zero-payment guard.
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { describe, it } from "node:test";
import ganache from "ganache";
import solc from "solc";
import { BrowserProvider, ContractFactory, parseEther } from "ethers";
import { DeterministicModel, InvoiceRiskAgent } from "../src/agent.js";
import { buildReceipt, canonicalJson, sha256, verifyReceipt } from "../src/receipt.js";
async function compileContract() {
const source = await readFile(
new URL("../contracts/InteractionReceiptRegistry.sol", import.meta.url),
"utf8",
);
const input = {
language: "Solidity",
sources: { "InteractionReceiptRegistry.sol": { content: source } },
settings: {
outputSelection: { "*": { "*": ["abi", "evm.bytecode.object"] } },
},
};
const output = JSON.parse(solc.compile(JSON.stringify(input)));
const errors = (output.errors ?? []).filter((item) => item.severity === "error");
assert.deepEqual(errors, []);
return output.contracts["InteractionReceiptRegistry.sol"]
.InteractionReceiptRegistry;
}
async function fixture() {
const compiled = await compileContract();
const eip1193 = ganache.provider({ logging: { quiet: true } });
const provider = new BrowserProvider(eip1193);
const signer = await provider.getSigner();
const factory = new ContractFactory(
compiled.abi,
`0x${compiled.evm.bytecode.object}`,
signer,
);
const contract = await factory.deploy();
await contract.waitForDeployment();
return { contract, eip1193 };
}
describe("verifiable AI interaction receipt", () => {
it("canonicalizes objects independently of key insertion order", () => {
assert.equal(canonicalJson({ b: 2, a: 1 }), canonicalJson({ a: 1, b: 2 }));
assert.equal(sha256({ b: 2, a: 1 }), sha256({ a: 1, b: 2 }));
});
it("anchors a paid AI result on a local EVM", async () => {
const { contract, eip1193 } = await fixture();
try {
const request = {
invoiceId: "INV-2026-091",
amountCents: 249_900,
purchaseOrder: null,
};
const response = await new InvoiceRiskAgent(
new DeterministicModel(),
).assess(request);
const receipt = buildReceipt({
agentId: "did:example:invoice-risk-agent-v1",
request,
response,
});
const transaction = await contract.commit(
receipt.receiptId,
receipt.agentId,
receipt.requestHash,
receipt.responseHash,
{ value: parseEther("0.001") },
);
const mined = await transaction.wait();
const event = mined.logs
.map((log) => contract.interface.parseLog(log))
.find((log) => log?.name === "InteractionCommitted");
assert.equal(response.decision, "REVIEW");
assert.equal(event.args.receiptId, receipt.receiptId);
assert.equal(event.args.requestHash, receipt.requestHash);
assert.equal(event.args.responseHash, receipt.responseHash);
assert.equal(event.args.amount, parseEther("0.001"));
assert.equal(await contract.receipts(receipt.receiptId), true);
assert.equal(verifyReceipt(receipt, request, response), true);
} finally {
await eip1193.disconnect();
}
});
it("detects a modified model response", async () => {
const request = { invoiceId: "INV-1", amountCents: 10_000, purchaseOrder: "PO-1" };
const response = await new InvoiceRiskAgent(new DeterministicModel()).assess(request);
const receipt = buildReceipt({ agentId: "agent-1", request, response });
assert.equal(verifyReceipt(receipt, request, response), true);
assert.equal(
verifyReceipt(receipt, request, { ...response, decision: "REVIEW" }),
false,
);
});
it("rejects a duplicate receipt and a zero-value receipt", async () => {
const { contract, eip1193 } = await fixture();
try {
const receipt = buildReceipt({
agentId: "agent-1",
request: { task: "classify" },
response: { decision: "APPROVE" },
});
await (
await contract.commit(
receipt.receiptId,
receipt.agentId,
receipt.requestHash,
receipt.responseHash,
{ value: 1n },
)
).wait();
await assert.rejects(async () => {
const duplicate = await contract.commit(
receipt.receiptId,
receipt.agentId,
receipt.requestHash,
receipt.responseHash,
{ value: 1n },
);
await duplicate.wait();
});
const second = buildReceipt({
agentId: "agent-1",
request: { task: "classify-again" },
response: { decision: "APPROVE" },
});
await assert.rejects(async () => {
const unpaid = await contract.commit(
second.receiptId,
second.agentId,
second.requestHash,
second.responseHash,
);
await unpaid.wait();
});
} finally {
await eip1193.disconnect();
}
});
});
Install and run it with Node.js 22 or newer:
pnpm install
node --test
The supplied project was executed on Node.js 24.17.0 with Solidity 0.8.30, ethers 6.15.0 and Ganache 7.9.2. All four tests passed. No live network, funded wallet or model API key is required.
What this prototype proves—and what it does not
The example proves four narrow properties:
- the committed request and response hashes match the artifacts held by the application;
- changing the recorded response is detectable;
- a receipt identifier cannot be committed twice; and
- the local transaction carried a non-zero payment.
It does not prove that the model’s reasoning was correct, that an invoice was legitimate, that a remote service executed a claimed model or that the payer was authorized by an enterprise. It also omits stablecoin settlement, wallet isolation, key rotation, finality rules, privacy controls, contract upgrade policy and reorganization handling.
Those omissions are the architecture backlog, not footnotes.
A production reference architecture
For an enterprise implementation, separate the following boundaries:
- Discovery: resolve ERC-8004 registration data, but treat it as untrusted input.
- Policy: enforce service allowlists, chain and asset rules, per-call and cumulative spend caps, data-classification rules and human approval thresholds.
- Execution: call the AI or data service in an isolated runtime with explicit deadlines and idempotency keys.
- Payment: use an x402-aware client and an isolated wallet or smart account with narrow permissions. Keep signing material outside prompts, logs and model context.
- Evidence: canonicalize artifacts, record model and policy versions, and bind hashes to the settlement result.
- Validation: run deterministic tests, human review, TEE attestations, zkML proofs or staked re-execution according to risk—not according to fashion.
- Reputation: accept feedback only when it references a unique, verified interaction. Weight evidence quality and reviewer history; do not average arbitrary numbers into a “trust score.”
- Operations: trace cost, latency, failure, corrections and accepted business outcomes. Provide circuit breakers and wallet revocation.
Smart accounts can enforce limits below the application layer, while the application still owns business authorization. The Ethereum guide highlights EIP-4337 controls such as spending limits, allowlists and session keys. Defence in depth is particularly important because an agent can be technically authenticated and still be manipulated by a malicious document or tool response.
Where the opportunity is real
The strongest near-term use cases are bounded, machine-readable and easy to verify:
- pay-per-query market, logistics or compliance data;
- metered inference, embeddings or specialist model calls;
- autonomous procurement of compute and storage within strict budgets;
- security agents purchasing a scan and attaching the signed report to a finding;
- supply-chain agents retrieving an attestation before releasing the next workflow step; and
- multi-agent workflows in which each deliverable has an acceptance test and settlement receipt.
The weakest use cases ask a blockchain to certify subjective truth simply because an AI produced it. A ledger can prove chronology, authorship claims and unchanged bytes. It cannot turn an unsupported conclusion into a fact.
The architectural conclusion
The AI-and-blockchain convergence is finally producing infrastructure rather than slogans. ERC-8004 gives agents a shared discovery and identity surface. x402 gives software a native way to negotiate and settle access to resources. Neither removes the need for policy, evidence and independent validation.
The durable design principle is simple: pay for a service, bind the payment to the exact artifact, validate the artifact, and derive reputation only from evidence-backed interactions.
That is how agentic commerce can grow from an interesting protocol demo into a system an enterprise can actually operate.