overview· xtz deposits· fa deposits· xtz withdrawals· xtz fast withdrawals· fa withdrawals· design
bridge indexing · design system

Design system

Color — two axes

Axis 1 · HUE = runtime

L1 / bridge--l1
EVM L2--evm
Michelson L2--mich
alias / NAC--alias (reserved)
system / kernel--sys

Axis 2 · ACCENT = indexed

indexed ✓--idx
planned ◔--planned

Encoding — legend

Legend
Hue L1 / bridge L2 EVM L2 Michelson system / kernel
Decision indexed planned not indexed
Status legacy · remains in mainnet

Source vocabulary

The .s line on every node is source · type · address. The source token names where the indexer (or, for non-indexed nodes, where the reader) can look up the object. Only the labels below are valid in diagrams.

SourceDipDup datasourceWhat's readVisible in diagrams
TzKTtzkt · tezos.tzktL1 operations, head, rollup inboxyes — Tezos-side anchors and rollup inbox
EVM nodeetherlink_node · evm.node + etherlink_subsquid · evm.subsquidL2 events and transactions (node for tip, subsquid for backfill)yes — every EVM anchor
Rollup noderollup_node · httpOutbox messages and proofsyes — Rollup outbox message
Tezos nodetezos_node · httpProtocol constants (one-shot at bootstrap)no — out-of-band lookup, no on-chain object
Metadatametadata · tzip_metadataTZIP-21 token metadatano — out-of-band lookup during ticket resolve
Kernel— (not a data source)Kernel-internal precompile or routine with no observable EVM traceyes — pedagogical nodes (e.g. SendOutboxMessage, parse_routing)
Kernel ≠ data source. Kernel · … means the object lives only inside the rollup kernel — no event, no separate tx, no standard JSON-RPC anchor. Such nodes are always faded (never indexed) and exist on the diagram to keep the flow legible (e.g. show how EVM Withdrawal becomes Rollup outbox message).

Layout and verification

one object = one node

One on-chain object = one node in the standard format (§3 · Node dictionary). Paired events (Token Mint/Burn) — as a separate node.

self-check before showing

Mermaid captures the geometry (overflow / collisions / a “missed arrow” are impossible). Before showing — verify the render has no console errors and that line statuses/colors are in place; screenshot the diagram by uid (take_snapshot → take_screenshot).

Mermaid

flowchart TD
  U(["User"]):::actor
  K["<b class='t'>Kernel parse_routing</b>"]:::sys
  subgraph R["L2 · EVM · ACTUAL"]
    direction TB
    E["<span class='glyph'>✓</span><b class='t'>Value Transfer</b><span class='s'>EVM node · transaction · 0x…feed</span><span class='ix'>✓ etherlink.on_xtz_deposit</span><span class='md'>✚ EtherlinkDepositOperation</span>"]:::evm
    F["<b class='t'>Event Deposit</b><span class='s'>EVM node · event · 0xff…01</span>"]:::evm
    E --> F
  end
  M["<span class='glyph'>◔</span><b class='t'>Event Deposit</b><span class='s'>TzKT · event · tz1Ke2h7…</span><span class='pln'>◔ planned: tezos.events</span>"]:::mich
  L["<b class='t'>Value Transfer</b><span class='s'>EVM node · transaction · 0x00…00</span>"]:::evm

  K -->|has code → queue| E
  K -->|Michelson · tz1| M
  K -.->|old kernel| L
  U -.->|initiates deposit| K

  class E indexed
  class M planned
  class F faded
  class L legacy
  class L faded
  class U faded
  class K faded

  classDef l1 fill:#e8836b1f,stroke:#e8836b,stroke-width:1.5px;
  classDef evm fill:#5aa6e01f,stroke:#5aa6e0,stroke-width:1.5px;
  classDef mich fill:#6fbf731f,stroke:#6fbf73,stroke-width:1.5px;
  classDef sys fill:#8b919c1f,stroke:#8b919c,stroke-width:1.5px;
  classDef indexed stroke-width:2px;
  classDef planned stroke-width:2px;
  classDef faded stroke-width:1.5px;
  classDef legacy stroke-width:1.5px;
  classDef actor fill:transparent,stroke:#e8836b,stroke-width:1.5px;

  linkStyle 0 stroke:#5aa6e0,stroke-width:2.4px;
  linkStyle 1 stroke:#5aa6e0,stroke-width:2.4px;
  linkStyle 2 stroke:#6fbf73,stroke-width:2.4px;
  linkStyle 3 stroke:#5aa6e0,stroke-width:2.4px,stroke-dasharray:6 5,opacity:0.55;
  linkStyle 4 stroke:#e8836b,stroke-width:2.4px,stroke-dasharray:6 5,opacity:0.55;

1 · Node HUE — classDef, applied inline

// fill = hue-bg (.12 alpha, 8-digit hex), stroke = hue
classDef l1   fill:#e8836b1f,stroke:#e8836b,stroke-width:1.5px;
classDef evm  fill:#5aa6e01f,stroke:#5aa6e0,stroke-width:1.5px;
classDef mich fill:#6fbf731f,stroke:#6fbf73,stroke-width:1.5px;
classDef sys  fill:#8b919c1f,stroke:#8b919c,stroke-width:1.5px;

NODE["…"]:::evm   // hue — inline on the node
⚠️ Pitfall: do not write color: in classDef. Mermaid turns it into the rule .class span{color … !important}, which overrides all per-line colors inside the node (handler/source turn white). Text color is set only by themeCSS (§3); classDef is the node's border/fill.

2 · DECISION — one line per node

