Aave V3
One immutable meta-adapter that reports an account's lending positions across any Aave V3 instance (Core, Prime, an L2 market, …).
One immutable meta-adapter that reports an account's lending positions across any Aave V3 instance (Core, Prime, an L2 market, …). Each governed instance is identified by the instance's ProtocolDataProvider address; within an instance, reserves are enumerated trustlessly via getAllReservesTokens(). Emits up to two legs per reserve (Supply/Collateral + Borrow).
Type: Meta-adapter
protocolSubId:
keccak256("aave-v3-lending")(brandaave, ecosystemaave-v3)Instance key: the instance's ProtocolDataProvider address, widened to
bytes32
Positions returned
Up to two legs per registered reserve, per instance (one supply-side leg + one borrow leg):
Supply
Supplied
false
false
aToken balance when usageAsCollateralEnabled == false
Collateral
Collateral
false
false
aToken balance when usageAsCollateralEnabled == true
Borrow
Borrowed
true
false
Variable + stable debt, summed — one Borrowed leg per reserve, emitted only when the total is > 0
Reserves not registered on NAVCalculator are skipped; assetFilter is respected. But if the account borrows a reserve whose asset is unregistered, the instance reports nothing at all — every supply and collateral leg included — rather than emitting credit legs with no offsetting debt, which would over-report NAV. Which reserves the account actually borrows is read from Aave's user-configuration bitmap (getUserConfiguration), so only those are checked against the registry rather than scanning every reserve on each read; if the bitmap is unavailable the adapter falls back to a full scan. See Never report collateral without its debt.
Balance calculation
Per instance, the ProtocolDataProvider (_toAddress(instance)) is queried. For each reserve from getAllReservesTokens():
Variable and stable debt are summed into a single Borrowed leg per reserve. Emitting them as two legs would collide on the identity key — both would share the same (chainId, protocolSubId, positionId, balanceAsset.asset, positionKind) tuple. Aave has deprecated new stable borrows, but legacy stable debt can still persist, so the adapter folds whatever stable debt remains into the variable amount.
The instance Pool address (used in positionId) is resolved via dp.ADDRESSES_PROVIDER().getPool().
Identity
positionId:
abi.encode(address pool)— one position per instance Pool; the reserve each leg refers to is carried inbalanceAsset, not thepositionIdpositionKind:
Supplied·Collateral·Borrowedlabels: there is no
labelsfield on the position. The adapter implementspositionLabels(positionId), which returns["Market", "<market id>"](the instance's on-chain market id, e.g. distinguishing Aave Core from Aave Lido) — surfaced only by the verbose reads.shape enumeration:
positionIds()advertises the Pool for each configured instance — the id the legs actually emit — not theProtocolDataProvidercoordinate. The two differ only here, and getting it wrong would be invisible to a label check:positionLabelsreturns the same["Market", …]for either address, so only a byte-exact join against real positions catches the mismatch._instancePositionsresolves the Pool back out of the same hook, so the emitted and advertised ids cannot drift apart.
Constructor
navCalculator_
NAVCalculator address (must be a contract); source of the instance set and asset registry
Registration
Registered with addMetaBalanceAdapter(adapter, instances) (register + seed); instances adjusted with addMetaInstances / removeMetaInstances. Each instance coordinate is a ProtocolDataProvider address widened to bytes32. One contract covers every Aave V3 market — adding a market is an addMetaInstances call, not a new deploy. A malformed coordinate is isolated per-instance by the fan-out.
Last updated