Materializes the Clear protocol state into PostgreSQL from the pipeline's decoded logs: reserves, IOU tokens, Curve StableSwap-NG pools, all user balances, and the full swap/liquidity history.
Everything is derived purely from events (an exporter plugin gets no RPC access), so:
- balances are exact only when indexing starts at or before each contract's first event;
- delivery is at-least-once, but a processed-event ledger makes writes exactly-once, so balance arithmetic never double-counts on a restart.
| Table | Content |
|---|---|
clear_reserves |
per reserve: kind (base/meta), lp_supply, cumulative total_deposits/total_withdrawals, iou_minted/iou_redeemed, swap_count |
clear_reserve_lp_balances |
(reserve, holder) → balance — every LP holder |
clear_reserve_assets |
reserve's underlying assets + their IOU token (from AssetAdded) |
clear_reserve_swaps |
depeg Swap history (amounts + IOU split) |
clear_reserve_activity |
deposits, withdrawals, single-asset ops, rebalances, IOU mint/redeem, flash loans |
clear_iou_tokens / clear_iou_balances |
IOU supply and per-holder balances |
clear_curve_pools |
per pool: lp_supply, swap_count |
clear_curve_lp_balances |
(pool, holder) → balance |
clear_curve_swaps |
TokenExchange / TokenExchangeUnderlying history |
clear_curve_liquidity |
add/remove liquidity history (token amounts as JSON, token_supply) |
Amounts are NUMERIC (uint256-safe); addresses are stored lowercased.
Build with the same Go toolchain and module versions as the EVMI server:
go build -buildmode=plugin -o clear-defi.so ./examples/exporters/clear-defi(Or let EVMI build it from source: install a Plugin pointing at this git repo / path.)
-
ABIs — create one
EvmJsonAbiper contract type. The plugin classifies contracts by ABIContractName(case-insensitive substring), so name them so they contain:Base+Reserve→ e.g.ClearBaseReserve(must include ERC20Transfer+ the reserve events)Meta+Reserve→ e.g.ClearMetaReserveIOU→ e.g.ClearIOUCurve/StableSwap/Pool→ e.g.CurveStableSwapNG(includeTransfer,TokenExchange, and the liquidity events)
-
Blockchain, log store, pipeline — as usual.
-
Sources — add a source per contract (CONTRACT), or a FACTORY source on
ClearReserveFactory/ the Curve deployer with the right child ABI so new reserves/pools are picked up automatically. All sources must be in the same pipeline the exporter reads. -
Plugin — install this plugin (
InstallPlugin). -
Exporter — create an
EvmiExporterbound to the pipeline with config:{ "dsn": "postgres://user:pass@postgres:5432/clear?sslmode=disable", "autoMigrate": true }Start it; it processes the pipeline's logs in block order and fills the tables.
The
uint256[]args on Curve liquidity events require an EVMI server that serializes array ABI args (added alongside this example); older servers panic on them.
autoload.config.json in this folder wires the whole thing declaratively — pass it as the EVMI
server config and every resource below is created on startup if absent (idempotent):
- the four ABIs (named for the classifier), a blockchain (sepolia), a log store
(clickhouse), a pipeline (
clear), and the exporter (with its Postgresdsn); - the base reserve as a
FACTORYsource — it indexes the reserve's own events and auto-creates aClearIOUCONTRACTchild source for every IOU announced viaAssetAdded(factoryCreationAddressLogArg: "iou"); the meta reserve and a Curve pool are plainCONTRACTsources; - the plugin itself (via
plugins, from your git repo).
Replace the placeholders first (they're marked with 0x…/<...>): the RPC URL and key, the
reserve/pool addresses, each source's startBlock (set to the deployment block for exact
balances), the metadata + exporter Postgres DSNs, and the plugin gitUrl. Then:
go run ./cmd/evm-indexer start --config examples/exporters/clear-defi/autoload.config.json --instance evmi-1The ABI content fields are the minimal event-only ABIs the exporter needs (the indexed flags
match the contracts, which is how EVMI splits topics from data). If you add events, extend both the
ABI and a handler in handlers.go.
Reserve TVL proxy (LP supply) and IOU exposure:
SELECT address, kind, lp_supply, iou_minted - iou_redeemed AS iou_outstanding, swap_count
FROM clear_reserves ORDER BY lp_supply DESC;A user's positions across reserves and pools:
SELECT 'reserve' AS venue, reserve AS pool, balance FROM clear_reserve_lp_balances WHERE holder = $1 AND balance <> 0
UNION ALL
SELECT 'curve', pool, balance FROM clear_curve_lp_balances WHERE holder = $1 AND balance <> 0;Recent depeg swaps on a reserve:
SELECT block_number, trader, token_in, amount_in, token_out, amount_out, iou_total
FROM clear_reserve_swaps WHERE reserve = $1 ORDER BY block_number DESC, log_index DESC LIMIT 50;Curve pool volume by day-block bucket (swaps) and current LP supply:
SELECT p.address, p.lp_supply, count(s.*) AS swaps, sum(s.tokens_sold::numeric) AS volume_sold
FROM clear_curve_pools p LEFT JOIN clear_curve_swaps s ON s.pool = p.address
GROUP BY p.address, p.lp_supply;- Reserve on-chain token reserves (how much USDC/USDT a reserve physically holds) are not
reconstructed here — that needs the ERC20
Transfers of the underlying tokens to/from the reserve (index those tokens too if you want it) or periodic RPC snapshots. This plugin tracks LP supply, flows, IOU, and per-holder LP/IOU balances, which is what the events give exactly. - Curve LP
lp_supplyis tracked from the pool's ERC20Transfers (mint/burn); thetoken_supplyreported by each liquidity event is also stored per-row inclear_curve_liquidity. - If indexing starts mid-history, holders who received tokens before the start block can show negative balances — start at the deployment block for exact figures.