Skip to main content

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:

FunctionTriggerPurpose
settleMintPosition creationHandle deposits, update tracking, charge commission
settleBurnPosition closureHandle 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
​

ParameterDescription
optionOwnerAddress creating the position
longAmountNotional value of long legs
shortAmountNotional value of short legs
ammDeltaAmountTokens moved to/from Uniswap
riskParametersCurrent risk parameters (fees, safe mode, etc.)

Returns
​

ReturnDescription
utilizationPool utilization after the mint (basis points)
tokenPaidNet 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
​

ParameterDescription
optionOwnerAddress closing the position
longAmountNotional value of long legs being closed
shortAmountNotional value of short legs being closed
ammDeltaAmountTokens moved from Uniswap
realizedPremiumPremium to settle (positive = paid to owner)
riskParametersCurrent risk parameters

Returns
​

ReturnDescription
tokenPaidNet 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 Uniswap
  • netBorrows: Tokens borrowed/returned
  • realizedPremium: 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
​

AspectsettleMintsettleBurn
TriggerPosition creationPosition closure
Net Borrowsshorts - longslongs - shorts
Fee BasisNotional onlyPremium (capped by notional)
Credit SharesAddedRemoved
AMM AssetsIncreasedDecreased
PremiumNot applicableSettled

The settlement system ensures:

  1. Accurate tracking of all asset flows
  2. Fair fee collection with builder revenue sharing
  3. Proper handling of borrowed amounts and interest
  4. Protection against rounding exploits
  5. Consistent utilization tracking via transient storage