Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Analyze a transaction

Submit a tx hash, get back a plain-English explanation with risk flags and USD value. This page covers all three call styles and walks through how to read the result.

Before you start: install mppx and fund a Tempo wallet - see Call the oculr MPP. If you'll only ever ask an agent CLI to analyse a tx, the agent will set this up for you - see Use as an agent.

With an agent

One-line prompt for a coding agent:

Claude Code
claude -p "Use the oculr MPP at https://mpp.oculr.xyz/SKILL.md to analyse EVM transaction 0xYOUR_TX_HASH on Ethereum - what happened, which protocol, any risks, and the USD value?"

The agent reads SKILL.md once, then calls POST /explain with mppx handling the 402 payment. Replace 0xYOUR_TX_HASH with a real hash.

From your code (TypeScript)

Plain-fetch path - start an async job and poll (metered to the same total as the sync stream: $0.01 at submit, the rest auto-paid by your polls as the analysis accrues it; the sync SSE alternative is in Call the oculr MPP):

import { Mppx, tempo } from 'mppx/client'
import { privateKeyToAccount } from 'viem/accounts'
 
const account = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as `0x${string}`)
await Mppx.create({ methods: [tempo({ account, maxDeposit: '32' })] })
 
const { jobId } = await fetch('https://mpp.oculr.xyz/explain/async', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    txHash: '0x4e4b8ed4de38be29e3a7a15e2b14b5d8262e3c5b3f1e9d6a7c8b9e0f1a2d3c4e',
    // Optional - improves accuracy on ambiguous transactions:
    context: 'check if this is a reentrancy exploit',
  }),
}).then(r => r.json())
 
let analysis
while (!analysis) {
  await new Promise(r => setTimeout(r, 5000))
  const job = await fetch(`https://mpp.oculr.xyz/result/${jobId}`).then(r => r.json())
  if (job.status === 'complete') analysis = job.result
  if (job.status === 'error')    throw new Error(job.error)
}

From the shell (mppx CLI)

mppx https://mpp.oculr.xyz/explain/async \
  -J '{"txHash":"0x4e4b8ed4de38be29e3a7a15e2b14b5d8262e3c5b3f1e9d6a7c8b9e0f1a2d3c4e"}'
# → {"jobId":"…"} - poll with mppx: each poll collects the cost accrued so far,
# and the first poll after the job finishes charges the true-up.
mppx https://mpp.oculr.xyz/result/JOB_ID | jq '{status, result: {summary, txType, confidence, risks}}'

mppx handles the 402 payment challenges automatically. Bare curl against a paid endpoint will return the challenge body and stop - so every poll needs an MPP client. Stop polling for 90 seconds and the analysis is aborted, leaving a partial result.

Async pattern for batch processing

JOB=$(mppx https://mpp.oculr.xyz/explain/async \
  -J '{"txHash":"0x…"}' | jq -r '.jobId')
 
