First lending deposit: risk parameters and health factor
For a first lending deposit, I want to understand the receipt token and withdrawal path before adding debt. Supplying $200 of USDC is enough for this walkthrough.
I use Aave V3 on Arbitrum as the example and read the position through the Pool contract. The numerical risk parameters below are assumptions for the math; check the active reserve configuration before borrowing.
The deposit, aToken, and withdrawal
When you supply an asset to Aave, you receive aTokens in return—interest-bearing tokens that represent your deposit plus accrued interest. Your aUSDC balance grows every block as borrowers pay interest. When you withdraw, you burn aTokens and get back your original asset plus whatever interest accumulated.
The protocol pools deposits from all suppliers and lends them to borrowers who post collateral. The interest rate is algorithmic: it goes up when utilization (borrowed / total supplied) is high to attract more suppliers, and goes down when utilization is low. There is no fixed term. You can withdraw anytime as long as there is available liquidity in the pool.
flowchart LR
S["Supplier"] -->|"Deposits USDC"| P["Aave Pool"]
P -->|"Mints aUSDC"| S
B["Borrower"] -->|"Posts ETH collateral"| P
P -->|"Lends USDC"| B
B -->|"Pays interest"| P
P -->|"Interest accrues to aUSDC"| SThe risk parameters that matter
Every asset on Aave has a risk configuration that controls how it can be used as collateral. These numbers are set by Aave governance and vary by asset and chain. Here are the ones you need to understand before depositing anything.
Loan-to-Value (LTV)
LTV is the maximum percentage of your collateral’s value that you can borrow against. If ETH has an LTV of 80%, you can borrow up to $640 against $800 worth of ETH. This is the ceiling, not a target—borrowing near max LTV leaves little margin for price drops.
Liquidation threshold
This is the point where your position becomes eligible for liquidation. With an assumed liquidation threshold of 82.5%, if your borrowed amount exceeds 82.5% of your collateral value, anyone can liquidate part of your position. At the assumed 80% LTV, HF is only 0.825 / 0.80 = 1.03125. Borrowing less creates a larger buffer than relying on that narrow gap. Aave liquidation docs
Liquidation penalty
When you get liquidated, the liquidator gets a bonus (usually 5-10% depending on the asset). This means you lose more than just the amount needed to bring your position back to health. With a 5% bonus, repaying $50 of debt entitles the liquidator to $52.50 of collateral, before any protocol share of the bonus. The penalty applies to the liquidated debt, not automatically to the entire position.
Health factor
Health factor is the single number that tells you how close you are to liquidation:
Health Factor = (Collateral × Liquidation Threshold) / Total Borrows
- Above 1.0: not currently eligible for liquidation under this health-factor check.
- At 1.0: no buffer; avoid operating at the boundary.
- Below 1.0: eligible for liquidation, which may execute before you can react.
If you supply $800 of ETH (assumed liquidation threshold 82.5%) and borrow $320 of USDC:
HF = (800 × 0.825) / 320 = 2.0625
That is a comfortable buffer. If ETH drops 50%, your collateral is now $400:
HF = (400 × 0.825) / 320 = 1.03125
A further decline of about 2.9% reaches HF 1.0 under these fixed-debt assumptions.
Borrowing uses the current market configuration
The current Aave V3 Pool reference specifies variable borrowing with interestRateMode = 2. Older stable-rate discussions do not describe the current borrowing interface. A supply-only deposit does not select a borrow mode. Aave Pool reference
A $200 supply walkthrough
This is an example workflow, rather than a receipt-backed performance report:
- Confirm the network and USDC contract in the Aave interface.
- Enter 200 USDC and inspect the Pool spender.
- Approve the amount needed for the supply.
- Review the fee quote and submit the supply transaction.
- Check the received aToken and the transaction receipt.
Supplying without borrowing avoids debt liquidation. USDC issuer risk, protocol risk, and withdrawal liquidity still matter. Record the actual fee and displayed supply rate at the time of the run; this post does not establish either.
Checking position on-chain
I wanted to verify the deposit independently of the Aave UI. Aave V3’s Pool contract has a getUserAccountData function that returns all the key numbers in one call:
from web3 import Web3
rpc = Web3(Web3.HTTPProvider("https://arb1.arbitrum.io/rpc"))
# Aave V3 Pool on Arbitrum
POOL = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"
pool_abi = [
{
"inputs": [{"name": "user", "type": "address"}],
"name": "getUserAccountData",
"outputs": [
{"name": "totalCollateralBase", "type": "uint256"},
{"name": "totalDebtBase", "type": "uint256"},
{"name": "availableBorrowsBase", "type": "uint256"},
{"name": "currentLiquidationThreshold", "type": "uint256"},
{"name": "ltv", "type": "uint256"},
{"name": "healthFactor", "type": "uint256"},
],
"stateMutability": "view",
"type": "function",
}
]
pool = rpc.eth.contract(address=POOL, abi=pool_abi)
wallet = "0xYourWalletAddress"
data = pool.functions.getUserAccountData(wallet).call()
collateral = data[0] / 1e8 # Values are in base currency (USD) with 8 decimals
debt = data[1] / 1e8
available_borrow = data[2] / 1e8
liq_threshold = data[3] / 1e4 # Basis points
ltv = data[4] / 1e4
health_factor = data[5] / 1e18 # 18 decimals, max uint if no debt
print(f"Collateral: ${collateral:.2f}")
print(f"Debt: ${debt:.2f}")
print(f"Available to borrow: ${available_borrow:.2f}")
print(f"Liquidation threshold: {liq_threshold:.2%}")
print(f"LTV: {ltv:.2%}")
print(f"Health factor: {health_factor:.4f}" if debt > 0 else "Health factor: ∞ (no debt)")
With only a supply position and no borrows, the health factor comes back as the max uint256 value—effectively infinite. Debt is zero, available borrows reflect the LTV against my collateral.
Monitoring health factor with a script
If I add debt, I want the account read working first. This loop prints health factor every minute; it needs a running terminal and does not deliver an alert elsewhere.
import time
import sys
from web3 import Web3
rpc = Web3(Web3.HTTPProvider("https://arb1.arbitrum.io/rpc"))
POOL = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"
pool_abi = [
{
"inputs": [{"name": "user", "type": "address"}],
"name": "getUserAccountData",
"outputs": [
{"name": "totalCollateralBase", "type": "uint256"},
{"name": "totalDebtBase", "type": "uint256"},
{"name": "availableBorrowsBase", "type": "uint256"},
{"name": "currentLiquidationThreshold", "type": "uint256"},
{"name": "ltv", "type": "uint256"},
{"name": "healthFactor", "type": "uint256"},
],
"stateMutability": "view",
"type": "function",
}
]
pool = rpc.eth.contract(address=POOL, abi=pool_abi)
WALLET = "0xYourWalletAddress"
HF_WARNING = 1.5 # Print warning below this
HF_CRITICAL = 1.2 # Exit with alarm below this
POLL_SECONDS = 60
def check_health():
data = pool.functions.getUserAccountData(WALLET).call()
debt = data[1] / 1e8
if debt == 0:
return None # No debt means no liquidation risk
return data[5] / 1e18
while True:
try:
hf = check_health()
if hf is None:
print(f"[{time.strftime('%H:%M:%S')}] No active borrows — health factor is infinite")
elif hf < HF_CRITICAL:
print(f"[{time.strftime('%H:%M:%S')}] CRITICAL: Health factor {hf:.4f} — liquidation imminent")
sys.exit(1) # Hook this into whatever alerting you use
elif hf < HF_WARNING:
print(f"[{time.strftime('%H:%M:%S')}] WARNING: Health factor {hf:.4f} — consider repaying or adding collateral")
else:
print(f"[{time.strftime('%H:%M:%S')}] OK: Health factor {hf:.4f}")
except Exception as e:
print(f"[{time.strftime('%H:%M:%S')}] RPC error: {e}")
time.sleep(POLL_SECONDS)
This is intentionally simple. A production version would push alerts to Telegram or Discord, handle RPC failover, and track historical readings. But for learning, a terminal script that polls every 60 seconds and prints a status line is enough to build the habit of watching your position.
Risk analysis
Smart contract and token risk
Aave publishes security and audit resources, but a deposit still depends on the deployed contracts and token behavior. USDC also carries issuer and depeg risk. Aave security resources
Oracle risk
If I borrow, collateral valuation affects liquidation eligibility. The account read gives the protocol’s aggregate view; it does not prove the underlying price inputs are correct.
Liquidity and operational risk
A withdrawal needs available reserve liquidity and an included transaction. RPC failure, sequencer downtime, or high fees can delay it. A small deposit limits the dollars at stake; it does not remove these dependencies.
Governance risk
Aave’s risk parameters are set by governance votes. A proposal could change LTVs, liquidation thresholds, or reserve factors. These changes affect your position’s risk profile without any action on your part. Following governance proposals on Aave governance is part of the monitoring job.
What I would verify before borrowing
First, confirm that the supply can be withdrawn. Then read the current collateral configuration and calculate how a price decline affects health factor at the intended debt amount.
The borrow limit is a ceiling. My margin comes from staying below it, rather than assuming the gap between LTV and liquidation threshold is enough. Keep repayment funds on the same chain and test the monitoring read before there is a loan to watch.