Settlement Flows
The settlement system handles all balance updates when positions are created or closed. This page details the settleMint and settleBurn functions that coordinate asset movements, share accounting, and fee collection.
Overviewβ
Settlement occurs at two critical points in a position's lifecycle:
| Function | Trigger | Purpose |
|---|---|---|
settleMint | Position creation | Handle deposits, update tracking, charge commission |
settleBurn | Position closure | Handle withdrawals, settle premium, charge fees |
Both functions are called by the PanopticPool and update the CollateralTracker's internal state.
settleMintβ
Called when a user creates a new option position.
Function Signatureβ
function settleMint(
address optionOwner,
int128 longAmount,
int128 shortAmount,
int128 ammDeltaAmount,
RiskParameters riskParameters
) external onlyPanopticPool returns (uint32, int128)
Parametersβ
| Parameter | Description |
|---|---|
optionOwner | Address creating the position |
longAmount | Notional value of long legs |
shortAmount | Notional value of short legs |
ammDeltaAmount | Tokens moved to/from Uniswap |
riskParameters | Current risk parameters (fees, safe mode, etc.) |
Returnsβ
| Return | Description |
|---|---|
utilization | Pool utilization after the mint (basis points) |
tokenPaid | Net tokens paid/received by the user |
Flowβ
settleMint
ββ 1. _updateBalancesAndSettle(isCreation=true)
β ββ Accrue interest for user
β ββ Calculate net borrows
β ββ Update credited shares (for longs)
β ββ Mint/burn user shares based on tokenToPay
β ββ Update s_depositedAssets
β ββ Update s_assetsInAMM
β ββ Update user's interest state
β
ββ 2. Calculate commission
β ββ commission = shortAmount + longAmount
β ββ commissionFee = commission Γ notionalFee / DECIMALS
β
ββ 3. Distribute commission
β ββ If no builder: burn shares
β ββ If builder: split between protocol and builder
β
ββ 4. Return (utilization, tokenPaid)
Commission Calculationβ
uint128 commission = uint256(int256(shortAmount) + int256(longAmount)).toUint128();
uint128 commissionFee = uint128(
Math.mulDivRoundingUp(commission, riskParameters.notionalFee(), DECIMALS)
);
uint256 sharesToBurn = Math.mulDivRoundingUp(commissionFee, _totalSupply, _totalAssets);
Commission Distributionβ
Without Builder:
if (riskParameters.feeRecipient() == 0) {
_burn(optionOwner, sharesToBurn);
emit CommissionPaid(optionOwner, address(0), commissionFee, 0);
}
With Builder:
// Protocol share (65%)
_transferFrom(
optionOwner,
address(riskEngine()),
(sharesToBurn * riskParameters.protocolSplit()) / DECIMALS
);
// Builder share (25%)
_transferFrom(
optionOwner,
address(uint160(riskParameters.feeRecipient())),
(sharesToBurn * riskParameters.builderSplit()) / DECIMALS
);
emit CommissionPaid(
optionOwner,
address(uint160(riskParameters.feeRecipient())),
uint128((commissionFee * riskParameters.protocolSplit()) / DECIMALS),
uint128((commissionFee * riskParameters.builderSplit()) / DECIMALS)
);
settleBurnβ
Called when a user closes an existing option position.
Function Signatureβ
function settleBurn(
address optionOwner,
int128 longAmount,
int128 shortAmount,
int128 ammDeltaAmount,
int128 realizedPremium,
RiskParameters riskParameters
) external onlyPanopticPool returns (int128)
Parametersβ
| Parameter | Description |
|---|---|
optionOwner | Address closing the position |
longAmount | Notional value of long legs being closed |
shortAmount | Notional value of short legs being closed |
ammDeltaAmount | Tokens moved from Uniswap |
realizedPremium | Premium to settle (positive = paid to owner) |
riskParameters | Current risk parameters |
Returnsβ
| Return | Description |
|---|---|
tokenPaid | Net tokens paid/received by the user |
Flowβ
settleBurn
ββ 1. _updateBalancesAndSettle(isCreation=false)
β ββ Accrue interest for user
β ββ Calculate net borrows (reversed from mint)
β ββ Update credited shares (for longs)
β ββ Handle rounding haircut if needed
β ββ Mint/burn user shares based on tokenToPay
β ββ Update s_depositedAssets (includes realizedPremium)
β ββ Update s_assetsInAMM
β ββ Update user's interest state
β
ββ 2. If realizedPremium != 0, calculate premium fee
β ββ premiumFee = |realizedPremium| Γ premiumFee rate
β ββ notionalCap = (long + short) Γ 10 Γ notionalFee
β ββ commissionFee = min(premiumFee, notionalCap)
β
ββ 3. Distribute commission (same as settleMint)
β
ββ 4. Return tokenPaid
Premium Fee Calculationβ
The premium fee is capped to prevent excessive fees on highly profitable positions:
if (realizedPremium != 0) {
// Premium-based fee
uint128 commissionP = realizedPremium > 0
? uint128(realizedPremium)
: uint128(-realizedPremium);
uint128 commissionFeeP = uint128(
Math.mulDivRoundingUp(commissionP, riskParameters.premiumFee(), DECIMALS)
);
// Notional-based cap (10x notional fee)
uint128 commissionN = uint256(int256(shortAmount) + int256(longAmount)).toUint128();
uint128 commissionFeeN = uint128(
Math.mulDivRoundingUp(commissionN, 10 * riskParameters.notionalFee(), DECIMALS)
);
// Take the minimum
commissionFee = Math.min(commissionFeeP, commissionFeeN).toUint128();
}
_updateBalancesAndSettleβ
The shared internal function that handles core balance updates.
Function Signatureβ
function _updateBalancesAndSettle(
address optionOwner,
bool isCreation,
int128 longAmount,
int128 shortAmount,
int128 ammDeltaAmount,
int128 realizedPremium
) internal returns (uint32, int128, uint256, uint256)
Key Calculationsβ
Net Borrowsβ
int128 netBorrows = isCreation
? shortAmount - longAmount // Creating: shorts borrow, longs provide
: longAmount - shortAmount; // Closing: reversed
Token To Payβ
int256 tokenToPay = int256(ammDeltaAmount) - netBorrows - realizedPremium;
This represents the net token flow:
ammDeltaAmount: Tokens moved to/from UniswapnetBorrows: Tokens borrowed/returnedrealizedPremium: Premium paid/received
Credit Delta (for Long Positions)β
uint256 creditDelta;
if (longAmount > 0) {
creditDelta = isCreation
? Math.mulDivRoundingUp(uint256(longAmount), _totalSupply, _totalAssets)
: Math.mulDiv(uint256(longAmount), _totalSupply, _totalAssets);
}
Long positions create "credited shares" that affect total supply calculations.
Rounding Haircutβ
When closing positions, Uniswap rounding can cause more credited shares to be returned than were originally tracked:
if (!isCreation && creditDelta > 0) {
uint256 _creditedShares = s_creditedShares;
if (_creditedShares < creditDelta) {
s_creditedShares = 0;
// User pays the rounding difference
tokenToPay += int128(
Math.mulDivRoundingUp(
creditDelta - _creditedShares,
_totalAssets,
_totalSupply
).toUint128()
);
} else {
s_creditedShares -= creditDelta;
}
}
Share Minting/Burningβ
Based on tokenToPay:
if (tokenToPay > 0) {
// User pays tokens β burn their shares
uint256 sharesToBurn = Math.mulDivRoundingUp(
uint256(tokenToPay), _totalSupply, _totalAssets
);
if (balanceOf[optionOwner] < sharesToBurn)
revert Errors.NotEnoughTokens(...);
_burn(optionOwner, sharesToBurn);
} else if (tokenToPay < 0) {
// User receives tokens β mint shares
uint256 sharesToMint = Math.mulDiv(
uint256(-tokenToPay), _totalSupply, _totalAssets
);
_mint(optionOwner, sharesToMint);
}
State Updatesβ
// Update deposited assets (includes premium)
s_depositedAssets = uint256(
int256(uint256(s_depositedAssets)) - ammDeltaAmount + realizedPremium
).toUint128();
// Update AMM assets
int256 newAssetsInAmm = int256(uint256(s_assetsInAMM));
newAssetsInAmm += isCreation ? int256(shortAmount) : -int256(shortAmount);
s_assetsInAMM = uint256(newAssetsInAmm).toUint128();
// Update user's borrow state
s_interestState[optionOwner] = s_interestState[optionOwner].addToLeftSlot(netBorrows);
// Get and store utilization
uint32 utilization = uint32(_poolUtilization());
Settlement Examplesβ
Example 1: Selling a Put Optionβ
Action: Create a short put position worth 1,000 USDC
settleMint called with:
ββ longAmount = 0
ββ shortAmount = 1,000 USDC
ββ ammDeltaAmount = 1,000 USDC (deposited to Uniswap)
ββ notionalFee = 10 bps
Calculations:
ββ netBorrows = 1,000 - 0 = 1,000 USDC (borrowed)
ββ tokenToPay = 1,000 - 1,000 - 0 = 0 (net neutral)
ββ commission = 1,000 Γ 0.001 = 1 USDC
ββ Result: User pays 1 USDC commission, borrows 1,000 USDC
Example 2: Buying a Call Optionβ
Action: Create a long call position worth 500 USDC
settleMint called with:
ββ longAmount = 500 USDC
ββ shortAmount = 0
ββ ammDeltaAmount = -500 USDC (removed from Uniswap)
ββ notionalFee = 10 bps
Calculations:
ββ netBorrows = 0 - 500 = -500 USDC (provided)
ββ tokenToPay = -500 - (-500) - 0 = 0 (net neutral)
ββ creditDelta = shares for 500 USDC
ββ commission = 500 Γ 0.001 = 0.5 USDC
ββ Result: User pays 0.5 USDC commission, provides 500 USDC
Example 3: Closing Profitable Longβ
Action: Close long position with 100 USDC profit
settleBurn called with:
ββ longAmount = 500 USDC
ββ shortAmount = 0
ββ ammDeltaAmount = 500 USDC (returned from Uniswap)
ββ realizedPremium = 100 USDC (profit)
ββ premiumFee = 5 bps
Calculations:
ββ netBorrows = 500 - 0 = 500 USDC (returned)
ββ tokenToPay = 500 - 500 - 100 = -100 (user receives)
ββ premiumFee = 100 Γ 0.0005 = 0.05 USDC
ββ notionalCap = 500 Γ 10 Γ 0.001 = 5 USDC
ββ commissionFee = min(0.05, 5) = 0.05 USDC
ββ Result: User receives ~99.95 USDC profit
Summaryβ
| Aspect | settleMint | settleBurn |
|---|---|---|
| Trigger | Position creation | Position closure |
| Net Borrows | shorts - longs | longs - shorts |
| Fee Basis | Notional only | Premium (capped by notional) |
| Credit Shares | Added | Removed |
| AMM Assets | Increased | Decreased |
| Premium | Not applicable | Settled |
The settlement system ensures:
- Accurate tracking of all asset flows
- Fair fee collection with builder revenue sharing
- Proper handling of borrowed amounts and interest
- Protection against rounding exploits
- Consistent utilization tracking via transient storage