Skip to content

Graph — Flow view

The flow view draws a trace: value moving between entities, left to right through ordered lanes — networks, stages, jurisdictions. Ties are directed, may repeat between the same pair (a swap there and back, a deposit and a withdrawal), and carry value (width), category (color), certainty (dash) and a label. It is the view for money trails, supply chains and address investigations.

⚠️ Experimental. Like the rest of Graph, the flow view is outside the 1.0 stability guarantee. This release lays nodes out from hints in the data; automatic in-lane layout, aggregation and collapsible groups come next.

The demo below follows funds as they move: income from a validator is swapped, bridged and moved through five networks, then spent on a token crowdfunding — and the trace goes on to show where the sale money went. Read it left to right: lanes are networks, line color is the asset, width its USD value, a dashed line an amount derived rather than read from transactions. Click the Crowdfunding node to fold the sale away and focus on the route that led to it (click again to bring it back); the legend's address poisoning item reveals a look-alike address that tried to pass as the team's.

Select a node or a tie: the chart reports it through onSelect, and the page fills the details panel — placed inside the card with the details option: beside the drawing on a wide screen, below it on a narrow one. The library draws, your page explains. The Crowdfunding node's open/close is page code in the same onSelect handler, switching a setFilter (collapsible groups built into the library come in the next release); the decoy starts hidden with hiddenLinkTypes: ['noise'].

Data

The same { nodes, links } contract as every Graph view. Hints on the nodes place them; any extra field passes through to onSelect and the tooltips untouched.

new RareCharts.Graph('#chart', {
  view: 'flow',
  lanes: [{ id: 'osmosis', label: 'Osmosis' }, { id: 'ethereum', label: 'Ethereum', weight: 3 }],
  laneBy: 'net',                                  // node field naming its lane
  nodeGroups: { tracked: { color: '#2c5d8f', label: 'tracked wallets' } },
  nodeShapes: { wallet: 'circle', contract: 'square', exchange: 'diamond', crowd: 'stack' },
  linkColorBy: 'asset',
  linkTypes: { ETH: { color: '#3d4a5c', label: 'ETH' } },
  linkWidth: { by: 'value', scale: 'sqrt', min: 1.6, max: 22 },
  linkDash: link => link.basis === 'derived',
  hulls: [{ id: 'owner', label: 'one key on five networks', members: n => n.cluster }],
  hiddenLinkTypes: ['noise'],                     // off until turned on in the legend
  details: '#details',                            // your panel, placed beside or below the drawing
  onSelect: ({ node, link }) => showDetails(node ?? link),
}).setData({
  nodes: [
    { id: 'osmo', label: 'Owner wallet', sub: 'osmo1uvkv…w7zs', group: 'tracked', kind: 'wallet', net: 'osmosis', row: 0 },
  ],
  links: [
    { from: 'osmo', to: 'swap-dex', asset: 'ATOM', value: 530, label: '61.38 ATOM', start: '2023-09' },
  ],
});

Placing nodes

  • lane (or the field named by laneBy) puts a node in a lane. Lanes split the width by weight, in the order given; a lane only the data mentions is appended.
  • row is the vertical slot: 0 is the main line, -1, -2 above, 1, 2 below. A node without a row takes the next free slot of its cell.
  • col splits a wide lane into sub-columns (0, 1, …).
  • x / y override both, as fractions 0..1 of the lane width and of the drawing height.

Ties

  • source / target (or from / to), plus any fields you need. Several ties between one pair fan out; bend adjusts a curve by hand.
  • Width comes from linkWidth — value through a square-root scale by default; a tie without a value gets the minimum width.
  • Color comes from linkColorBy (a field or a function) resolved against linkTypes; dash from the type or from linkDash(link).
  • Time: start / end (years, YYYY-MM or ISO dates). setTimeWindow(from, to) dims ties outside the window — and fades nodes whose ties are all outside — or hides them with timeWindowMode: 'hide'.

Options

Flow-specific options; the common Graph options (height, details, fullscreen, linkTypes, tooltips…) are on the Graph page.

Option Type Default Description
viewstring—'flow' turns the flow view on
lanesarray[][{ id, label, weight? }] in left-to-right order; weight sets a lane's share of the width
laneBystring | function'lane'Node field (or function) naming its lane
nodeGroupsobject—{ group: { color, label } } — node color and legend entry
nodeShapes / shapeByobject / string | function{} / 'kind'Shape per node kind: 'circle', 'square', 'diamond', 'stack', or an SVG path
linkColorBystring | function'type'Tie field (or function) whose value is looked up in linkTypes
linkWidthobject{ by: 'value', scale: 'sqrt', min: 1.6, max: 22 }Tie width from a value field; scale: 'linear' also available
linkDashfunction—link => boolean — dashed tie (e.g. a derived amount); a type's own dash wins
linkLabelsbooleantrueTie labels; also setLinkLabels()
hullsarray[][{ id, label, members, color? }] — a soft band through a group's nodes; members is a list of ids or node => boolean
hiddenLinkTypesarray[]Tie categories hidden at start; the legend shows them dimmed and turns them on
timeWindowModestring'dim'What setTimeWindow does to ties outside the window: 'dim' or 'hide'
onSelectfunction—({ node, link, event }) => void on selection; without it, a clicked tie pins its tooltip
minWidthnumber0Narrower drawings scroll sideways instead of crushing the lanes; a side details panel needs this much room next to it, or it moves below
nodeRadiusnumber18Node radius in the flow view

Methods

graph.setFilter(item => !item.noise);   // nodes and ties; a tie hides with either end
graph.setTimeWindow(null, '2024-01');    // open start, through January 2024
graph.select('osmo');                    // or select({ link: id }); clearSelection()
graph.setLinkLabels(false);
graph.setHulls([]);

Interaction

Select — click a node or a tie: its neighbourhood stays, the rest fades, and onSelect({ node, link, event }) fires; click the background or press Escape to clear. Without onSelect, clicking a tie pins its tooltip instead.

Keyboard — nodes are in the Tab order lane by lane; Enter or Space selects; → follows an outgoing tie, ← an incoming one, ↑ / ↓ move within a lane.

Legend — node groups and tie categories; click a category to hide or show its ties. Categories in hiddenLinkTypes start hidden. A node whose ties are all hidden is hidden with them.

Details — with details, the selected node or tie is explained in a panel inside the card: beside the drawing on a wide card, below it on a narrow one (see the Graph options).

Full screen — ⛶ in the footer opens the trace on the whole screen; Esc or ✕ closes it.

Narrow screens — set minWidth and the chart scrolls sideways instead of crushing the lanes; a details panel moves below the drawing.