DiffusalCollateralVault
Central collateral management with portfolio margin enforcement
The DiffusalCollateralVault contract is the central custodian for user collateral in the Diffusal protocol. It holds USDC deposits and enforces health checks on withdrawals and trades. This is a high-risk contract as it directly holds user funds.
Note: Margin calculation logic has been extracted to DiffusalMarginCalculator for contract size optimization. The vault delegates margin queries to the calculator.
Overview
The vault provides three core functions:
| Function | Description |
|---|---|
| Custody | Holds USDC collateral for all users |
| Margin Calculation | Computes equity, unrealized PnL, IM, and MM |
| Health Enforcement | Enforces margin requirements on withdrawals and trades |
Margin Hierarchy
┌────────────────────────────┐
│ Deposit │
│ Raw USDC in vault │
└─────────────┬──────────────┘
│ +
▼
┌────────────────────────────┐
│ Unrealized PnL │
│ Sum of position PnL │
└─────────────┬──────────────┘
│ =
▼
┌────────────────────────────┐
│ Equity │
│ Total account value │
└─────────────┬──────────────┘
│ MUST >=
▼
┌────────────────────────────┐
│ Initial Margin (IM) │
│ Required for trading │
└─────────────┬──────────────┘
│ >
▼
┌────────────────────────────┐
│ Maintenance Margin (MM) │
│ Required to avoid │
│ liquidation │
└────────────────────────────┘Key Concepts
Equity Calculation
Unrealized PnL is the sum of mark-to-market gains/losses across all positions.
Initial Margin (IM)
Initial margin is calculated using SPAN-like stress testing via the MarginEngine. See Margin Calculations for the complete formula.
Maintenance Margin (MM)
MM is 80% of IM. See Margin System for margin mechanics.
Health Check
A user is healthy if:
A user is liquidatable if:
Storage & State
The contract uses ERC-7201 namespaced storage for upgradeability:
/// @custom:storage-location erc7201:diffusal.storage.CollateralVault
struct CollateralVaultStorage {
address owner;
address pendingOwner; // Two-step ownership
bool paused;
address positionManager;
address quoter;
address oracle;
address portfolioManager; // Portfolio management
address marginCalculator; // Delegated margin calculations
uint256 adversePnLBuffer; // Buffer rate on stress loss
uint256 initialMarginRate; // % of notional for IM buffer
mapping(address => bool) operators;
mapping(address => mapping(uint256 => uint256)) portfolioDeposits; // user → portfolioId → USDC
}
// Immutable
IERC20 public immutable collateralToken;
address private immutable _SERIES_REGISTRY;MarginInfo Struct
struct MarginInfo {
uint256 deposit; // Raw USDC deposit (6 decimals)
int256 unrealizedPnL; // Mark-to-market PnL (6 decimals)
int256 equity; // deposit + unrealizedPnL
uint256 initialMargin; // IM requirement (6 decimals)
uint256 maintenanceMargin; // MM = IM × 80%
uint256 maxWithdraw; // max(0, equity - IM)
bool isHealthy; // equity >= MM
}User Functions
Portfolio-Aware Architecture
All deposit, withdrawal, and margin functions are portfolio-aware. There
are no legacy user-level functions. See Portfolio-Aware
Functions for depositToPortfolio(),
withdrawFromPortfolio(), and related view functions.
Deposit and withdrawal functions require a portfolioId parameter. Portfolio 0 is auto-created on first deposit if it doesn't exist.
View Functions
Margin Calculator Delegation
All margin calculation functions are delegated to
DiffusalMarginCalculator. Only
portfolio-aware versions exist (e.g., getPortfolioEquity,
getPortfolioInitialMargin). See Portfolio-Aware View
Functions below.
Configuration Getters
function getAdversePnLBuffer() external view returns (uint256)
function getInitialMarginRate() external view returns (uint256)
function marginCalculator() external view returns (address)
function portfolioManager() external view returns (address)
function pendingOwner() external view returns (address)Owner Functions
setOperator
Authorizes or deauthorizes an operator.
function setOperator(address operator, bool authorized) external onlyOwnerEmits: OperatorUpdated
Contract Reference Setters
function setPositionManager(address manager) external onlyOwner
function setQuoter(address _newQuoter) external onlyOwner
function setOracle(address _newOracle) external onlyOwner
function setPortfolioManager(address _portfolioManager) external onlyOwner
function setMarginCalculator(address _marginCalculator) external onlyOwnerConfiguration Setters
function setAdversePnLBuffer(uint256 buffer) external onlyOwner
function setInitialMarginRate(uint256 rate) external onlyOwnerpause / unpause
Emergency pause controls.
function pause() external onlyOwner
function unpause() external onlyOwnerWhen paused: deposits and withdrawals are disabled.
Emits: PausedStateChanged
Two-Step Ownership Transfer
function transferOwnership(address newOwner) external onlyOwner
function acceptOwnership() external- Current owner calls
transferOwnership(newOwner)→ setspendingOwner - New owner calls
acceptOwnership()→ completes transfer
Emits: OwnershipTransferStarted, OwnershipTransferred
Portfolio-Aware Functions
The vault supports portfolio-aware collateral management, enabling gas-bounded liquidations with isolated margin per portfolio.
Portfolio-Aware User Functions
depositToPortfolio
Deposits USDC collateral into a specific portfolio.
function depositToPortfolio(uint256 portfolioId, uint256 amount) external| Parameter | Type | Description |
|---|---|---|
portfolioId | uint256 | Portfolio ID to deposit into |
amount | uint256 | Amount of USDC (6 decimals) |
Behavior:
- Creates portfolio via
PortfolioManagerif it doesn't exist - First deposit auto-creates portfolio 0 (default)
Emits: DepositedToPortfolio(user, portfolioId, amount)
withdrawFromPortfolio
Withdraws USDC collateral from a specific portfolio.
function withdrawFromPortfolio(uint256 portfolioId, uint256 amount) externalRequirements:
- Post-withdrawal portfolio equity must cover portfolio's IM
Emits: WithdrawnFromPortfolio(user, portfolioId, amount)
Portfolio-Aware View Functions
getPortfolioDeposit
Returns a user's deposit in a specific portfolio.
function getPortfolioDeposit(address user, uint256 portfolioId) external view returns (uint256)getPortfolioEquity
Returns a user's equity in a specific portfolio.
function getPortfolioEquity(address user, uint256 portfolioId) external view returns (int256 equity)getPortfolioUnrealizedPnL
Returns unrealized PnL for a specific portfolio.
function getPortfolioUnrealizedPnL(address user, uint256 portfolioId) external view returns (int256 pnl)getPortfolioInitialMargin
Returns initial margin requirement for a specific portfolio.
function getPortfolioInitialMargin(address user, uint256 portfolioId) external view returns (uint256 im)getPortfolioMaintenanceMargin
Returns maintenance margin for a specific portfolio.
function getPortfolioMaintenanceMargin(address user, uint256 portfolioId) external view returns (uint256 mm)getPortfolioMaxWithdraw
Returns maximum withdrawable amount from a specific portfolio.
function getPortfolioMaxWithdraw(address user, uint256 portfolioId) external view returns (uint256 maxWithdraw)isPortfolioHealthy
Checks if a portfolio is healthy (equity >= MM).
function isPortfolioHealthy(address user, uint256 portfolioId) external view returns (bool healthy)isPortfolioLiquidatable
Checks if a portfolio can be liquidated.
function isPortfolioLiquidatable(address user, uint256 portfolioId) external view returns (bool liquidatable)getPortfolioMarginInfo
Returns comprehensive margin info for a portfolio.
function getPortfolioMarginInfo(address user, uint256 portfolioId) external view returns (MarginInfo memory info)Portfolio-Aware Operator Functions
transferCollateralBetweenPortfolios
Transfers collateral between two portfolios of the same user.
function transferCollateralBetweenPortfolios(
address user,
uint256 fromPortfolioId,
uint256 toPortfolioId,
uint256 amount
) externalRequirements:
- Both portfolios must remain healthy after transfer
debitPortfolioCollateral
Debits collateral from a specific portfolio.
function debitPortfolioCollateral(address user, uint256 portfolioId, uint256 amount)
external returns (uint256 actualDebit)creditPortfolioCollateral
Credits collateral to a specific portfolio.
function creditPortfolioCollateral(address user, uint256 portfolioId, uint256 amount) externalEvents
| Event | Parameters | Description |
|---|---|---|
DepositedToPortfolio | user, portfolioId, amount | USDC deposited to portfolio |
WithdrawnFromPortfolio | user, portfolioId, amount | USDC withdrawn from portfolio |
OperatorUpdated | operator, authorized | Operator status changed |
PositionManagerUpdated | manager | Position manager changed |
QuoterUpdated | quoter | Quoter changed |
OracleUpdated | oracle | Oracle changed |
PortfolioManagerUpdated | portfolioManager | Portfolio manager changed |
MarginCalculatorUpdated | marginCalculator | Margin calculator changed |
AdversePnLBufferUpdated | buffer | Adverse PnL buffer changed |
InitialMarginRateUpdated | rate | Initial margin rate changed |
PausedStateChanged | paused | Pause state toggled |
OwnershipTransferStarted | previousOwner, newOwner | Ownership transfer initiated |
OwnershipTransferred | previousOwner, newOwner | Ownership transfer completed |
Integration Points
Depends On
| Contract | Purpose |
|---|---|
| DiffusalOptionsPositionManager | Position queries for PnL/margin |
| DiffusalOptionsSeriesRegistry | Series info for margin calculations |
| DiffusalOptionsQuoter | Mark prices for unrealized PnL |
| DiffusalPortfolioManager | Portfolio existence and creation |
| MarginEngine | SPAN-like margin calculations |
| USDC (ERC20) | Collateral token |
Used By
| Contract | Purpose |
|---|---|
| DiffusalOptionsOrderBook | Premium transfers, health checks |
| DiffusalOptionsRFQ | Premium transfers, health checks |
| DiffusalSettlementEngine | Credit/debit on settlement |
| DiffusalLiquidationEngine | Liquidation proceeds |
| DiffusalInsuranceFund | Stores insurance fund balance |
Security Considerations
Reentrancy Protection
All user-facing functions use nonReentrant modifier to prevent callback attacks.
Withdrawal Margin Check
Users can only withdraw excess collateral:
uint256 maxWithdraw = getMaxWithdraw(msg.sender);
if (amount > maxWithdraw) revert Errors.WithdrawExceedsMaximum();This ensures post-withdrawal equity still covers initial margin.
Post-Trade Health Checks
Trading contracts check health after every trade:
if (!marginCalculator.isHealthy(maker)) revert Errors.InsufficientMargin();
if (!marginCalculator.isHealthy(taker)) revert Errors.InsufficientMargin();Operator Trust
Operators can transfer collateral between users. Only trusted contracts should be authorized:
- DiffusalOptionsOrderBook
- DiffusalOptionsRFQ
- DiffusalSettlementEngine
- DiffusalLiquidationEngine
Emergency Pause
Owner can pause the vault to prevent deposits/withdrawals during emergencies. Operator functions remain active to allow settlement and liquidation.
Code Reference
Source: packages/contracts/src/DiffusalCollateralVault.sol
Interface: packages/contracts/src/interfaces/IDiffusalCollateralVault.sol
Key Constants
// From Constants.sol
uint256 constant WAD = 1e18;
uint256 constant WAD_TO_USDC = 1e12; // WAD (18 dec) to USDC (6 dec)
uint256 constant MAINTENANCE_MARGIN_RATE = 0.8e18; // 80%Related
- DiffusalPortfolioManager — Portfolio management
- Margin System (Protocol) — High-level margin mechanics
- MarginEngine — SPAN-like stress testing
- Liquidation (Protocol) — When margin falls below MM
- Protocol Design — Collateral model overview