Aave V3 has been forked with the contracts essentially unchanged. That means
the replay work: scaled balances, index accrual, risk parameters, liquidation
extraction: applies to a fork once it stops assuming one set of addresses.
What a deployment is
A deployment names three contracts: the Pool (where user actions are
logged), the PoolConfigurator (where reserves and risk parameters are set)
and the oracle. Every service takes one. Nothing else changes.
Currently live: Aave V3 and Spark.
How the addresses were established
Not from a list. Several forks emit the identical ReserveInitialized event,
so we found every emitter of it and identified each by calling symbol() on
its first aToken: Spark’s is spDAI, “Spark DAI”. The Pool came from
aToken.POOL(), the oracle from ADDRESSES_PROVIDER().getPriceOracle().
rake aave:deployments:verify re-runs that check against the chain and fails
loudly on a mismatch, so a wrong constant can’t sit unnoticed.
Spark’s health panel, and what its coverage statement says
A liquidation event carries its own amounts, so the record stands on its own:
and where an asset’s price can’t be derived, the USD columns are blank rather
than estimated.
A health factor is different: it needs a price for every asset in a
position. Spark’s page now carries the same health panel sections as Aave’s
(the distribution, the accounts closest to liquidation, the trend, the
per-account composition), built by the same PanelBuilder against Spark’s own
Pool, reserves, risk parameters and oracle, on the same schedule (Saturdays for
the monthly cross-section, every morning for the daily one).
What makes it honest is the coverage statement. Of Spark’s priced assets
only a few use an oracle family we replicate exactly (a Chainlink proxy or a
capped stable feed); the rest are custom adapters. An account holding an
adapter-priced asset is stored as adapter oracle instead of being given an
approximate health factor, so the computed set is smaller than Aave’s by
construction, and the card says exactly how much smaller: “N of M accounts
computed; skipped: adapter oracle Z”, with the adapter count against the total
next to it. The statement is persisted with the run that produced it, and the
same lines open the CSV.
That is the general rule for forks: coverage follows the oracles, not the
lending logic, and the page states the coverage rather than leaving you to
infer it from an empty table.
Where Spark appears
Spark is a first-class deployment everywhere the pipeline reaches, not a
footnote on Aave’s pages:
- Its own tracking page and
liquidations page, with the realized and
configured bonus per event and the breakdown by collateral and month. - The
aave_liquidationsdataset withprotocol = spark, on the
Data Downloads form and over the API
(GET /liquidations?protocol=spark; the API reference
lists the parameter). Every query on the Aave-model tables filters on
protocol: the same asset is a reserve in both deployments with different
token addresses. - The MCP
get_liquidationstool, with the sameprotocolargument.
The aave_health dataset offers spark as a filter because the form is
generated from the deployment registry, but it has no Spark rows, for the
reason above: the panel is built only where the oracles are replicated.
An export filtered to Spark says zero rows rather than estimating any.
Adding another fork
- Find its PoolConfigurator by its
ReserveInitializedevents and confirm the
deployment by callingsymbol()on an aToken. - Register it in
Aave::Deploymentand verify with the rake task. - Sync reserves, risk parameters and oracle sources for that deployment
(the nightly reference-data job covers every registered deployment). - Validate prices against its oracle before publishing anything derived
from them. - Extract liquidations and add the deployment to the nightly schedule in
config/recurring.yml, twenty minutes clear of its neighbours. - Add a monthly panel entry (
RefreshAaveHealthPanelJobwith the deployment
key, on its own morning); the daily job covers every registered deployment
without a schedule change. The coverage statement then says what the
oracles allow, whatever that is.
Reserves, parameters and oracle sources are all stored per deployment. The same
asset is a reserve in several of them with different token addresses, so every
lookup is scoped: a fork’s aToken must never answer for Aave’s.