# DefiDash dashboard guide for AI assistants

DefiDash: DeFi portfolio dashboards built on per-wallet Prometheus metrics.
This guide describes how dashboards are built so an assistant can create or edit one for the user, through the DefiDash connector (MCP, `/api/v1/mcp`) or the in-page API `window.defidash.agent`.

## Workflow

1. Read the user's request and decide which panels it needs.
2. Call get_context to see the user's wallets, networks, holdings and existing dashboards.
3. If the request is about a protocol that is not in holdings.protocols, tell the user their wallets have no position there and the panels will be empty, and ask whether to build it anyway (for example, to watch a position they plan to open).
4. Call search_metrics with a protocol or topic (e.g. 'aave', 'health factor', 'uniswap fees') to get real metric names, units and labels. Metric names are never guessed.
5. Call preview_panel on the main panels before adding them; if they come back empty, say so instead of presenting an empty dashboard as done.
6. Call create_dashboard (new) or apply_changes (existing) with a list of ops. Changes land in the dashboard's draft, which the user sees in the editor and can undo.
7. Read the returned summary and warnings; fix anything flagged with another apply_changes.
8. Publish only when the user explicitly asks to publish (publish needs confirm: true).

## Panel types

| type | use for | default size (w×h) |
| --- | --- | --- |
| `stat` | One big number (optionally with a sparkline). Net worth, debt, health factor, APY. | 6×4 |
| `gauge` | A number on a dial with coloured bands. Health factor, LTV, utilisation. | 6×5 |
| `bargauge` | Horizontal bars, one per series. Composition: collateral by asset, value by protocol. | 12×7 |
| `timeseries` | Lines over the selected time range. Net worth, debt vs collateral, prices over time. | 24×8 |
| `table` | Rows of labelled values. Positions, tokens, per-wallet breakdowns. | 24×8 |
| `text` | Markdown notes and headings. No queries. | 24×3 |

24 columns. Panels fill left to right and wrap; rows group panels under a heading.

## Units

- `currencyUSD`: US dollars, e.g. $12.3K
- `percent`: 0-100 shown as a percentage
- `percentunit`: 0-1 shown as a percentage (0.25 -> 25%)
- `none`: plain number (health factors, counts, token amounts)
- `short`: plain number with K/M/B suffixes
- `dateTimeAsIso`: Unix seconds shown as a date
- `s`: duration in seconds
- `ms`: duration in milliseconds
- `dtdurations`: duration shown as days/hours

Threshold colours: green, yellow, orange, red, blue, purple, text, or a #hex colour.

## Queries

- Filter every wallet-scoped metric by the dashboard variables: {wallet=~"$wallet",network=~"$network"}. Missing $wallet/$network variables are added automatically.
- Aggregate across wallets and networks with sum(...) for USD values and min(...) for health factors; use sum by (label) (...) for one series per label (asset, protocol_name, symbol, wallet).
- Legends may use label placeholders, e.g. legend "{{symbol}} · {{network}}".
- Stat, gauge, bargauge and table panels read the current value (instant); timeseries panels read history over the dashboard's time range. Override per query with instant.
- Account scoping is applied by the server: a query only ever sees the signed-in user's own wallets.
- EVM metrics are prefixed defidash_eth_ (they also cover arb, base, op, polygon, bsc via the network label); Solana metrics are prefixed defidash_sol_.
- Ratios ending in _ratio are 0-1; show them with unit percentunit.

## Variables

- wallet and network are system variables: the viewer picks among their own wallets and networks. They are created automatically when a query uses $wallet or $network.
- custom: a fixed list of choices (e.g. 7d,30d,90d); query: choices from label_values(metric{...}, label); defidash-number: a numeric input (e.g. a target in USD) used as $name in queries.
- textbox variables are not supported; use defidash-number instead.

## Ops

Changes are a list of ops applied in order, all or nothing. A panel is referenced by its numeric id, by a `ref` given to it earlier in the same request, or by its exact title.

- `set_dashboard {title?, description?}`
- `add_panel {type, title, queries[{expr, legend?, instant?}], unit?, decimals?, min?, max?, thresholds?[{value, color}], no_value?, description?, width?, height?, row?, ref?, content? (text only), options? (raw Grafana options)}`
- `update_panel {panel, …any add_panel field, type?}`: only the given fields change
- `remove_panel {panel}`: removing a row removes its panels
- `move_panel {panel, row? ("top" for top level), before?, width?, height?}`
- `add_row {title, collapsed?, ref?}`
- `add_variable {name, type: custom|query|defidash-number, label?, query?, options?, default?, multi?, include_all?, min?, max?}`
- `remove_variable {name}`

Limits: ops per request 50, queries per panel 8, panels per dashboard 70, dashboard json bytes 1048576.

## Metric families

Search the catalog for exact names (`search_metrics`, or `GET /api/v1/agent/metrics?q=`).

