Development guide for the PolyAave protocol - a prediction-market collateralized lending system built on Aave V3 with multi-level debt tracking and oracle-driven pricing.
Development instructions for the PolyAave protocol - a prediction-market collateralized lending system that extends Aave V3 with specialized collateral handling and multi-level debt tracking.
PolyAave is a lending protocol that:
The protocol follows a functional-core/imperative-shell pattern with three layers:
Pure, deterministic mathematical functions with no storage access. All critical calculations live here:
Stateless façade layer that orchestrates business logic:
Minimal storage container with event emission:
All canonical types are defined in `DataStruct.sol`:
```solidity
struct PoolData {
uint256 totalSupplied; // Total base asset supplied (Ray precision)
uint256 totalBorrowedAllMarkets; // Sum of all market borrows
// ... spread curve parameters
}
struct MarketData {
uint256 variableBorrowIndex; // Protocol spread index (Ray)
uint256 totalBorrowed; // Total borrowed for this market
uint256 lastUpdateTimestamp;
// ... oracle and risk parameters
}
struct UserPosition {
uint256 collateralAmount; // Collateral tokens deposited
uint256 borrowAmount; // Principal from Aave
uint256 scaledDebtBalance; // Scaled spread debt
}
```
1. **LP Tokens**: Minted 1-for-1 with deposits: `Σ lpBalances == pool.totalSupplied`
2. **Market Debt**: `Σ market.totalBorrowed == pool.totalBorrowedAllMarkets`
3. **Liquidity Constraint**: `pool.totalBorrowedAllMarkets ≤ pool.totalSupplied`
User debt consists of two components:
**Principal (P)**: Aave-sourced debt attributed by market share
```
P_user = D_protocol × (B_market / Σ B_all_markets) × (b_user / B_market)
```
**Spread (S)**: Protocol margin tracked via scaled balance
```
S_user = (scaledDebt_user × index_market) - b_user
```
**Total Debt**:
```
Debt_user = P_user + S_user
```
```
utilization = totalBorrowed / totalSupplied
if utilization ≤ U_optimal:
spreadRate = baseRate + slope1 × utilization
else:
spreadRate = baseRate + slope1 × U_optimal + slope2 × (utilization - U_optimal)
```
Parameters `{baseRate, U_optimal, slope1, slope2}` are governance-controlled to ensure LP yield exceeds Aave APR while providing borrower competitiveness.
**Flow**: `_processSupply` → `calculateLPTokensToMint` → update storage
**Key steps**:
1. Calculate LP tokens to mint (1:1 with deposit at genesis, scaled by share post-genesis)
2. Transfer base asset from user
3. Update `pool.totalSupplied` and user LP balance
4. Emit `Supply` event
**Flow**: `_updateMarketIndices` → `_processBorrow` → oracle check → LTV validation → Aave borrow → scale debt
**Key steps**:
1. Update market spread index based on time elapsed
2. Fetch live collateral price from oracle
3. Validate LTV (Loan-to-Value) ratio
4. Borrow principal from Aave V3 pool
5. Scale and record user debt (principal + spread)
6. Transfer borrowed asset to user
7. Emit `Borrow` event
**Critical checks**:
**Flow**: `_processRepay` → compute debt components → repay Aave → pocket spread → update balances
**Key steps**:
1. Calculate total debt (principal + accrued spread)
2. Transfer repayment amount from user
3. Repay principal portion to Aave
4. Retain spread portion in protocol
5. Update user scaled debt balance
6. Return excess collateral if fully repaid
7. Emit `Repay` event
**Planned flow**: `_processResolution` → redeem collateral → net P/L → distribute to LPs
**Purpose**: Handle prediction market maturity when collateral price deterministically resolves.
**Status**: Stubbed for V2 implementation
**Purpose**: Allow liquidators to seize under-collateralized positions when LTV threshold breached.
1. **Always update `CoreMath.sol` first** - this is the source of truth
2. Make functions `pure` wherever possible (no storage reads)
3. Use SafeCast for all type conversions
4. Add zero-division guards explicitly
5. Maintain Ray precision (1e27) for all rate calculations
6. Document invariants in comments
1. **Never put business logic in `StorageShell.sol`** - it should only store and emit
2. Keep `Core.sol` stateless - gather data, compute, return instructions
3. Update storage only after all validations pass
4. Emit events for all state changes
5. Maintain global invariants documented in §1.5
1. Design math functions in `CoreMath.sol` with test cases
2. Add orchestration logic to `Core.sol`
3. Add minimal storage and events to `StorageShell.sol`
4. Update `DataStruct.sol` if new types needed
5. Test invariants thoroughly before deployment
**Threat**: Stale or manipulated price feeds lead to under-collateralization
**Mitigations**:
**Threat**: LP yield falls below Aave borrowing cost, causing capital flight
**Mitigations**:
**Threat**: Insufficient base asset for withdrawals due to high utilization
**Mitigations**:
1. **Unit tests** for all CoreMath functions (pure math)
2. **Integration tests** for state transitions (supply → borrow → repay flows)
3. **Invariant tests** for global constraints (debt attribution, LP accounting)
4. **Fuzz tests** for edge cases (extreme utilization, price volatility)
5. **Scenario tests** for market lifecycle (creation → maturity → resolution)
| File | Purpose | Contains |
|------|---------|----------|
| `DataStruct.sol` | Type definitions | All protocol structs (PoolData, MarketData, UserPosition) |
| `CoreMath.sol` | Pure math functions | Spread curves, debt attribution, LP token calculations |
| `Core.sol` | Business logic orchestration | Stateless façade coordinating storage and math |
| `StorageShell.sol` | State container | Storage variables, events, minimal getters |
| `interfaces/` | External dependencies | Aave V3, oracles, ERC standards |
```solidity
// Gather position data
UserPosition memory pos = storageShell.getUserPosition(user, market);
MarketData memory market = storageShell.getMarketData(marketId);
PoolData memory pool = storageShell.getPoolData();
// Compute via CoreMath
(uint256 principal, uint256 spread) = CoreMath.calculateUserDebt(
pos.borrowAmount,
pos.scaledDebtBalance,
market.variableBorrowIndex,
market.totalBorrowed,
pool.totalBorrowedAllMarkets
);
uint256 totalDebt = principal + spread;
```
```solidity
// Fetch current state
MarketData memory market = storageShell.getMarketData(marketId);
PoolData memory pool = storageShell.getPoolData();
// Compute new index
uint256 newIndex = CoreMath.compoundSpreadIndex(
market.variableBorrowIndex,
CoreMath.calculateSpreadRate(pool.totalBorrowedAllMarkets, pool.totalSupplied, params),
block.timestamp - market.lastUpdateTimestamp
);
// Write back
storageShell.updateMarketIndex(marketId, newIndex, block.timestamp);
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/polyaave-contract-development-guide/raw