A lending protocol writes the record a risk engine runs on: every supply, borrow, repay, withdrawal, and liquidation, block by block. SQD serves that record as decoded logs, with traces and state diffs alongside, from genesis to head across 225+ networks. You compute the health factors and the scores; SQD provides the inputs.
Every position a lending protocol tracks is written to its log stream: each supply, borrow, repay, and withdrawal, and the liquidation that closes a failing one, block by block. Feeding a risk model the naive way means paginating that history over eth_getLogs against per-provider block-range and result-size caps, standing up an archive node for blocks old enough to backtest against, then decoding each event's data words and per-token decimals before a single health factor is computed. It is all onchain already; the cost is not in the model but in reading the record back cleanly, per block.
A risk engine does not consume a vendor's risk score; it consumes those raw fields, per block, and computes its own.
Here is a real one, an Aave v3 LiquidationCall from Ethereum block 25,488,542 (2026-07-08): a keeper repaid 774.53 USDT of a failing position's debt and seized 0.4697 WETH of its collateral. The three indexed topics identify the assets and the borrower; the four data words carry the amounts, the keeper, and the payout mode:
Real, from transaction 0x6241…a8ea at block 25,488,542 (2026-07-08 14:47:35 UTC), emitted by the Aave v3 Pool 0x8787…A4E2. Field layout from the Aave v3 IPool interface.
One gotcha: the liquidator and both amounts ride in data, not in topics. You can filter by collateral asset, debt asset, or borrower, because those are indexed, but finding every liquidation executed by a given keeper means decoding data on your side. And each amount is a raw integer in its own token's decimals: here the debt is 6-decimal USDT and the collateral is 18-decimal WETH, a 10^12 gap that a single-decimals assumption misreads completely.
The topic0 is keccak256 of the event signature, LiquidationCall(address,address,address,uint256,uint256,address,bool). The position events ride the same stream under their own signatures: Supply, Borrow, Repay, Withdraw, and ReserveDataUpdated with the pool's updated rates and indices. Another Aave v3 deployment is the same query with the dataset name and pool address swapped.
The subscription is two filter lines: the pool's address and the event's topic0. Pinned to one block, it returns exactly the row below; pointed at the chain head, it is the live feed your alerting runs on:
History runs to genesis through the same query as live data, so a risk model can be tested against every past liquidation rather than a curated sample. Pin a stress window: the 48 hours from 2025-10-10 00:00 UTC, resolved by timestamp to Ethereum blocks 23,543,616 through 23,557,920. The query above, with only the block range changed, returns 902 LiquidationCall events from the Aave v3 Pool:
One gotcha: a single POST does not return the whole window. The stream ends at a dataset chunk boundary (on the order of a thousand blocks here), not at your toBlock; the 14,305-block window above came back in 13 sequential responses, and the client resumes each one from the last block header received plus one. By default the stream also returns only blocks with matching data, plus sparse boundary headers, so the quiet stretches between liquidations cost almost nothing to read. The same resume loop, pointed at the chain head, is the alerting feed.
Running the same backfill against an RPC node means eth_getLogs, and eth_getLogs is capped: public endpoints enforce block-range and result-size limits that turn a year of history into a pagination loop over provider-specific windows, and blocks this old need an archive node behind the endpoint. The caps are measured, verbatim, in the eth_getLogs guide. On the Portal the filter is the whole integration: a dataset name, a pool address, a topic0, on any of 225+ networks.
Live queries against real blockchain data. Pick a network, choose a query, hit run.
The public endpoint is free for development. See plans for dedicated portals and higher limits.
Filter any lending pool by contract address and event signature and stream every LiquidationCall with borrower, collateral asset, debt asset, and both amounts, decoded against the protocol ABI. The same filter shape reads Aave v3, Compound, Morpho, and any EVM lending fork.
Supply, Borrow, Repay, and Withdraw events carry every position change; ReserveDataUpdated carries the pool's updated rates and indices with each interaction. Fold them into per-borrower positions in your own store: the Portal serves the events, not a reconstructed balance.
A liquidation is one transaction with a tree of internal calls underneath: the repayment, the collateral seizure, the flash loan a keeper wrapped around both. Traces expose that call tree; state diffs expose the storage writes that emit no event at all.
The same filter that replays years of liquidation history for a backtest streams new blocks at the head as NDJSON over plain HTTP. There is no separate historical and real-time integration; the block range is the only thing that changes.
The liquidations, position events, traces, and state diffs are all onchain already; the work a provider saves is in reading them back cleanly, per block, across every protocol and chain.
See this on your own contracts
Tell us the chains and contracts you care about, and we will show you the query that returns them.
eth_getLogs is the standard way to read events, and the first wall teams hit. The block-range and result caps that force pagination, the archive requirement, and a real block where eth_getLogs omits logs the receipt returns.
What an internal transaction is, why event logs and the transaction list miss it, what a trace record contains, and why traces are one of the most expensive things to pull from an RPC node.
Swaps, liquidations, and the internal calls a log-only feed misses.
Decoded events and traces streamed into the warehouse you already run.
Pre-decoded onchain data for agents, over MCP or plain HTTP.
Address-keyed history: transfers, native and internal value, NFT moves.
Traces and state diffs, the execution footprint logs alone never show.
Private Portal. Dedicated. Validated. Managed. Tell us what you're building, we'll show you what it looks like on SQD.