# Tres Tx Story

> Analyze a blockchain transaction by its hash using TRES Finance MCP and generate a visual flow diagram showing all asset movements, followed by a plain-language explanation of what happened. Use this skill whenever the user provides a transaction hash and asks to "analyze", "explain", "visualize", "diagram", "show me what happened in", or "tell the story of" a transaction. Also trigger when the user pastes a tx hash (long hex string starting with 0x) and asks anything about it. Always use this skill for transaction hash analysis — do not attempt to answer from memory alone.

- Skill: `gabrielmoreira/tres-tx-story` (Agent Skill)
- Install (CLI): `npx skillmds@latest add gabrielmoreira/tres-tx-story`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gabrielmoreira/tres-tx-story/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: gabrielmoreira (https://skillmd.com/u/gabrielmoreira)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/gabrielmoreira/tres-tx-story

---


# TRES Transaction Story

Fetch a transaction by hash from TRES Finance, render a flow diagram of all asset
movements, and explain in plain English what happened.

## MCP Server
All calls use the `TRES Finance MCP` server (URL: `https://ai.tres.finance/mcp`).

---

## Step 1 — Authenticate
Call `get_viewer` (no arguments) to verify the session.

---

## Step 2 — Fetch the Transaction

Query the transaction using its hash as the `identifier`:

```graphql
query GetTransactionByHash($hash: String!, $currency: String) {
  transaction(identifier: $hash, currency: $currency, limit: 1) {
    results {
      id
      identifier
      platform
      timestamp
      success
      decodedFunctionName
      methodId
      fromAddress { identifier displayName isInternal customAccountName }
      toAddress   { identifier displayName isInternal customAccountName }
      contract    { identifier contractName protocols applications }
      classification { activity action functionName }
      ledgerSummary
      applications
      protocols
      internalAccounts { id name }
      children {
        id
        amount
        balanceFactor
        platform
        type
        fiatValue
        nonTaxableType
        isInternalTransfer
        financialActionGroup
        belongsTo { id name identifier }
        sender    { identifier displayName isInternal customAccountName }
        recipient { identifier displayName isInternal customAccountName }
        asset {
          identifier
          symbol: key
          assetClass { id symbol verificationStatus }
        }
      }
    }
  }
}
```

Variables: `{ "hash": "<user_provided_hash>", "currency": "usd" }`

> **Fallback**: If `identifier` returns 0 results, retry with `identifier_Contains`
> using the last 20 characters of the hash. Hashes on some chains may be stored
> without the `0x` prefix — strip it and retry if still empty.

---

## Step 3 — Parse the Data

From the result, extract:

| Field | Purpose |
|---|---|
| `identifier` | The tx hash |
| `platform` | Blockchain (ETHEREUM, SOLANA, etc.) |
| `timestamp` | Date/time |
| `success` | Did it succeed? |
| `decodedFunctionName` | What smart-contract function was called |
| `fromAddress` / `toAddress` | Top-level sender → receiver |
| `contract.contractName` | Smart contract involved (if any) |
| `classification.activity` + `.action` | TRES classification label |
| `children[]` | Each sub-transaction (individual token movement) |

For each child sub-transaction, extract:
- `asset.assetClass.symbol` — the token symbol (e.g. ETH, USDC)
- `amount` — quantity moved
- `fiatValue` — USD equivalent
- `balanceFactor` — `1` = inflow, `-1` = outflow
- `belongsTo.name` — which internal wallet this belongs to
- `sender.identifier` / `recipient.identifier` — counterparties
- `type` — GAS, TOKEN_TRANSFER, etc.

---

## Step 4 — Render the Diagram

Use the `show_widget` tool (Visualizer) to render an HTML widget containing an inline SVG diagram.

---

### PRE-RENDER CHECKLIST — do this before writing a single SVG element

**A. Prepare all label strings first**

For every arrow label (token amount + symbol), apply these formatting rules:
1. Format the amount:
   - amount ≥ 1,000,000 → `"1.2M"` style (1 decimal)
   - amount ≥ 1,000 → `"264K"` or `"12,233"` (comma-separated integer)
   - amount ≥ 1 → round to 2 decimal places max
   - amount < 1 → keep up to 4 significant figures (e.g. `"0.033"`)
2. Truncate the symbol:
   - symbol ≤ 6 chars → use as-is (ETH, USDC, DAI, CRV)
   - symbol 7–10 chars → use as-is only if the full label fits (see step 3)
   - symbol > 10 chars OR contains `+` OR is a compound LP token → replace with `"LP tokens"`
3. Measure the full label: `label = amount + " " + symbol`. Count characters. Multiply by 7. That's the pixel width.
4. The available space for a label = the arrow's x-span minus 20px safety margin.
   - Left gap (wallet→contract): x goes from 136 to 248 → span = 112px → max label = 13 chars
   - Right gap (contract→wallet): x goes from 408 to 504 → span = 96px → max label = 11 chars
5. If `label_px_width > available_space`, shorten further: abbreviate amount to 1 sig fig, or use symbol-only if still too wide.

**B. Count the arrows and calculate SVG height**

- Count non-gas arrows (outflows + inflows). Call this `N`.
- Each arrow row occupies 34px of vertical space.
- Base node centre Y = 50 + (N × 34) / 2 (so arrows fan symmetrically around the node centre).
- Gas arrow hangs 60px below the bottom of the left wallet circle.
- SVG height = node_centre_y + node_radius + 80 (gas arrow + label + padding).
- Minimum SVG height = 230px.

**C. Lay out arrow Y positions**

- Space arrows evenly, 34px apart, centred on `node_centre_y`.
- First arrow y = node_centre_y - ((N-1) × 34) / 2
- Each subsequent arrow y += 34
- Outflow arrows (wallet → contract) use left half of diagram.
- Inflow arrows (contract → wallet) use right half.
- If there is exactly 1 inflow and multiple outflows, centre the inflow arrow at `node_centre_y`.

---

### Fixed node positions (do not change these)

```
ViewBox width: 640
Left wallet circle:    cx=90,  cy=node_centre_y, r=46
Contract rect:         x=248,  y=node_centre_y-55, width=160, height=110, rx=12
Right wallet circle:   cx=550, cy=node_centre_y, r=46

Arrow x coordinates:
  Outflow (left→centre): x1=136, x2=246   midpoint_x=191
  Inflow (centre→right): x1=410, x2=504   midpoint_x=457
  Gas (downward):        x1=x2=90, y1=node_centre_y+46
```

These x values are derived from the node edges and must not be adjusted. They guarantee labels always fall in the clear gap between nodes.

---

### Arrow label placement rules (no exceptions)

```
Label x = midpoint_x of arrow (191 for outflows, 457 for inflows)
Label y = arrow_y - 12
text-anchor = "middle"
font-size = 10.5px
font-weight = 500
```

**Never** place a label at the same y as another label. If two arrows are only 34px apart, their labels (at y-12) are 34px apart — that is sufficient. Never stack two labels at the same y coordinate.

**Never** use `text-anchor="start"` for labels that fall near node edges — always `"middle"` anchored at the midpoint x.

---

### Node internal text rules

Wallet circle (r=46, so usable text band = ±36px from centre):
```
Line 1 (name):    y = cy - 8,  font-size=11, font-weight=500
Line 2 (address): y = cy + 7,  font-size=9.5, font-family=monospace
Line 3 (role):    y = cy + 21, font-size=9
```
Three lines maximum. Each line must fit within the circle width (chord at that y ≈ 80px = ~11 chars at 9px, ~9 chars at 10px). Truncate wallet names longer than 10 chars.

Contract rect (width=160, height=110):
```
Line 1 (name):     y = rect_top + 28, font-size=11, font-weight=500
Line 2 (subname):  y = rect_top + 44, font-size=10
Line 3 (function): y = rect_top + 60, font-size=9.5
Line 4 (address):  y = rect_top + 76, font-size=9, font-family=monospace
Line 5 (type):     y = rect_top + 92, font-size=9
```
Maximum 5 lines. Each line centred at x=328. Lines must fit within 152px (160 - 8px padding each side) → max ~21 chars at 9px. Truncate anything longer.

---

### Gas arrow rules

- Always vertical, pointing down from the bottom of the left wallet circle.
- x1 = x2 = 90 (same as wallet cx)
- y1 = node_centre_y + 46 + 2 (2px gap below circle edge)
- y2 = y1 + 55
- Label: placed to the RIGHT of the line — `x=98, y=(y1+y2)/2, text-anchor="start"`
- Below the arrow tip, add a small pill: rect with label "network fee", centred at x=90.
- Gas label max length: `"0.033 ETH gas"` = 13 chars. Always fits.

---

### Color coding

| Element | Fill | Stroke/Color |
|---|---|---|
| Internal wallet (sender) | `#EAF3DE` | `#639922` |
| Internal wallet (receiver) | `#dcfce7` | `#16a34a` |
| Smart contract | `#EEEDFE` | `#7F77DD` |
| Outflow arrow | — | `#dc2626` |
| Inflow arrow | — | `#16a34a` |
| Gas arrow | — | `#ea580c` |
| Outflow label text | — | `#dc2626` |
| Inflow label text | — | `#16a34a` |
| Gas label text | — | `#ea580c` |

---

### Animation

```css
@keyframes flowDash { to { stroke-dashoffset: -40; } }
@keyframes fadeUp   { from { opacity:0; transform:translateY(14px); } to { opacity:1; transform:translateY(0); } }

Outflow arrows: stroke-dasharray:8 5; animation: flowDash 1.1s linear infinite;
Inflow arrows:  stroke-dasharray:8 5; animation: flowDash 1.1s linear infinite;
Gas arrow:      stroke-dasharray:4 4; animation: flowDash 1.7s linear infinite;
Cards/sections: opacity:0; animation: fadeUp .5s ease forwards; (staggered delays)
```

---

### Header

Single line of plain text, no pill badges:
```
[hash-pill: 0xABCD...1234]   Chain · Date · function() · ✓ success
```
Hash pill style: monospace, 11px, bg-secondary, border, border-radius 6px, padding 4px 10px.
Everything else: 12px, color-text-secondary. Success mark: color-text-success, font-weight 500.

---

### Stat cards (below diagram)

3-column grid, one card per: total sent (red), total received (green), gas paid (orange).
Format values as: `−$272,910` / `264K LP` / `0.127 ETH`. Keep under 12 chars.
Sublabel: 11px, muted, shows token breakdown if multiple assets.

---

### Footer route card

`[function() badge]  via ContractName  · [protocol tag] [protocol tag]  [Explain X ↗ button]`
Function badge: yellow bg `#FEF9C3`, text `#854d0e`, border-radius 6px.
Protocol tags: bg-secondary, border, border-radius 4px, 11px.
Button: calls `sendPrompt('How does [ContractName/protocol] work?')`

---

## Step 5 — Plain-Language Explanation

After the diagram, write a short explanation (3–6 sentences) in simple, non-technical
language. Cover:

1. **What happened**: The core action (swap, transfer, deposit, stake, etc.)
2. **Who was involved**: Wallets and contracts in plain terms
3. **What moved**: Which assets changed hands and in what direction
4. **Net result**: What the org's wallets ended up with / paid out

**Tone**: Imagine explaining to someone who understands crypto but not accounting.
Avoid jargon like "balanceFactor", "sub-transaction", "FIFO". Say "sent", "received",
"paid as gas", "swapped X for Y", etc.

**Example**:
> This was a token swap on Uniswap. Your wallet (Main Treasury) sent 1,000 USDC to
> the Uniswap V3 router contract, which in turn sent back 0.412 ETH (worth ~$1,020).
> You also paid 0.003 ETH (~$7.40) in gas fees to the Ethereum network.
> Net result: you traded stablecoins for ETH at roughly $2,476 per ETH.

---

## Error Handling

| Situation | Action |
|---|---|
| Transaction not found | Tell user the hash wasn't found in TRES. Ask if they want to check the hash or try a different one. |
| No children (empty sub-txs) | Show header only; note "No asset movements recorded — the tx may be a failed or contract-only interaction." |
| Missing fiat values | Show amounts without USD; note "USD value unavailable for some assets." |
| Auth failure | Tell user to check TRES connection and re-authenticate. |

