DiffusalCollateralVault
Central collateral management with portfolio margin enforcement
The DiffusalCollateralVault contract is the central custodian for user collateral in the Diffusal protocol. It holds USDT 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 USDT 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 USDT 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 → USDT
}
// Immutable
IERC20 public immutable collateralToken;
address private immutable _SERIES_REGISTRY;MarginInfo Struct
struct MarginInfo {
uint256 deposit; // Raw USDT 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 owner() external view returns (address)
function isAdmin(address account) external view returns (bool)
function marginCalculator() external view returns (address)
function portfolioManager() external view returns (address)
function pendingOwner() external view returns (address)Admin Functions
setOperator
Authorizes or deauthorizes an operator.
function setOperator(address operator, bool authorized) external onlyAdminEmits: OperatorUpdated
Contract Reference Setters
function setPositionManager(address manager) external onlyAdmin
function setPortfolioManager(address _portfolioManager) external onlyAdmin
function setMarginCalculator(address _marginCalculator) external onlyAdminpause / unpause
Emergency pause controls.
function pause() external onlyAdmin
function unpause() external onlyAdminWhen paused: deposits and withdrawals are disabled.
Emits: PausedStateChanged
Owner Functions
function addAdmin(address admin) external onlyOwner
function removeAdmin(address admin) external onlyOwnerTwo-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 USDT 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 USDT (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 USDT 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)For portfolio equity, PnL, IM/MM, health, and max-withdraw metrics, query DiffusalMarginCalculator rather than the vault.
Portfolio-Aware Operator Functions
transferCollateralBetweenPortfolios
Transfers collateral between two portfolios of the same user.
function transferCollateralBetweenPortfolios(
uint256 fromPortfolioId,
uint256 toPortfolioId,
uint256 amount
) externalRequirements:
- Both portfolios must remain healthy after transfer
- Source portfolio must not have expired-but-unsettled short positions (prevents collateral drain during the expiry-to-settlement window)
debitPortfolioCollateral
Debits collateral from a specific portfolio.
function debitPortfolioCollateral(address user, uint256 portfolioId, uint256 amount, CollateralOperation operation)
external returns (uint256 actualDebit)creditPortfolioCollateral
Credits collateral to a specific portfolio.
function creditPortfolioCollateral(address user, uint256 portfolioId, uint256 amount, CollateralOperation operation) externalEvents
| Event | Parameters | Description |
|---|---|---|
DepositedToPortfolio | user, portfolioId, amount | USDT deposited to portfolio |
WithdrawnFromPortfolio | user, portfolioId, amount | USDT withdrawn from portfolio |
CollateralTransferredBetweenPortfolios | user, fromPortfolioId, toPortfolioId, amount | Collateral moved between portfolios |
PortfolioCollateralDebited | user, portfolioId, amount, operation | Operator debit from portfolio |
PortfolioCollateralCredited | user, portfolioId, amount, operation | Operator credit to portfolio |
DebitCapped | user, portfolioId, requestedAmount, actualAmount | Debit capped to available deposit |
OperatorUpdated | operator, authorized | Operator status changed |
PortfolioManagerUpdated | portfolioManager | Portfolio manager changed |
MarginCalculatorUpdated | marginCalculator | Margin calculator 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 |
| USDT (ERC20) | Collateral token |
Used By
| Contract | Purpose |
|---|---|
| DiffusalOptionsOrderBook | Premium transfers, health checks |
| DiffusalRfqExecutor | 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 = marginCalculator.getPortfolioMaxWithdraw(msg.sender, portfolioId);
if (amount > maxWithdraw) revert Errors.WithdrawExceedsMaximum();This ensures post-withdrawal equity still covers initial margin.
Expired Position Guard
Inter-portfolio collateral transfers are blocked when the source portfolio contains expired-but-unsettled short positions. This prevents collateral drain during the expiry-to-settlement window, where expired short positions have IM=0 (mark price drops to 0 at expiry) but still carry settlement obligations that will be debited by the settlement engine.
Post-Trade Health Checks
Trading contracts check health after every trade:
if (!marginCalculator.isPortfolioHealthy(maker, makerPortfolioId)) revert Errors.InsufficientMargin();
if (!marginCalculator.isPortfolioHealthy(taker, takerPortfolioId)) revert Errors.InsufficientMargin();Operator Trust
Operators can transfer collateral between users. Only trusted contracts should be authorized:
- DiffusalOptionsOrderBook
- DiffusalRfqExecutor
- 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_USDT = 1e12; // WAD (18 dec) to USDT (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