DeFi Accounting From First Principles: Assets, Shares, Profit, and Loss
Deriving the accounting rules behind vault shares, fair deposits, redemptions, and proportional profit and loss.
View the tested implementationI’m relearning DeFi from first principles—one topic at a time, with equations, handwritten notes, Rust code, and tests. Day 1 started with a simple question:
What exactly does a vault share represent?
The complete implementation and test suite live in the DeFi accounting repository.
Begin with the accounting identity
Here, A is assets, L is liabilities, and E is equity. Equivalently, E = A − L.
Chapter 4 of the IFRS Conceptual Framework for Financial Reporting defines:
- An asset as a present economic resource controlled by the entity as a result of past events (paragraph 4.3).
- A liability as a present obligation to transfer an economic resource as a result of past events (paragraph 4.26).
- Equity as the residual interest in the entity’s assets after deducting all its liabilities (paragraph 4.63).
The accounting identity above connects those three elements of financial position. Applied to a vault, it means shareholders have a claim on net assets, not necessarily every token appearing in its gross balance. The Conceptual Framework supplies the underlying concepts, but it is not itself an IFRS Accounting Standard.
Let A be net vault assets, S total shares, and p the assets represented by one share:
A share is not a fixed quantity of USDC. It represents proportional ownership:
This distinction between underlying assets and vault shares is fundamental to ERC-4626, the tokenized-vault standard.
A deposit is not profit
Alice deposits 1,000 USDC into an empty vault. The vault establishes an initial one-to-one conversion and mints 1,000 shares:
The vault controls 1,000 USDC, but it did not earn 1,000 USDC. It received an asset and simultaneously issued Alice a claim against it. A deposit increases assets and shareholder claims together; it does not create profit.
Profit reprices existing shares
The strategy earns 200 USDC. Assets increase to 1,200 while shares remain 1,000:
Alice’s shares are now worth 1,200 USDC.
Profit increases assets without increasing shares.
Pricing a new depositor fairly
Bob now deposits 600 USDC. Giving him 600 shares would be incorrect: each share is worth 1.2 USDC, so 600 shares represent 720 USDC. The extra 120 USDC would come from Alice’s earlier profit.
Bob should receive:
In general:
After Bob’s deposit:
The deposit changed the vault’s size but not the value of an existing share. If the deposit is d and minted shares are m = dS/A:
A correctly priced deposit preserves share price.
Redemption reverses a deposit
If a user redeems r shares:
Consider 3,300 USDC and 2,750 shares. Redeeming 300 shares returns:
After paying 360 USDC and burning 300 shares:
A correctly priced redemption preserves share price.
Loss destroys assets, not shares
Starting from 2,940 USDC and 2,450 shares, the strategy loses 490 USDC. Assets fall to 2,450 while shares stay at 2,450, making each share worth 1 USDC.
The price fell from 1.2 to 1, but the loss is not 20%:
Returning from 1 to 1.2 requires a 20% gain. Loss and recovery differ because their denominators differ.
The four state transitions
| Operation | Assets | Shares | Ideal share price |
|---|---|---|---|
| Deposit | Increase | Increase | Unchanged |
| Redemption | Decrease | Decrease | Unchanged |
| Profit | Increase | Unchanged | Increases |
| Loss | Decrease | Unchanged | Decreases |
Turning the equations into Rust
The model tracks three pieces of state:
pub struct Vault {
total_assets: u64,
total_shares: u64,
balances: HashMap<UserId, u64>,
}Conversions use a u128 intermediate before division:
fn mul_div_floor(a: u64, b: u64, c: u64) -> Result<u64, AccountingError> {
debug_assert!(c > 0);
let product = (a as u128) * (b as u128);
let result = product / (c as u128);
u64::try_from(result).map_err(|_| AccountingError::Overflow)
}This avoids overflowing u64 during multiplication even when the final quotient fits. Every prospective state update is also calculated before mutation, so rejected operations cannot leave half-updated accounting behind.
Testing properties, not only examples
The repository has 37 passing tests: 16 unit tests, 7 property tests, and 14 scenario tests. They verify that:
- User balances sum to total share supply.
- Profit and loss never change share supply.
- Failed operations never mutate state.
- Total user claims never exceed vault assets.
- Rounding down cannot reduce the value of remaining shares.
- Exactly divisible deposits and redemptions preserve the ratio.
What I learned
The central lesson was learning to classify state transitions correctly:
- A deposit is not revenue.
- A withdrawal is not a loss.
- Profit does not require issuing shares.
- Loss does not require destroying shares.
- Fair entry and exit preserve everyone who stays in the vault.
The equations look simple when values divide evenly. Real programs use finite-width integers, introducing rounding, overflow, zero-share deposits, insolvency, and first-depositor edge cases.
That is where Day 2 begins: fixed-point arithmetic and rounding.
References
- IFRS Foundation, Conceptual Framework for Financial Reporting, Chapter 4, paragraphs 4.3, 4.26, and 4.63.
- IFRS Foundation, Conceptual Framework overview.
- IFRS Foundation, Preface to IFRS Standards.
- Ethereum Improvement Proposals, ERC-4626: Tokenized Vaults.