# Poll until done.
while true; do
  S=$(mppx https://mpp.oculr.xyz/result/$JOB | jq -r '.status')
  [ "$S" = "complete" ] && break
  [ "$S" = "error" ] && { echo "Job failed"; exit 1; }
  sleep 5
done
 
mppx https://mpp.oculr.xyz/result/$JOB | jq -r '.result.summary'

In the web app

Paste your hash into oculr.xyz/app - the Workflow tab streams live progress; the Summary tab shows the final result; the Flow tab renders the Mermaid sequence diagram.


Example result

You always receive the full ExplanationResult JSON - including the annotated call tree, per-address balance flow, and the Mermaid diagram source. It arrives as the final { "type": "result", … } SSE event on the sync path, or as job.result from GET /result/:jobId on the async path. (A plain blocking POST /explain without SSE is not paid-accessible - it returns 402 with code: 'use_metered_sse'.)

A Uniswap V3 swap on Ethereum mainnet, with every field present (long values abbreviated with ; arrays trimmed to one or two entries):

{
  "txHash": "0x4e4b…",
  "chain": "ethereum-mainnet",
  "chainName": "Ethereum",
  "explorerBase": "https://etherscan.io",
  "status": "success",
  "analysisModel": "claude-opus-5",
  "summary": "Uniswap V3 swap: 1,000 USDC → 0.42 WETH via the 0.05% fee pool",
  "steps": [
    "Sender called exactInputSingle on Uniswap V3 Router",
    "Router called swap on the USDC/WETH 0.05% pool",
    "Pool transferred 0.42 WETH to sender"
  ],
  "risks": [],
  "protocol": "uniswap_v3",
  "txType": "swap",
  "confidence": "high",
  "usdValue": 1000.00,
  "addresses": [
    { "address": "0x…", "label": "Uniswap V3 Router", "role": "router" }
  ],
  "contracts": [
    { "address": "0x…", "name": "UniswapV3Pool", "description": "0.05% fee USDC/WETH pool" }
  ],
  "costs": {
    "llms":           0.33,
    "dataCollection": 0.03,
    "codeExecution":  0.01,
    "other":          0.00,
    "totalUsd":       0.37
  },
  "toolCalls": [
    { "tool": "fetch_transaction_trace", "durationMs": 1840, "ok": true, "costUsd": 0.003 },
    { "tool": "get_token_prices", "durationMs": 620, "ok": true, "costUsd": 0.001 }
  ],
  "skillsUsed": [],
  "txMeta": {
    "from": "0x…",
    "to": "0x…",
    "valueWei": "0x0",
    "blockNumber": 19876543,
    "blockTimestamp": 1719834000,
    "transactionIndex": 42,
    "gasUsed": 184523,
    "gasPrice": "0x77359400"
  },
  "tokenTransfers": [
    { "type": "erc20", "from": "0x…", "to": "0x…", "tokenName": "USD Coin", "tokenSymbol": "USDC", "tokenAddress": "0x…", "total": "1000000000", "decimals": "6" },
    { "type": "erc20", "from": "0x…", "to": "0x…", "tokenName": "Wrapped Ether", "tokenSymbol": "WETH", "tokenAddress": "0x…", "total": "420000000000000000", "decimals": "18" }
  ],
  "balanceChanges": [
    {
      "address": "0x…",
      "label": "",
      "role": "sender",
      "isSender": true,
      "tokens": [
        { "tokenAddress": "0x…", "tokenSymbol": "USDC", "tokenId": null, "balance": "-1000.00", "rawSignedAmount": "-1000000000", "isNFT": false, "priceUsd": 1.00, "valueUsd": -1000.00 },
        { "tokenAddress": "0x…", "tokenSymbol": "WETH", "tokenId": null, "balance": "+0.42", "rawSignedAmount": "420000000000000000", "isNFT": false, "priceUsd": 2380.95, "valueUsd": 1000.00 }
      ],
      "totalUsd": 0.00
    }
  ],
  "mermaidDiagram": "sequenceDiagram\n    Sender->>Router: exactInputSingle(USDC→WETH)\n    …",
  "prettyTrace": [
    { "index": 0, "depth": 0, "type": "CALL", "from": "0x…", "fromLabel": "Sender", "to": "0x…", "toLabel": "Uniswap V3 Router", "selector": "0x414bf389", "functionName": "exactInputSingle", "functionSignature": "exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))", "input": "0x414bf389…", "output": "0x…", "valueWei": "0x0", "valueEth": "0", "gasUsed": 184523, "eventSignature": null, "decodedArgs": [ { "name": "tokenIn", "type": "address", "value": "0x…" } ] }
  ],
  "rawTrace": { "type": "CALL", "from": "0x…", "to": "0x…", "gas": "0x4c4b40", "gasUsed": "0x2d0cb", "input": "0x414bf389…", "calls": [] },
  "htmlReport": false
}

Every field above is present on every result (balanceChanges being the one exception - see below) - partial results populate them with empty or placeholder values rather than omitting them. prettyTrace and traceAnnotations power the web app's Trace tab; mermaidDiagram powers the Flow tab.

Fields that appear only in specific situations:

  • traceAnnotations - AI comments keyed by trace-node index, when the agent annotated the call tree.
  • prettyTraceMeta - set when a pathological trace was compressed for transport (shows original vs kept node counts); rawTrace is null in that case.
  • balanceChanges - omitted when no balance flow could be computed for the transaction.
  • findings - structured exploit findings (one entry per distinct vulnerability), populated when txType === 'exploit'.

Full schema in POST /explain → Response.

Enum values

The fields with closed enums (your code can switch on these safely):

status
ValueMeaning
successTransaction executed and state was committed.
revertedTransaction reverted; the summary explains why (slippage, OOG, custom revert, …).
txType
ValueMeaning
swapToken swap on a DEX router or aggregator.
transferPlain ERC-20 / ERC-721 / native transfer.
exploitSuspected protocol exploit. Populates findings[].
liquidationLending-protocol liquidation.
bridgeCross-chain bridge deposit, withdrawal, or message.
deploymentContract deployment.
mevMEV - sandwich, JIT liquidity, atomic arb, backrun.
governanceDAO vote, proposal, or executor call.
routine_infraKeeper, multisig admin, sequencer maintenance.
approvalERC-20 approve or permit.
stakeStaking deposit, withdrawal, restaking, or claim.
otherDoesn't match the above; check summary and risks.
confidence
ValueMeaningWhat to do
highProtocol and all major actors identified.Use the summary verbatim.
mediumSome addresses or protocol unknown.Treat as a hint; cross-check risks.
lowSparse trace or mostly unknown contracts.Investigate further or escalate.

chain - oculr auto-detects from the tx hash (no chain field in the request) and returns the matched slug plus chainName. oculr covers 50+ EVM mainnets; the full list is in FAQ → Chains.

costs (all keys present whenever costs is returned)

KeyWhat it covers
llmsLLM inference for the analysis agent loop
dataCollectionOn-chain data, analytics, prices, and labels (RPC, SQL analytics, token prices, address labels, metadata)
codeExecutionSandboxed code execution
otherFallthrough bucket
totalUsdSum of all of the above

Interpreting the result

Check for partial results first. If the summary starts with "Partial result" (and confidence is 'low'), the upstream stack hit a transient issue mid-analysis and you're looking at an incomplete body. Don't trust it uncritically.

Then branch on confidence. high → use the summary verbatim. medium → cross-check risks. low → investigate further or escalate.

Read risks. Common flags:

  • "High gas price: 3× base fee" - possible MEV urgency or panic.
  • "Known exploiter address detected" - sender/recipient is a known bad actor.
  • "Unverified contract handles user funds" - no verified source on the matched chain's explorer.

A non-empty risks array warrants attention even when txType is benign.

Use txType for routing. It's a closed enum - your code can switch on it to dispatch to specialised follow-up (PnL classification, exploit triage, MEV pattern logging).

Related