class NODE indexed   // ✓ teal glyph + teal handler line
class NODE planned   // ◔ amber glyph
class NODE faded     // not indexed (opacity .45)
class NODE legacy    // dashed border; multiple lines per node allowed
⚠️ Pitfall: a comma in class is a list of NODES, not classes. class NODE evm,indexed reads as “class indexed for nodes NODE and evm” — the hue won't arrive. That's why hue is inline :::evm and decision is one line per class.

3 · Node dictionary — HTML label + themeCSS

NODE["<span class='glyph'></span><b class='t'>Event Withdrawal</b><span class='s'>EVM node · event · 0xff…02</span><span class='ix'>✓ etherlink.on_withdraw</span><span class='md'>✚ EtherlinkWithdrawOperation</span>"]:::evm

// lines: .t name · .s source (source · type · address) · .ix handler (teal) · .md model · .pln planned (amber)
// themeCSS (inside mermaid.initialize) — line colors:
.nodeLabel .t   { display:block; white-space:nowrap; font-weight:600; color:#eef0f3; padding-right:20px } // name
.nodeLabel .s   { display:block; white-space:nowrap; font-size:9.5px; color:#7c828e } // source
.nodeLabel .ix  { display:block; font-size:9.5px; color:#3ed6c0 }   // handler (teal)
.nodeLabel .md  { display:block; font-size:9.5px; color:#7c828e }   // model
.nodeLabel .pln { display:block; font-size:9.5px; color:#d9a83c }   // planned (amber)
.nodeLabel .glyph { position:absolute; top:-2px; right:0; font-size:17px; font-weight:700 }
.node.indexed .nodeLabel .glyph { color:#3ed6c0 }  // ✓ teal
.node.planned .nodeLabel .glyph { color:#d9a83c }  // ◔ amber
.node.faded   { opacity:.45 }
.node.legacy rect { stroke-dasharray:5 4 }
Node lines do not wrap. white-space:nowrap on every line + keeping them short: the node grows wider, not taller, and doesn't break the layout. source — strictly source · type · address, nothing more. The trailing name (method, event, entrypoint, notes like input=0x / 4-arg / → receiver) lives in the title and is not repeated in source: it only widens the node. The shorter the source line, the narrower the node — and node width, multiplied across side-by-side columns, is what drives the whole diagram's width.
Address — hash or alias, no strict rule. Both read fine: a truncated hash (0xff…01, tz1Ke2h7…) or a role alias (rollup, gateway, tez-ticketer). A hash earns its place when it distinguishes several similar contracts (0xff…01 XTZBridge vs 0xff…02 FABridge); an alias is clearer when there's a single well-known contract and the hash would distinguish nothing. Multi-word aliases are hyphenated.
Title stays short. Forms: Call Contract.method, Event Name, or a role label (Transfer Ticket, Value Transfer, Rollup cement). No runtime prefix (Tezos … — the hue already encodes the runtime) and no parenthetical qualifiers ((mint) / (burn) — disambiguation belongs in the source address).
No stray "why / when" lines. A node says what is indexed. Free-text notes about conditions or phase (only if code present, Block 3, proxy specified?, bridge.rs:401-507) don't read at node size and just clutter — if something needs explaining, extend the graphic, not the node. This isn't a ban on conditions or roadmap: recurring structural facts get a defined encoding here first (a hue, a glyph, a dedicated line class — the way roadmap is already carried by color + status glyphs), and only then appear in nodes. Until it's in this standard, it doesn't go in a node.

4 · ARROW — linkStyle by declaration order

linkStyle 0 stroke:#5aa6e0,stroke-width:2.4px;                                   // evm
linkStyle 3 stroke:#5aa6e0,stroke-width:2.4px,stroke-dasharray:6 5,opacity:.55; // legacy
Mermaid trade-off: the condition label on the arrow stays neutral gray (per-label color matching the runtime hue isn't available out of the box). A deliberate concession for the mermaid medium.

4.1 · Arrow labels — what value moves

A -->|xtz| B                // xtz moves along the edge — type only, from/to obvious from nodes
A -->|ticket| B             // ticket moves along the edge — same rule
A -->|User → Helper| B      // token transfer: B is a `Call Token.transfer` node — write from → to, no `Token:` prefix
A -->|mint → User| B        // ERC-20 mint: B is `Call Token.mint`
A -->|User → burn| B        // ERC-20 burn: B is `Call Token.burn`
A -->|approve Ticketer| B   // approve: B is `Call Token.approve`, write target spender
A -->|NAC| B                // system routing labels stay as short action words
xtz / ticket — type only, no from/to. When xtz or a ticket moves along the edge, just write xtz or ticket — node positions already say who sends and who receives. Tokens, by contrast, always materialize as a dedicated Call Token.* node (transfer / mint / burn / approve); the arrow leading INTO that call carries from → to with no Token: prefix — already implicit from the call name. System and routing labels (NAC, SP sees commitment, with proxy → queue) stay as short action words.

5 · Actor — capsule (stadium node)

SP(["Service Provider"]):::actor
class SP faded
classDef actor fill:transparent,stroke:#e8836b,stroke-width:1.5px;

6 · Init — canonical block (extracted into mermaid-init.js)

import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({
  startOnLoad:false, securityLevel:'loose', theme:'base',
  themeCSS,                                  // the CSS string from §3
  themeVariables:{
    fontFamily:'JetBrains Mono, monospace', fontSize:'12px',
    lineColor:'#565c67', clusterBkg:'transparent',
    clusterBorder:'#1e212a', edgeLabelBackground:'#0e1014',
  },
  flowchart:{ htmlLabels:true, curve:'basis', nodeSpacing:42, rankSpacing:58, padding:10, useMaxWidth:false },
});
await mermaid.run({ querySelector:'.mermaid' });