> ## Documentation Index
> Fetch the complete documentation index at: https://paralens.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Build an Ethereum Transaction Visualizer with ParaLens

> Use ParaLens to build a shareable transaction page that shows intent, actor, token flows, gas cost, and Etherscan links for any Ethereum tx hash.

Take any Ethereum transaction hash and render a rich, human-readable page that shows exactly what happened — the economic intent, who was involved, what tokens moved, and how much gas was spent. ParaLens does the heavy lifting: one `POST /analyze` call returns a fully structured `TxReport` with labeled intent, actor attribution, token flows, and supporting motif evidence, so your UI can focus on display logic rather than on-chain trace decoding.

## What to Display

<CardGroup cols={2}>
  <Card title="Intent Label" icon="tag">
    The plain-English description of what the transaction did — e.g., "Atomic arbitrage" or "Swap via aggregator". Sourced from `classification.intent_label`.
  </Card>

  <Card title="Actor & Signer" icon="user">
    The economic actor (who benefited) and the signing wallet (who paid gas). These are often different for smart wallets, aggregators, and MEV bots.
  </Card>

  <Card title="Token Flows" icon="arrow-right-arrow-left">
    A visual send/receive summary: which tokens went in, which came out, and the USD value of each movement — sourced from `economics` and `inventory_deltas`.
  </Card>

  <Card title="Gas & Net USD" icon="gauge">
    Gas cost in USD alongside the net economic result before and after gas, so readers can see the true cost of the transaction.
  </Card>

  <Card title="Motifs (Proof)" icon="magnifying-glass">
    The structural primitives that the engine matched — swap hops, closed value flows, flash loan receipts — listed as supporting evidence for the intent label.
  </Card>

  <Card title="Explorer Link" icon="arrow-up-right-from-square">
    A direct Etherscan link for one-click cross-referencing on the public block explorer, sourced from `tx.etherscan_url`.
  </Card>
</CardGroup>

## Fields to Use

The table below maps each visualizer component to the exact `TxReport` field that supplies it.

| Visualizer component | TxReport field                                        |
| -------------------- | ----------------------------------------------------- |
| Intent label         | `classification.intent_label`                         |
| Confidence badge     | `classification.confidence` + `classification.status` |
| Actor address        | `classification.actor`                                |
| Signer address       | `attribution.tx_signer`                               |
| Token in             | `economics.token_in`                                  |
| Token out            | `economics.token_out`                                 |
| Gas cost (formatted) | `economics.fee_usd.text`                              |
| Net USD (formatted)  | `economics.net_usd_before_gas.text`                   |
| Etherscan link       | `tx.etherscan_url`                                    |
| Proof motifs         | `motifs[]`                                            |

## Actor vs. Signer

In a simple token transfer the signer and the economic actor are the same wallet. In more complex transactions they diverge:

* **Aggregators** — a user signs a transaction to a DEX aggregator router; the router is the on-chain actor but the user is the economic beneficiary.
* **Smart wallets** — a relayer EOA signs a meta-transaction on behalf of an ERC-4337 account or a Gnosis Safe.
* **MEV bots** — a bot EOA signs the transaction, but the bot contract (or the searcher behind it) is the real economic actor.

When `classification.actor_matches_signer` is `false`, display both addresses separately and add a visual indicator (e.g., a tooltip or badge) explaining the discrepancy. This distinction matters for compliance, attribution, and UX clarity.

```typescript theme={null}
const actorMatchesSigner = report.classification.actor_matches_signer;

if (!actorMatchesSigner) {
  console.log("⚠️  Actor differs from signer");
  console.log("Signer  :", report.attribution.tx_signer);
  console.log("Actor   :", report.classification.actor);
} else {
  console.log("Signer / Actor:", report.attribution.tx_signer);
}
```

## Token Flow Diagram

Group `inventory_deltas` by `subject` and then by `direction` to build a per-address inflow/outflow table. This gives you the raw material for a Sankey diagram, a flow table, or a simple "+/−" token list.

```typescript theme={null}
type Delta = {
  subject: string;
  asset_label: string;
  direction: "Inflow" | "Outflow";
  quantity: string;
  usd: { value: number; text: string } | null;
};

type FlowRow = {
  asset: string;
  direction: "Inflow" | "Outflow";
  quantity: string;
  usd: string;
};

function buildFlowTable(
  deltas: Delta[],
  subject: string
): FlowRow[] {
  return deltas
    .filter((d) => d.subject === subject)
    .map((d) => ({
      asset: d.asset_label,
      direction: d.direction,
      quantity: d.quantity,
      usd: d.usd?.text ?? "—",
    }));
}

// Usage
const actorFlows = buildFlowTable(
  report.inventory_deltas,
  report.classification.actor
);

actorFlows.forEach((row) => {
  const sign = row.direction === "Inflow" ? "+" : "−";
  console.log(`${sign} ${row.quantity} ${row.asset}  (${row.usd})`);
});
```

## Reverted Transactions

When `classification.intent_kind` is `"TransactionReverted"`, the transaction failed on-chain. No token transfers occurred, but the signer still paid gas. In this case:

* Skip the token flow section entirely — there is nothing to show.
* Display a **failure card** with the revert reason (if available) and the gas cost.
* Use `economics.fee_usd.text` to show how much the failed transaction cost.

```typescript theme={null}
if (report.classification.intent_kind === "TransactionReverted") {
  return (
    <FailureCard
      title="Transaction Reverted"
      message="This transaction failed on-chain. No tokens were transferred."
      gasCost={report.economics.fee_usd?.text ?? "unknown"}
      etherscanUrl={report.tx?.etherscan_url}
    />
  );
}
```

## Example: Rendering Intent Label and Actor

The snippet below shows a minimal React component that renders the headline information from a `TxReport`.

```typescript theme={null}
import React from "react";

type TxReport = {
  classification: {
    intent_label: string;
    intent_kind: string;
    confidence: number;
    status: string;
    actor: string;
    actor_matches_signer: boolean;
  };
  attribution: {
    tx_signer: string;
  };
  economics: {
    token_in: { symbol: string; amount: string } | null;
    token_out: { symbol: string; amount: string } | null;
    net_usd_before_gas: { text: string; available: boolean };
  };
  tx: {
    etherscan_url: string;
  };
};

export function TxHeader({ report }: { report: TxReport }) {
  const { classification, attribution, economics, tx } = report;

  return (
    <div className="tx-header">
      {/* Intent */}
      <h1>{classification.intent_label}</h1>
      <span className="badge confidence">
        {Math.round(classification.confidence * 100)}% confidence
      </span>

      {/* Actor / Signer */}
      <div className="addresses">
        {classification.actor_matches_signer ? (
          <p>
            <strong>Address:</strong> {attribution.tx_signer}
          </p>
        ) : (
          <>
            <p>
              <strong>Signer:</strong> {attribution.tx_signer}
            </p>
            <p>
              <strong>Actor:</strong> {classification.actor}
            </p>
          </>
        )}
      </div>

      {/* Token flow summary */}
      {economics.token_in && (
        <p>
          Sent: {economics.token_in.amount} {economics.token_in.symbol}
        </p>
      )}
      {economics.token_out && (
        <p>
          Received: {economics.token_out.amount} {economics.token_out.symbol}
        </p>
      )}

      {/* Net USD */}
      {economics.net_usd_before_gas.available && (
        <p>Net value: {economics.net_usd_before_gas.text}</p>
      )}

      {/* Explorer */}
      <a href={tx.etherscan_url} target="_blank" rel="noreferrer">
        View on Etherscan ↗
      </a>
    </div>
  );
}
```