| family | metrics | networks | examples |
| --- | --- | --- | --- |
| any-protocol | 13 | arb, base, bsc, eth, op, polygon, sol | `defidash_eth_protocol_asset_usd_value`, `defidash_eth_protocol_debt_usd_value`, `defidash_eth_protocol_info`, `defidash_eth_protocol_net_usd_value` |
| wallet-wide | 33 | arb, base, bsc, eth, op, polygon, sol | `defidash_eth_debt_total_usd`, `defidash_eth_protocol_count`, `defidash_eth_protocols_total_usd`, `defidash_eth_total_usd` |
| aave | 27 | arb, eth | `defidash_eth_aave_available_borrow_usd`, `defidash_eth_aave_borrow_apy_ratio`, `defidash_eth_aave_borrowed_usd`, `defidash_eth_aave_collateral_approx_liquidation_distance_all_ratio` |
| aerodrome | 8 | arb, eth | `defidash_eth_aerodrome_in_range_positions_count`, `defidash_eth_aerodrome_out_of_range_positions_count`, `defidash_eth_aerodrome_position_tick_spacing`, `defidash_eth_aerodrome_position_uncollected_fees_usd` |
| compound_iii | 6 | arb, eth | `defidash_eth_compound_iii_borrowed_usd`, `defidash_eth_compound_iii_market_count`, `defidash_eth_compound_iii_net_usd`, `defidash_eth_compound_iii_position_borrowed_usd` |
| curve | 5 | arb, eth | `defidash_eth_curve_net_usd`, `defidash_eth_curve_pool_count`, `defidash_eth_curve_position_balance_usd`, `defidash_eth_curve_position_lp_balance` |
| etherfi | 4 | arb, eth | `defidash_eth_etherfi_balance_usd`, `defidash_eth_etherfi_eeth_equivalent`, `defidash_eth_etherfi_eth_price_usd`, `defidash_eth_etherfi_weeth_balance` |
| euler | 8 | arb, eth | `defidash_eth_euler_borrowed_usd`, `defidash_eth_euler_net_usd`, `defidash_eth_euler_position_borrow_apy_ratio`, `defidash_eth_euler_position_borrowed_usd` |
| fluid | 8 | arb, eth | `defidash_eth_fluid_borrow_usd`, `defidash_eth_fluid_net_usd`, `defidash_eth_fluid_position_borrow_amount`, `defidash_eth_fluid_position_borrow_usd` |
| gmtrade | 10 | sol | `defidash_sol_gmtrade_glv_apr_ratio`, `defidash_sol_gmtrade_glv_apy_ratio`, `defidash_sol_gmtrade_glv_balance`, `defidash_sol_gmtrade_glv_fees_7d_usd` |
| gmx | 5 | arb, eth | `defidash_eth_gmx_collateral_usd`, `defidash_eth_gmx_notional_usd`, `defidash_eth_gmx_position_collateral_usd`, `defidash_eth_gmx_position_count` |
| hyperliquid | 24 | hl | `defidash_eth_hyperliquid_net_usd`, `defidash_eth_hyperliquid_vault_apr_ratio`, `defidash_eth_hyperliquid_vault_count`, `defidash_eth_hyperliquid_vault_equity_usd` |
| kelp | 3 | arb, eth | `defidash_eth_kelp_balance_usd`, `defidash_eth_kelp_rseth_balance`, `defidash_eth_kelp_rseth_price_usd` |
| morpho | 12 | arb, eth | `defidash_eth_morpho_borrowed_usd`, `defidash_eth_morpho_market_positions_count`, `defidash_eth_morpho_min_health_factor`, `defidash_eth_morpho_net_usd` |
| pancakeswap_v3 | 8 | arb, eth | `defidash_eth_pancakeswap_in_range_positions_count`, `defidash_eth_pancakeswap_out_of_range_positions_count`, `defidash_eth_pancakeswap_position_fee_tier_ratio`, `defidash_eth_pancakeswap_position_uncollected_fees_usd` |
| pendle | 6 | arb, eth | `defidash_eth_pendle_market_count`, `defidash_eth_pendle_net_usd`, `defidash_eth_pendle_position_lp_valuation_usd`, `defidash_eth_pendle_position_pt_valuation_usd` |
| stablecoin-vaults | 4 | arb, eth | `defidash_eth_erc4626_vault_balance_usd`, `defidash_eth_erc4626_vault_shares_amount`, `defidash_eth_erc4626_vault_underlying_amount`, `defidash_eth_erc4626_vault_underlying_usd_price` |
| symbiotic | 4 | arb, eth | `defidash_eth_symbiotic_balance_usd`, `defidash_eth_symbiotic_position_balance_usd`, `defidash_eth_symbiotic_position_underlying_amount`, `defidash_eth_symbiotic_vault_count` |
| uniswap | 33 | arb, eth | `defidash_eth_uniswap_in_range_positions_count`, `defidash_eth_uniswap_out_of_range_positions_count`, `defidash_eth_uniswap_position_closing_timestamp`, `defidash_eth_uniswap_position_current_price_token1_per_token0` |
| usual | 6 | arb, eth | `defidash_eth_usual_balance_usd`, `defidash_eth_usual_floor_price`, `defidash_eth_usual_usd0_balance`, `defidash_eth_usual_usd0_usd` |

