Pool Utilization
Pool utilization is a fundamental metric that measures the ratio of borrowed assets to total assets in a CollateralTracker vault. This metric drives interest rates, collateral requirements, and is protected against manipulation through transient storage.
Definitionβ
Pool utilization represents how much of the pool's capital is currently deployed (borrowed by option sellers):
Utilization = (Assets in AMM + Unrealized Interest) / Total Assets
Where:
- Assets in AMM: Capital deployed to Uniswap for option positions (
s_assetsInAMM) - Unrealized Interest: Accrued but not yet collected interest
- Total Assets:
s_depositedAssets + s_assetsInAMM + unrealizedInterest
Utilization Scalesβ
The protocol uses two scales for utilization:
Basis Points (DECIMALS = 10,000)β
Used for collateral ratio calculations:
function _poolUtilizationView() internal view returns (uint256 poolUtilization) {
return Math.mulDivRoundingUp(
s_assetsInAMM + s_marketState.unrealizedInterest(),
DECIMALS, // 10,000
totalAssets()
);
}
Example: 50% utilization = 5,000
WAD Scale (1e18)β
Used for interest rate calculations:
function _poolUtilizationWadView() internal view returns (uint256 poolUtilization) {
return Math.mulDivRoundingUp(
s_assetsInAMM + s_marketState.unrealizedInterest(),
WAD, // 1e18
totalAssets()
);
}
Example: 50% utilization = 0.5e18
Flash Deposit Protectionβ
A critical security mechanism prevents flash deposits from artificially lowering utilization within a single transaction.
The Attack Vectorβ
Without protection, an attacker could:
- Flash loan a large amount of tokens
- Deposit tokens (lowering utilization)
- Open a position at artificially low collateral requirements
- Withdraw tokens
- Repay flash loan
Transient Storage Solutionβ
The protocol uses EIP-1153 transient storage to track the maximum utilization seen during a transaction:
bytes32 internal constant UTILIZATION_TRANSIENT_SLOT =
keccak256("panoptic.utilization.snapshot");
function _poolUtilization() internal returns (uint256 poolUtilization) {
uint256 storedUtilization;
bytes32 slot = UTILIZATION_TRANSIENT_SLOT;
// Load stored utilization from transient storage
assembly {
storedUtilization := tload(slot)
}
// Calculate current utilization
poolUtilization = _poolUtilizationView();
// Return the HIGHER of stored vs current
if (storedUtilization > poolUtilization) {
return storedUtilization;
} else {
// Store current as new maximum
assembly {
tstore(slot, poolUtilization)
}
return poolUtilization;
}
}
How It Worksβ
Transaction Start:
ββ Transient slot = 0
β
ββ Step 1: User deposits (utilization drops to 40%)
β ββ Stored: 40%
β
ββ Step 2: User opens position (utilization = 60%)
β ββ Stored: 60% (higher)
β
ββ Step 3: User tries flash deposit attack
β ββ Deposit: utilization calculation = 30%
β ββ But stored = 60%
β ββ Returns: 60% (prevents manipulation)
β
Transaction End:
ββ Transient storage cleared automatically
WAD Versionβ
The same protection applies to WAD-scale utilization:
function _poolUtilizationWad() internal returns (uint256) {
uint256 storedUtilization;
bytes32 slot = UTILIZATION_TRANSIENT_SLOT;
assembly {
storedUtilization := tload(slot)
}
// Convert stored value from DECIMALS to WAD
storedUtilization = (storedUtilization * WAD) / DECIMALS;
uint256 poolUtilization = _poolUtilizationWadView();
if (storedUtilization > poolUtilization) {
return storedUtilization;
} else {
// Store as DECIMALS for consistency
assembly {
tstore(slot, div(mul(poolUtilization, DECIMALS), WAD))
}
return poolUtilization;
}
}
Note: The transient slot always stores the value in DECIMALS scale, with conversion happening at read/write time for the WAD version.
Utilization Effectsβ
On Interest Ratesβ
Higher utilization leads to higher interest rates (see Interest Rate Model):
| Utilization | Effect on Rate |
|---|---|
| < Target (66.67%) | Below target rate |
| = Target (66.67%) | At target rate |
| > Target (66.67%) | Above target rate |
| Near 100% | Up to 4Γ target rate |
On Collateral Requirementsβ
Utilization affects both buyer and seller collateral ratios:
Sellers (via _sellCollateralRatio):
- Low utilization: Base ratio (e.g., 20%)
- High utilization: Up to 100%
Buyers (via _buyCollateralRatio):
- Low utilization: Base ratio (e.g., 10%)
- High utilization: Half base ratio (e.g., 5%)
See Utilization and Ratios for complete details.
On Cross-Marginingβ
The cross-buffer ratio decreases with utilization:
- Low utilization: Full cross-margin benefit
- High utilization: No cross-margin benefit
Pool Data Queryβ
All utilization-related metrics can be queried via getPoolData():
function getPoolData() external view returns (
uint256 depositedAssets,
uint256 insideAMM,
uint256 creditedShares,
uint256 currentPoolUtilization
) {
depositedAssets = s_depositedAssets;
insideAMM = s_assetsInAMM;
creditedShares = s_creditedShares;
currentPoolUtilization = _poolUtilizationView();
}
Asset Trackingβ
The CollateralTracker maintains three key asset-related state variables:
s_depositedAssetsβ
s_depositedAssetsCached amount of assets deposited by liquidity providers:
uint128 internal s_depositedAssets;
Updated on:
- Deposits:
s_depositedAssets += assets - Withdrawals:
s_depositedAssets -= assets - Position changes:
s_depositedAssets = int256(s_depositedAssets) - ammDeltaAmount + realizedPremium
s_assetsInAMMβ
s_assetsInAMMAmount currently deployed in Uniswap positions:
uint128 internal s_assetsInAMM;
Updated on:
- Position creation:
s_assetsInAMM += shortAmount - Position closing:
s_assetsInAMM -= shortAmount
s_creditedSharesβ
s_creditedSharesShares representing long position collateral:
uint256 internal s_creditedShares;
Updated on:
- Long position creation:
s_creditedShares += creditDelta - Long position closing:
s_creditedShares -= creditDelta
Total Supply Calculationβ
The total supply includes both user shares and credited shares:
function totalSupply() public view returns (uint256) {
return _internalSupply + s_creditedShares;
}
This affects share pricing and conversion calculations.
Utilization Boundsβ
Minimum (0%)β
Occurs when no assets are deployed:
s_assetsInAMM = 0unrealizedInterest = 0- All capital is idle in the pool
Maximum (Approaching 100%)β
Occurs when nearly all assets are deployed:
s_assetsInAMM β totalAssets- High interest rates discourage further borrowing
- Collateral requirements approach 100% for sellers
View vs. State-Modifying Functionsβ
| Function | Returns | Side Effects |
|---|---|---|
_poolUtilizationView() | Current utilization (DECIMALS) | None |
_poolUtilizationWadView() | Current utilization (WAD) | None |
_poolUtilization() | Max(stored, current) (DECIMALS) | Updates transient storage |
_poolUtilizationWad() | Max(stored, current) (WAD) | Updates transient storage |
Usage:
- View functions: For external queries and read-only calculations
- State-modifying functions: For operations that affect positions
Summaryβ
| Aspect | Description |
|---|---|
| Definition | (AMM assets + interest) / total assets |
| Scales | DECIMALS (10,000) or WAD (1e18) |
| Protection | Transient storage prevents flash manipulation |
| Effects | Drives interest rates, collateral ratios, cross-margin |
| Storage | Maximum utilization persists within transaction |
The utilization system ensures:
- Fair pricing based on actual demand
- Protection against flash loan attacks
- Consistent behavior within transactions
- Proper incentives for liquidity balance