Diffusal

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:

FunctionDescription
CustodyHolds USDC collateral for all users
Margin CalculationComputes equity, unrealized PnL, IM, and MM
Health EnforcementEnforces 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

Equity=Deposit+Unrealized PnL\text{Equity} = \text{Deposit} + \text{Unrealized PnL}

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:

EquityMaintenance Margin\text{Equity} \geq \text{Maintenance Margin}

A user is liquidatable if:

Equity<Maintenance Margin AND not MMM\text{Equity} < \text{Maintenance Margin} \text{ AND not MMM}

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 onlyOwner

Emits: 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 onlyOwner

Configuration Setters

function setAdversePnLBuffer(uint256 buffer) external onlyOwner
function setInitialMarginRate(uint256 rate) external onlyOwner

pause / unpause

Emergency pause controls.

function pause() external onlyOwner
function unpause() external onlyOwner

When paused: deposits and withdrawals are disabled.

Emits: PausedStateChanged


Two-Step Ownership Transfer

function transferOwnership(address newOwner) external onlyOwner
function acceptOwnership() external
  1. Current owner calls transferOwnership(newOwner) → sets pendingOwner
  2. 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
ParameterTypeDescription
portfolioIduint256Portfolio ID to deposit into
amountuint256Amount of USDC (6 decimals)

Behavior:

  • Creates portfolio via PortfolioManager if 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) external

Requirements:

  • 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
) external

Requirements:

  • 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) external

Events

EventParametersDescription
DepositedToPortfoliouser, portfolioId, amountUSDC deposited to portfolio
WithdrawnFromPortfoliouser, portfolioId, amountUSDC withdrawn from portfolio
OperatorUpdatedoperator, authorizedOperator status changed
PositionManagerUpdatedmanagerPosition manager changed
QuoterUpdatedquoterQuoter changed
OracleUpdatedoracleOracle changed
PortfolioManagerUpdatedportfolioManagerPortfolio manager changed
MarginCalculatorUpdatedmarginCalculatorMargin calculator changed
AdversePnLBufferUpdatedbufferAdverse PnL buffer changed
InitialMarginRateUpdatedrateInitial margin rate changed
PausedStateChangedpausedPause state toggled
OwnershipTransferStartedpreviousOwner, newOwnerOwnership transfer initiated
OwnershipTransferredpreviousOwner, newOwnerOwnership transfer completed

Integration Points

Depends On

ContractPurpose
DiffusalOptionsPositionManagerPosition queries for PnL/margin
DiffusalOptionsSeriesRegistrySeries info for margin calculations
DiffusalOptionsQuoterMark prices for unrealized PnL
DiffusalPortfolioManagerPortfolio existence and creation
MarginEngineSPAN-like margin calculations
USDC (ERC20)Collateral token

Used By

ContractPurpose
DiffusalOptionsOrderBookPremium transfers, health checks
DiffusalOptionsRFQPremium transfers, health checks
DiffusalSettlementEngineCredit/debit on settlement
DiffusalLiquidationEngineLiquidation proceeds
DiffusalInsuranceFundStores 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%

On this page