## Examples

### Aave health and debt

Request: "Aave on Arbitrum: health factor big, debt and collateral over time, positions table"

```json
[
 {
  "op": "set_dashboard",
  "title": "Aave health"
 },
 {
  "op": "add_panel",
  "type": "gauge",
  "title": "Health factor",
  "queries": [
   {
    "expr": "min(defidash_eth_aave_health_factor{wallet=~\"$wallet\",network=~\"$network\"})",
    "legend": "Lowest HF"
   }
  ],
  "unit": "none",
  "min": 0,
  "max": 5,
  "no_value": "No active position",
  "thresholds": [
   {
    "value": null,
    "color": "red"
   },
   {
    "value": 1.2,
    "color": "orange"
   },
   {
    "value": 1.5,
    "color": "yellow"
   },
   {
    "value": 2,
    "color": "green"
   }
  ]
 },
 {
  "op": "add_panel",
  "type": "stat",
  "title": "Borrowed",
  "queries": [
   {
    "expr": "sum(defidash_eth_aave_borrowed_usd{wallet=~\"$wallet\",network=~\"$network\"})"
   }
  ],
  "unit": "currencyUSD"
 },
 {
  "op": "add_panel",
  "type": "stat",
  "title": "Supplied",
  "queries": [
   {
    "expr": "sum(defidash_eth_aave_supplied_usd{wallet=~\"$wallet\",network=~\"$network\"})"
   }
  ],
  "unit": "currencyUSD"
 },
 {
  "op": "add_panel",
  "type": "timeseries",
  "title": "Debt vs collateral",
  "queries": [
   {
    "expr": "sum(defidash_eth_aave_supplied_usd{wallet=~\"$wallet\",network=~\"$network\"})",
    "legend": "Supplied"
   },
   {
    "expr": "sum(defidash_eth_aave_borrowed_usd{wallet=~\"$wallet\",network=~\"$network\"})",
    "legend": "Borrowed"
   }
  ],
  "unit": "currencyUSD"
 },
 {
  "op": "add_panel",
  "type": "table",
  "title": "Positions",
  "queries": [
   {
    "expr": "sum by (asset, side, network) (defidash_eth_aave_position_balance_usd{wallet=~\"$wallet\",network=~\"$network\"})"
   }
  ],
  "unit": "currencyUSD"
 }
]
```

### Portfolio overview

Request: "Total value across EVM and Solana, value by protocol, history"

```json
[
 {
  "op": "add_panel",
  "type": "stat",
  "title": "Total value",
  "queries": [
   {
    "expr": "sum(defidash_eth_total_usd{wallet=~\"$wallet\",network=~\"$network\"}) + sum(defidash_sol_total_usd{wallet=~\"$wallet\",network=~\"$network\"})"
   }
  ],
  "unit": "currencyUSD",
  "width": 8
 },
 {
  "op": "add_panel",
  "type": "bargauge",
  "title": "By protocol",
  "queries": [
   {
    "expr": "sum by (protocol_name) (defidash_eth_protocol_net_usd_value{wallet=~\"$wallet\",network=~\"$network\"})",
    "legend": "{{protocol_name}}"
   }
  ],
  "unit": "currencyUSD",
  "width": 16
 },
 {
  "op": "add_panel",
  "type": "timeseries",
  "title": "EVM value over time",
  "queries": [
   {
    "expr": "sum(defidash_eth_total_usd{wallet=~\"$wallet\",network=~\"$network\"})",
    "legend": "EVM"
   }
  ],
  "unit": "currencyUSD"
 }
]
```

### Edit an existing dashboard

Request: "Replace the positions table with bars and make the health factor wider"

```json
[
 {
  "op": "update_panel",
  "panel": "Positions",
  "type": "bargauge"
 },
 {
  "op": "update_panel",
  "panel": "Health factor",
  "width": 12
 }
]
```

## Tools

The same tools exist on the MCP server and on `window.defidash.agent`:

- `get_guide`: this document
- `get_context`: the user's wallets, networks, dashboards and plan limits
- `search_metrics {query?, protocol?, network?}`: metric names, units, labels
- `get_dashboard {dashboard_id}`: compact summary of a dashboard's draft
- `create_dashboard {title, ops?}`: new private dashboard with an optional first set of ops
- `apply_changes {dashboard_id, ops, dry_run?}`: edit the draft
- `preview_panel {dashboard_id, panel}`: run one panel's queries and return values
- `discard_draft {dashboard_id}`: throw the draft away
- `publish {dashboard_id, confirm: true}`: publish the draft; only on the user's explicit request
