v1.3RPC gateways in the trace, user operations sent elsewhere →

Trace every on‑chain move
inside your agent's trace

hashspan is an open-source OpenTelemetry library that records every transaction an AI agent or service sends as spans keyed by its hash, inside its own trace. It works the same for TypeScript services that send EVM transactions, such as payment and payout workers, wallet backends and bots. Status, gas, L1 fees and revert reasons, in the backend you already use.

Your AI agent's on-chain transactions as OpenTelemetry spans, inside the agent's own trace.

TypeScript · Node.js 22.3+ · EVM chains, checked on Base · any OpenTelemetry backend · @hashspan/viem: 827 npm downloads last month

Adapters

Checked with the Vercel AI SDK, Mastra, LangChain, OpenAI Agents and Coinbase AgentKit. See the setups →

trace 97bf3a6real run · Base Sepolia (84532)
SpanTime and result
invoke_agent
inside invoke_agent: execute_tool pay_vendor
inside execute_tool pay_vendor: send 845320x2e68…3fa80x2e68…3fa8
inside execute_tool pay_vendor: confirm 84532success · block 48338717success · block 48338717
inside invoke_agent: execute_tool withdraw_from_vault
inside execute_tool withdraw_from_vault: send 84532
inside execute_tool withdraw_from_vault: confirm 84532reverted · WithdrawalLimitExceeded(…)reverted · WithdrawalLimitExceeded(…)
hashspan in 50 seconds, with music and no voice. Real traces from Base Sepolia and the local lab.
What the video shows, in words
  1. An agent pays for an API, and the question: did it settle?
  2. The payment's trace: the payment span, the facilitator settling it, and a confirm span with the result.
  3. One withHashspan() call, and the trace building row by row.
  4. A withdrawal that reverts: its confirm span turns red, with the revert reason and the L1 fee.
  5. The pipeline: viem, Coinbase CDP, x402, MPP and ERC-4337, through the OpenTelemetry SDK to Jaeger, Grafana Tempo, Langfuse and Honeycomb, with fee and latency metrics.
  6. A real trace in Jaeger, and the metrics dashboard in Grafana.
  7. User operations and call batches, JSON-RPC spans, and replaced transactions.
  8. The error decoder: a pasted error lands on the page that explains it.
  9. The tested setups, one bar each.
  10. Safe by design: hashspan never signs or broadcasts.
  11. The attribute lookup on the site.
  12. A coding agent adding hashspan with one command, then hashspan.dev and npm install @hashspan/viem.
blockchain.tx.*

The chain outcome, where the agent's decision already lives.

Your agent trace shows the LLM call and the tool call. hashspan adds what happened next.

Send and confirm spans

Each transaction gets a send span when the hash comes back and a confirm span when the receipt does, nested under the tool that sent it and linked to each other.

  1. execute_toolpay_vendor
  2. send 845320x2e68…3fa8
  3. mempoolpending…
  4. confirm 84532success
  5. block#48338717

The real cost

On OP-stack chains like Base, blockchain.tx.fee includes the L1 data fee, and blockchain.tx.l1_fee records it on its own. The operator fee has its own attribute, blockchain.tx.operator_fee. Fees come from the sealed receipt, even on Base's flashblocks RPCs. With a metrics SDK, fees and confirmation times become histograms too.

Agent-aware

Transaction spans carry the agent's identity, so you can ask which agent spent what.

gen_ai.agent.name = "treasury-agent"

Reverts you can read

Mined reverts are replayed and decoded, custom errors included.

status = reverted
revert.reason = WithdrawalLimitExceeded(…)

Replaced transactions, handled

Same nonce, new hash? The receipt goes to the transaction that was actually mined.

blockchain.tx.status = replaced

You choose what is recorded

Calldata arguments and error messages are opt-in. Addresses are recorded by default, in lower case, and can be hashed or dropped; x402 keeps only the origin of a paid URL. It never signs or broadcasts.

Payments, too

With x402, the agent signs and a facilitator settles. A payment span records what was paid, to whom and whether it settled. With a reader, the settling transaction gets a confirm span linked to it, and the payment span checks that it actually carries your payment. MPP payments made with mppx get the same spans.

blockchain.payment.status = settled

What the send spent its time on

