Aave CLI Commands¶
Overview¶
The Aave CLI provides commands for managing Aave V3 markets and updating position data. The main command degenbot aave update fetches blockchain events, processes them, and synchronizes the database with current user positions (collateral and debt). The argv vocabulary is Rust-owned (ADR-051) — see the Rust CLI page.
Background: Aave V3 Architecture¶
Aave V3 uses scaled balances for tracking user positions:
aTokens represent collateral deposits, scaled by a liquidity index
vTokens (variable debt tokens) represent borrowed amounts, scaled by a borrow index
Scaling indexes grow over time as interest accrues, ensuring users earn/pay proportional interest
When events occur (supply, withdraw, borrow, repay, transfer), the database stores scaled balances. The actual balance at any point is calculated as:
actual_balance = scaled_balance * index
The system tracks events from:
Pool contract: Reserve configuration, E-Mode changes
aToken contracts: Collateral operations (Mint/Burn/Transfer)
vToken contracts: Borrow/repay operations (Mint/Burn)
GHO vToken: Special debt token with discount mechanism for GHO borrowing
GHO Token Support¶
GHO is Aave’s stablecoin with discounted borrowing based on stkAAVE holdings. The CLI tracks GHO positions with revision-specific logic:
GHO Tables¶
AaveGhoTokenTable: Discount token address and rate strategyAaveV3UsersTable.gho_discount: User’s discount percentage
Revision Differences¶
Revision 1: Uses
_accrueDebtOnAction()for discount accountingRevision 2: Uses
_get_discounted_balance()with enhanced balance calculationRevision 4+: Discount mechanism deprecated, uses different rounding for mint operations
GHO V4+ Rounding Differences¶
Critical: GHO V4+ uses different rounding than standard vTokens V4+:
Operation |
Standard vToken V4+ |
GHO vToken V4+ |
|---|---|---|
BORROW (mint) |
|
|
REPAY (burn) |
|
|
This difference exists because GHO deprecated the discount mechanism in revision 4, which changed the rounding requirements in the Pool contract.
When processing GHO BORROW events, the scaled amount must be pre-calculated
from the original borrow amount (extracted from the BORROW event) using
calculate_mint_scaled_amount() before being applied to the Mint event.
This rounding behaviour now lives in the Rust degenbot-aave-updater core
(see the Writer implementation section below); the Python
TokenProcessorFactory that previously exposed it was retired with the
rest of the Python enrichment/processing pipeline.
Offline Position Calculation¶
The degenbot aave update command is designed to rebuild a complete database of collateral and debt positions by retrieving chronological events from several Aave contracts and decoding the underlying amounts that are stored in smart contract storage. This event-driven approach allows for fast offline calculation of positions without making any RPC calls to query live contract state. Once the database is synchronized, all position calculations can be performed locally using the stored scaled balances and index values.
Commands¶
The command vocabulary is Rust-owned: degenbot-cli declares it over degenbot-cli-core’s aave arms. The authoritative flag/exit-code reference is the Rust CLI page; the domain behaviour below is unchanged.
degenbot aave update¶
Update positions for all active Aave V3 markets by processing blockchain events.
degenbot aave update [--chunk SIZE] [--to-block BLOCK]
Parameters¶
Parameter |
Default |
Description |
|---|---|---|
|
10,000 |
Maximum number of blocks to process per database commit |
|
|
Last block in update range. Format: |
Block Identifiers¶
Valid block tags: earliest, finalized, safe, latest, pending
Examples:
latest- Latest blocklatest:-64- 64 blocks before chain tip (default, ensures finality)safe:128- 128 blocks after last safe block12345678- Specific block number
Behavior¶
Identify active markets: Queries database for active Aave V3 markets on all chains
Determine update range: Starts from
last_update_block + 1for each marketProcess in chunks: Iteratively processes blocks up to
chunk_size, committing after each chunkTrack progress: Displays progress bar showing blocks processed
Skip up-to-date chains: If no new blocks exist since last update, skips that chain
Example Usage¶
# Update all active markets to 128 blocks before latest
degenbot aave update --to-block "latest:-128"
# Update using smaller chunks for slower machines
degenbot aave update --chunk 5000
# Update to a specific block for historical analysis
degenbot aave update --to-block "18900000"
degenbot aave position show¶
Show a user’s position in a specific Aave market.
degenbot aave position show <ADDRESS> [--market MARKET] [--chain-id CHAIN_ID]
Displays collateral and debt positions for the given address.
degenbot aave position risk / degenbot aave market show¶
Not part of the Rust console’s argv (the authoritative command list is the
Rust CLI page); only aave position show ships as a
position-inspection verb. The risk/health-factor and market-config domain
computations remain in the Rust degenbot-aave core.
degenbot aave activate¶
Activate the Aave V3 market for the session chain (Ethereum default 1 when no chain layer supplied a value).
degenbot aave activate [--chain-id CHAIN_ID]
Only activated markets are included in degenbot aave update runs.
degenbot aave deactivate¶
Deactivate an Aave market (positions not updated).
degenbot aave deactivate [--name MARKET]
Event Processing Details¶
Reserve Initialization (ReserveInitialized)¶
Tracked from PoolConfigurator contract. Creates new asset entry with:
Underlying ERC20 token address
aToken and vToken addresses
Token implementation revisions (for encoding compatibility)
Rust tables updated: erc20_tokens, aave_v3_assets
Reserve Data Update (ReserveDataUpdated)¶
Emitted from Pool when interest rates change. Updates:
liquidity_rate: Current supply rate (RAY precision)borrow_rate: Current variable borrow rate (RAY precision)liquidity_index: Index for converting collateral scaled balancesborrow_index: Index for converting debt scaled balanceslast_update_block: Block of last rate update
Rust table updated: aave_v3_assets
User E-Mode Set (UserEModeSet)¶
Emitted when user changes their efficiency mode category.
Rust table updated: aave_v3_users.e_mode
Scaled Token Mint (Mint)¶
Mint events originate from three sources, identified by comparing value and balanceIncrease:
if value == balanceIncrease: # _transfer - skip (BalanceTransfer handles this)
elif balanceIncrease > value: # _burnScaled - interest accrual
else: # _mintScaled - user action (supply/borrow)
User Operations:
Operation |
Trigger |
|---|---|
|
aToken _mintScaled |
|
aToken _burnScaled (interest > withdrawal) |
|
vToken _mintScaled |
|
vToken _burnScaled (interest > repayment) |
|
GHO vToken mint |
|
GHO vToken burn |
Processing:
_mintScaled:amount = value - balanceIncrease,amountScaled = ray_div(amount, index)_burnScaled: Addevent_valuedirectly as interestAll sources create user/position if needed
Rust tables: aave_v3_users, position tables, and aave_gho_tokens for GHO
Scaled Token Burn (Burn)¶
Burn events always follow _burn(amountScaled) storage reduction.
Processing:
Event
value=amount - balanceIncrease(net after interest)Reconstruct:
amount = event_value + balance_increaseConvert:
amountScaled = ray_div(amount, index)Subtract
amountScaledfrom position balanceDelete position if balance reaches zero
GHO vToken: Uses _process_gho_debt_burn() with revision-specific logic (1 or 2).
Balance Transfer (BalanceTransfer)¶
Only occurs for aTokens (collateral). Transfers scaled amount directly:
Decrements sender’s collateral balance
Increments recipient’s collateral balance (creates user/position if needed)
Deletes sender’s position if balance reaches zero
Rust tables updated: aave_v3_users, aave_v3_collateral_positions
Token Upgrade (Upgraded)¶
When aToken or vToken implementation changes:
Detects which token type (aToken or vToken)
Queries new implementation for revision number
Updates revision in
aave_v3_assets
Rust table updated: aave_v3_assets.a_token_revision or aave_v3_assets.v_token_revision
GHO Discount Token Updated (DiscountTokenUpdated)¶
Emitted when the discount token for GHO vToken changes.
Rust table updated: aave_gho_tokens.v_gho_discount_token
GHO Discount Rate Strategy Updated (DiscountRateStrategyUpdated)¶
Emitted when the discount rate strategy for GHO vToken changes. The strategy calculates discount percentages based on user’s GHO debt and discount token balances.
Rust table updated: aave_gho_tokens.v_gho_discount_rate_strategy
Error Handling & Validation¶
Pre-update Checks¶
Invalid block tag: Raises
ValueErrorifto_blocktag not in valid setFuture block: Raises
ValueErrorifto_blockahead of current chain tipNo new blocks: Logs message and skips chain if
start_block >= end_block
Event Processing Validation¶
Asset existence: Asserts asset exists in DB before processing events
User existence: Creates user entry if not found (for collateral operations)
Position tracking: Maintains invariant that position exists when processing operations
Non-negative balances: Asserts balances never go negative
Revision compatibility: Raises
ValueErrorfor unsupported aToken/vToken revisions
Balance Verification¶
After each block, performs balance checks on modified users:
Calls
scaledBalanceOf(address)on contractCompares against stored
balancevalueRaises assertion error on mismatch with detailed context
GHO Discount Verification:
Calls
getDiscountPercent(address)on GHO vToken contractCompares against stored
gho_discountvalue inAaveV3UsersTableRaises assertion error on mismatch with detailed context
Algorithm Details¶
Chunk Processing¶
The update processes blocks in chunks to limit memory usage and enable incremental commits:
while working_start_block <= last_block:
# Calculate end of current chunk (minimum of constraints)
working_end_block = min(
last_block,
working_start_block + chunk_size - 1,
market.last_update_block for each market (if ahead)
)
# Update all markets ready for this chunk
for market in markets_needing_update(start == market.last_update_block + 1):
update_aave_v3_market(start, end, market)
# Commit changes
for market in updated_markets:
market.last_update_block = working_end_block
session.commit()
# Advance to next chunk
working_start_block = working_end_block + 1
Market Selection Logic¶
A market is updated in a chunk if:
market.last_update_block is None(never updated), ORmarket.last_update_block + 1 == working_start_block(ready for next segment)
This allows markets at different block heights to be synchronized gradually.
Event Ordering¶
Events are processed in block number, then log index order:
sorted(all_events, key=operator.itemgetter("blockNumber", "logIndex"))
This ensures chronological processing within each block.
Token Revision Libraries¶
The code supports multiple Aave V3 token revisions. The wad/ray math (scaled-balance
multiplication with rounding) is Rust-owned (degenbot-aave::wad_ray_math,
exposed to the updater via degenbot-aave::updater::processors). The rounding
modes mirror the Solidity WadRayMathLibrary:
Rounding Mode |
Usage |
|---|---|
Default (no arg) |
Half-up rounding (revisions 1-3) |
|
Round down (aToken mint rev 4+, vToken burn rev 4+) |
|
Round up (aToken burn rev 4+, vToken mint rev 4+) |
Rounding Functions by Revision¶
All revisions: ray_div(a, b, rounding=...) with the rounding mode
(FLOOR / CEIL / half-up default) — the Rust processors module selects
the mode per token revision + operation.
Rounding.FLOOR- Round downRounding.CEIL- Round upDefault (no rounding arg) - half-up rounding
Rounding by Token Type and Operation¶
Token Type |
Revision |
Mint (Supply/Borrow) |
Burn (Withdraw/Repay) |
|---|---|---|---|
aToken |
1-3 |
|
|
aToken |
4+ |
|
|
vToken |
1-3 |
|
|
vToken |
4+ |
|
|
GHO vToken |
4+ |
|
|
⚠️ Important: GHO V4+ uses ray_div_floor for BORROW (mint), unlike standard vTokens V4+ which use ray_div_ceil.
Writer implementation¶
The Aave V3 writer is Rust-owned (degenbot-aave core crate). The per-market chunk loop, RPC fetch+decode, DB writes, the per-chunk transaction, and the on-chain-truth verification all live in the Rust core, driven from the degenbot aave update arm of the Rust console (degenbot-cli-core; the Python driver reaches the same work through the run_aave_update PyO3 seam). The former Python writer pipeline (update_aave_market, event_handlers._process_*, transaction_processor/operations_parser/token_processor, db_*.py, the verify_* Python invariants) was retired by the §4.2 cutover (task CZM7TI) after the Rust path was proven GREEN to the live chain tip with full verification. The Event Processing Details and Algorithm Details sections above describe domain behavior that remains accurate; the implementation now lives in rust/crates/integrations/degenbot-aave/.
Configuration¶
The command uses Web3 connections from the degenbot config file. Each active chain must have an RPC endpoint configured.
Required Config¶
[rpc]
1 = "https://mainnet.example.com" # Ethereum mainnet
# ... other chain IDs
Environment Variables¶
General¶
Variable |
Values |
Description |
|---|---|---|
|
|
Enable debug-level logging output |
|
|
Enable function call trace logging |
Dependencies¶
Database: Rust
degenbot-dbowner, consumed through the stabledegenbot.dbPython mirrorBlockchain: Web3.py for RPC calls
Math: Rust
degenbot-aave::wad_ray_mathfor scaled balance calculations with rounding mode support (the former Pythonaave/libraries/package was retired)Logging: the Rust console (
degenbot-cli) renders operator output; the Rust core emits throttledlog-level operator progress linesWriter: Rust
degenbot-aave-updatercore crate (the Python enrichment/processing pipeline was retired)
Solidity Reference¶
The CLI interacts with Aave V3 contracts. Key implementation details in rust/crates/integrations/degenbot-aave/src/updater/ + degenbot-aave::wad_ray_math:
Scaled Balance Pattern¶
// From ScaledBalanceTokenBase.sol
function _mintScaled(...) {
uint256 amountScaled = amount.rayDiv(index);
_mint(onBehalfOf, amountScaled.toUint128()); // Storage change
emit Mint(..., amount + balanceIncrease, balanceIncrease, index); // Event
}
Events are notifications only. Actual storage changes happen in _mint() and _burn() calls using amountScaled.
Rounding¶
Solidity uses half-up rounding: (a * b + HALF_RAY) / RAY
The Rust port (degenbot-aave::wad_ray_math) matches this exactly for correct balance synchronization. The processors module in degenbot-aave selects FLOOR, CEIL, or half-up rounding per token revision + operation.
Event Structure¶
Mint/Burn events include:
value: Emitted amount (not storage amount)balanceIncrease: Interest since last actionindex: Current liquidity/borrow index
CLI derives storage amounts: amountScaled = ray_div(user_amount, index)