Share-Based Accounting
How positions track balances efficiently using shares and indices
Why Shares?
Traditional balance tracking has problems at scale - updating every user's balance on every interest accrual is O(n) complexity and causes rounding errors to accumulate differently per user.
Share-based accounting solves this by updating a single global index (O(1) complexity), and deriving user balances on-demand by multiplying their shares by the current index. All users share the same index, eliminating drift.
Core Data Model
Position Structure
Each user position stores shares, not native balances:
deposit_scaled- supply sharesdebt_scaled- debt shares
Native balances are derived:
deposit_native = deposit_scaled × lending_indexdebt_native = debt_scaled × borrow_index
Pool Indices
Each pool maintains two indices that grow over time:
lending_index- cumulative supply growth (starts at 1.0)borrow_index- cumulative debt growth (starts at 1.0)last_updated- timestamp of last sync
Balance Calculation
Supply balance: deposit_native = deposit_scaled × lending_index
Debt balance: debt_native = debt_scaled × borrow_index
Share Operations
Minting Shares (Deposit/Borrow)
When a user deposits or borrows, shares are minted based on the current index:
shares = native_amount / index
Example Deposit: User deposits 100 tokens at lending_index = 1.05 → shares_minted = 100 / 1.05 = 95.24 shares
Burning Shares (Withdraw/Repay)
When a user withdraws or repays, shares are burned:
Example Withdraw: User withdraws after index grows to 1.10 → shares_burned = 95.24 shares → native_received = 95.24 × 1.10 = 104.76 tokens (earned 4.76 tokens interest)
Interest Accrual Example
Initial State (t=0):
- lending_index = 1.0, borrow_index = 1.0
- Alice deposits 1000 tokens → 1000 shares
- Bob borrows 500 tokens → 500 debt shares
After 1 Year:
- Pool utilization = 50%
- Borrow rate = 5.125%, Lending rate = 2.306%
- New indices: borrow_index = 1.0526, lending_index = 1.0231
Balances:
- Alice: 1000 shares × 1.0231 = 1023.1 tokens (+2.31% APY)
- Bob: 500 shares × 1.0526 = 526.3 tokens debt (+5.26% APY)
- Interest gap: 26.3 - 23.1 = 3.2 tokens (protocol revenue)
RAY Precision
All index calculations use RAY precision (1e27) to maintain accuracy. This matches Aave's implementation and provides enough precision for tokens with up to 9 decimals plus 18 digits of buffer.
RAY precision (1e27) prevents rounding errors from accumulating over time, ensuring fair interest distribution across all users.
Index Synchronization
Before any operation, the pool's indices must be synchronized to the current time:
This ensures all operations use up-to-date indices for accurate share calculations.
Key Invariants
- Indices never decrease - they only grow (monotonic)
- Shares only change on user actions - deposit/withdraw/borrow/repay/liquidate
- Protocol revenue = debt interest - supply interest
- All calculations use RAY precision (1e27)