Signing, RPC and HTTP spans from your other instrumentation nest under the send span, for viem clients with a chain and for CDP. With traceTransport(), each JSON-RPC request becomes a span of its own, and propagateTraceContext() brings an RPC gateway such as eRPC into the same trace.

quickstart

One extend call.
No new dashboard.

Standard OpenTelemetry spans, exported wherever your traces already go.

  • Jaeger
  • Grafana Tempo
  • Langfuse
  • Honeycomb
  • any OTLP backend
$ npm install @hashspan/viem @opentelemetry/api viem
import { createPublicClient, createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { withHashspan } from '@hashspan/viem';

const hashspan = withHashspan();
const wallet = createWalletClient({ account, chain: base, transport: http() })
  .extend(hashspan);
const reader = createPublicClient({ chain: base, transport: http() })
  .extend(hashspan);

// Inside an agent tool: send + confirm spans appear under the tool span.
const hash = await wallet.sendTransaction({ to, value });
await reader.waitForTransactionReceipt({ hash });

Not an agent?
It works the same.

In a TypeScript service that sends EVM transactions through viem, the Coinbase CDP SDK or x402, or through a wallet service, such as a payment or payout worker, a wallet backend or a bot, the quick start works as it is. Leave out the agent option, and the spans nest under the request or job span you already have. The service.name of your OpenTelemetry resource tells your services apart.

Wallet services send in different ways: through a viem account, an EIP-1193 provider or their own API. The integrations page sorts them by how they send and says what each path records. Other libraries, such as ethers, have no adapter, and chains other than EVM are not supported.

A transaction signed elsewhere, by a KMS or HSM signer or a relayer, is traced when it is broadcast through viem, and a wallet service that sends through its own API gets a send span too. For workers at volume, see many transactions; a payout worker example runs on a local chain.

A request handler span POST /payouts and a job span payout-batch, each with a send and a confirm span under it. The job sent through an EIP-1193 provider. Both confirm spans have status success; the service is payout-worker and no span has an agent attribute.A request handler span POST /payouts and a job span payout-batch, each with a send and a confirm span under it. The job sent through an EIP-1193 provider. Both confirm spans have status success; the service is payout-worker and no span has an agent attribute.

Already using an agent observability tool or a chain monitor?

They do different jobs. See where hashspan fits next to them.

How hashspan compares →

Questions

Does hashspan sign or send transactions?

No. It observes the transactions your code sends and never signs or broadcasts anything itself.

Which chains are supported?

EVM chains: transactions sent through viem or Coinbase CDP accounts, ERC-4337 user operations from viem bundler clients, CDP smart accounts, or a smart account SDK or hosted wallet that returns only a hash, EIP-5792 call batches from viem wallet clients, x402 payments, and MPP payments on Tempo. Fees, including the L1 data fee, are checked against real receipts on Base.

Do I need an AI agent?

No. A TypeScript service that sends EVM transactions through viem, the Coinbase CDP SDK or x402, or through a wallet service, such as a payment or payout worker, a wallet backend or a bot, works the same way: the send and confirm spans nest under whatever span is active, such as a request or a job, and the service.name of your OpenTelemetry resource (see the quick start) tells your services apart. Other libraries, such as ethers, have no adapter, and chains other than EVM are not supported.

Does it trace x402 payments?

Yes. @hashspan/x402 records a payment span for each paid request: what was paid, to whom and whether it settled. With a viem reader, the settling transaction gets a linked confirm span too, and for exact and upto payments the payment span records whether that transaction actually carries your payment. MPP payments made with mppx get the same payment span from @hashspan/mpp.

Which tracing backends work?

Any OpenTelemetry backend: Jaeger, Grafana Tempo, Langfuse, Honeycomb or anything that accepts OTLP.

Does it record calldata or addresses?

Calldata arguments and error messages are opt-in. Addresses are recorded by default, in lower case, and can be hashed or dropped. The transaction hash is always recorded.

Is it ready for production?

The API is stable since 1.0.0, and releases are published with npm provenance. The span and attribute names stay in development under a change policy: a renamed attribute is recorded under both names for a release, and every change is announced in the changelog.

See your first traced transaction
in five minutes.

Type to search the docs, Learn topics and the blog.