Skip to main content

Saturation

Git Source

Authors: imi@1m1.io, Will duelingGalois@protonmail.com

Saturation (=sat) is defined as the net borrow. In theory, we would want to divide net borrow by the total liquidity; in practice, we keep the net borrow only in the tree. The unit of sat is relative to active liquidity assets, or the amount of L deposited less the amount borrowed. When we determine how much a swap moves the price, or square root price, we can define our equation using ticks, or tranches (100 ticks), where for some base bb, the square root price is btb^t for some tick tt. Alternatively for a larger base B=b100B = b^{100} we can define the square root price as BTB^T for some tranche TT. Using the square root price, we can define the amount of x or y in each tranche as x=LBT0LBT1x = LB^{T_0} - LB^{T_1} and y=LBT1LBT0y= \frac{L}{ B^{T_1}} - \frac{L}{ B^{T_0}}, where liquidity is L=reserveXreserveYL = \sqrt{reserveX \cdot reserveY}. If we want to know how much debt of x or y can be liquidated within one tranche, we can solve these equations for L and then the amount of x and y are considered the debt we would like to see if it could be liquidated in one tranche. If saturation with respect to our starting LL is smaller, that amount of debt can be liquidated in one swap in the given tranche. Otherwise it is to big and can not. Note that we assume T1 and T0ZT_1 \text{ and } T_0 \in \mathbb{Z} and T0+1=T1T_0 + 1 = T_1. Then our definition of saturation relative to L is as follows,

saturationRelativeToL={debtXBT1(BB1)debtYBT0(BB1)\begin{equation} saturationRelativeToL = \begin{cases} \frac{debtX}{B^{T_{1}}}\left(\frac{B}{B-1}\right) \\ debtY\cdot B^{T_{0}}\cdot\left(\frac{B}{B-1}\right) \end{cases} \end{equation}

Saturation is kept in a tree, starting with a root, levels and leafs. We keep 2 trees, one for net X borrows, another for net Y borrows. The price is always the price of Y in units of X. Mostly, the code works with the sqrt of price. A net X borrow refers to a position that if liquidated would cause the price to become smaller; the opposite for net Y positions. Ticks are along the price dimension and int16. Tranches are 100 ticks, stored as int16. Leafs (uint16) split the sat, which is uint112, into intervals. From left to right, the leafs of the tree cover the sat space in increasing order. Each account with a position has a price at which its LTV would reach LTVMAX, which is its liquidation (=liq) price. To place a debt into the appropriate tranche, we think of each debt and its respective collateral as a serries of sums, where each item in the series fits in one tranche. Using formulas above, we determine the number of ticks a debt would cross if liquidated. This is considered the span of the liquidation. Using this value we then determine the start and end points of the liquidation, where the start would be closer to the prices, on the right of the end for net debt of x and on the left of the end for net debt of Y. Once we have the liquidation start, end, and span, we begin to place the debt, one tranche at a time moving towards the price. In this process we compare the prior recorded saturation and allow the insertion up to some max, set at 90% or the configuration set by the user. A Tranche contains multiple accounts and thus a total sat. The tranches' sat assigns it to a leaf. Each leaf can contain multiple tranches and thus has a total actual sat whilst representing a specific sat per tranche range. Leafs and thus tranches and thus accounts above a certain sat threshold are considered over saturated. These accounts are penalized for being in an over saturated tranche. Each account, tranche and leaf has a total penalty that needs to be repaid to flatten the position fully. Sat is distributed over multiple tranches, in case a single tranche does not have enough available sat left. Sat is kept cumulatively in the tree, meaning a node contains the sum of the sat of its parents. Updating a sat at the bottom of the tree requires updating all parents. Penalty is kept as a path sum, in uints of LAssets, meaning the penalty of an account is the sum of the penalties of all its parents. Updating the penalty for a range of leafs only requires updating the appropriate parent. Position (=pos) refers to the relative index of a child within its parent. Index refers to the index of a node in within its level

State Variables

SATURATION_TIME_BUFFER_IN_MAG2

uint256 internal constant SATURATION_TIME_BUFFER_IN_MAG2 = 101;

MAX_SATURATION_RATIO_IN_MAG2

uint256 internal constant MAX_SATURATION_RATIO_IN_MAG2 = 95;

START_SATURATION_PENALTY_RATIO_IN_MAG2

uint256 internal constant START_SATURATION_PENALTY_RATIO_IN_MAG2 = 85;

MAX_INITIAL_SATURATION_MAG2

uint256 internal constant MAX_INITIAL_SATURATION_MAG2 = 90;

EXPECTED_SATURATION_LTV_MAG2

uint256 internal constant EXPECTED_SATURATION_LTV_MAG2 = 85;

EXPECTED_SATURATION_LTV_MAG2_TIMES_SAT_BUFFER_SQUARED

uint256 internal constant EXPECTED_SATURATION_LTV_MAG2_TIMES_SAT_BUFFER_SQUARED = 867_085;

EXPECTED_SATURATION_LTV_PLUS_ONE_MAG2

uint256 internal constant EXPECTED_SATURATION_LTV_PLUS_ONE_MAG2 = 185;

PENALTY_FACTOR_IN_MAG2

uint256 private constant PENALTY_FACTOR_IN_MAG2 = 10;

SAT_CHANGE_OF_BASE_Q128

uint256 private constant SAT_CHANGE_OF_BASE_Q128 = 0xa39713406ef781154a9e682c2331a7c03;

SAT_CHANGE_OF_BASE_TIMES_SHIFT

uint256 private constant SAT_CHANGE_OF_BASE_TIMES_SHIFT = 0xb3f2fb93ad437464387b0c308d1d05537;

TICK_OFFSET

int16 private constant TICK_OFFSET = 1112;

LOWEST_POSSIBLE_IN_PENALTY

uint256 internal constant LOWEST_POSSIBLE_IN_PENALTY = 0xd9999999999999999999999999999999;

MIN_LIQ_TO_REACH_PENALTY

uint256 private constant MIN_LIQ_TO_REACH_PENALTY = 850;

INT_ONE

int256 private constant INT_ONE = 1;

INT_NEGATIVE_ONE

int256 private constant INT_NEGATIVE_ONE = -1;

INT_ZERO

int256 private constant INT_ZERO = 0;

LEVELS_WITHOUT_LEAFS

uint256 internal constant LEVELS_WITHOUT_LEAFS = 3;

LOWEST_LEVEL_INDEX

uint256 internal constant LOWEST_LEVEL_INDEX = 2;

LEAFS

uint256 internal constant LEAFS = 4096;

CHILDREN_PER_NODE

uint256 internal constant CHILDREN_PER_NODE = 16;

CHILDREN_AT_THIRD_LEVEL

uint256 private constant CHILDREN_AT_THIRD_LEVEL = 256;

TICKS_PER_TRANCHE

int256 private constant TICKS_PER_TRANCHE = 100;

TRANCHE_BASE_OVER_BASE_MINUS_ONE_Q72

uint256 constant TRANCHE_BASE_OVER_BASE_MINUS_ONE_Q72 = 0x5a19b9039a07efd7b39;

MIN_TRANCHE

int256 internal constant MIN_TRANCHE = -199;

MAX_TRANCHE

int256 internal constant MAX_TRANCHE = 198;

FIELD_NODE_MASK

uint256 private constant FIELD_NODE_MASK = 0xffff;

SATURATION_MAX_BUFFER_TRANCHES

uint8 internal constant SATURATION_MAX_BUFFER_TRANCHES = 3;

QUARTER_MINUS_ONE

uint256 private constant QUARTER_MINUS_ONE = 24;

QUARTER_OF_MAG2

uint256 private constant QUARTER_OF_MAG2 = 25;

NUMBER_OF_QUARTERS

uint256 private constant NUMBER_OF_QUARTERS = 4;

SOFT_LIQUIDATION_SCALER

uint256 private constant SOFT_LIQUIDATION_SCALER = 10_020;

TWO_Q64

uint256 private constant TWO_Q64 = 0x20000000000000000;

FOUR_Q128

uint256 private constant FOUR_Q128 = 0x400000000000000000000000000000000;

MAG4_TIMES_Q64

uint256 private constant MAG4_TIMES_Q64 = 0x27100000000000000000;

B_Q72_MINUS_ONE

uint256 private constant B_Q72_MINUS_ONE = 0x1008040201008040200;

Functions

initializeSaturationStruct

initializes the satStruct, allocating storage for all nodes

initCheck can be removed once the tree structure is fixed

function initializeSaturationStruct(
SaturationStruct storage satStruct
) internal;

Parameters

NameTypeDescription
satStructSaturationStructcontains the entire sat data

initTree

init the nodes of the tree

function initTree(
Tree storage tree
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to

update

update the borrow position of an account and potentially check (and revert) if the resulting sat is too high

run accruePenalties before running this function

function update(
SaturationStruct storage satStruct,
Validation.InputParams memory inputParams,
address account,
uint256 userSaturationRatioMAG2
) internal;

Parameters

NameTypeDescription
satStructSaturationStructmain data struct
inputParamsValidation.InputParamscontains the position and pair params, like account borrows/deposits, current price and active liquidity
accountaddressfor which is position is being updated
userSaturationRatioMAG2uint256

updateTreeGivenAccountTrancheAndSat

internal update that removes the account from the tree (if it exists) from its prev position and adds it to its new position

function updateTreeGivenAccountTrancheAndSat(
Tree storage tree,
SaturationPair memory newSaturation,
address account,
int256 newEndOfLiquidationInTicks,
uint256 activeLiquidityInLAssets,
uint256 userSaturationRatioMAG2
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
newSaturationSaturationPairthe new sat of the account, in units of LAssets (absolute) and relative to active liquidity
accountaddresswhos position is being considered
newEndOfLiquidationInTicksint256the new tranche of the account in mag2.
activeLiquidityInLAssetsuint256of the pair
userSaturationRatioMAG2uint256

removeSatFromTranche

remove sat from tree, for each tranche in a loop that could hold sat for the account

function removeSatFromTranche(
Tree storage tree,
address account,
int256 trancheDirection
) internal returns (bool highestSetLeafRemoved);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
accountaddresswhos position is being considered
trancheDirectionint256direction of sat distribution depending on netX/netY

Returns

NameTypeDescription
highestSetLeafRemovedboolflag indicating whether we removed sat from the highest leaf xor not

removeSatFromTrancheStateUpdates

depending on old and new leaf of the tranche, update the sats, fields and penalties of the tree

function removeSatFromTrancheStateUpdates(
Tree storage tree,
SaturationPair memory oldAccountSaturationInTranche,
int256 tranche,
uint256 oldLeaf,
address account,
uint256 trancheIndex
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
oldAccountSaturationInTrancheSaturationPairaccount sat
trancheint256under consideration
oldLeafuint256where tranche was located before this sat removal
accountaddressneeded to accrue penalty
trancheIndexuint256which tranche of the account are we handling?

addSatToTranche

add sat to tree, for each tranche in a loop as needed. we add to each tranche as much as it can bear.

*Saturation Distribution Logic This function distributes debt across multiple tranches, maintaining two types of saturation:

  1. satInLAssets: The absolute debt amount in L assets (should remain constant total)
  2. satRelativeToL: The relative saturation that depends on the tranche's price level As we move between tranches (different price levels), the same absolute debt translates to different relative saturations due to the price-dependent formula. conceptually satInLAssets should not be scaled as it represents actual debt that doesn't change with price.*
function addSatToTranche(
Tree storage tree,
address account,
int256 trancheDirection,
int256 newEndOfLiquidationInTicks,
SaturationPair memory newSaturation,
uint256 activeLiquidityInLAssets,
uint256 userSaturationRatioMAG2
) internal returns (bool highestSetLeafAdded);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
accountaddresswho's position is being considered
trancheDirectionint256direction of sat distribution depending on netX/netY
newEndOfLiquidationInTicksint256the new tranche of the account location in MAG2
newSaturationSaturationPairthe new sat of the account, in units of LAssets (absolute) and relative to active liquidity
activeLiquidityInLAssetsuint256of the pair
userSaturationRatioMAG2uint256

Returns

NameTypeDescription
highestSetLeafAddedboolflag indicating whether we removed sat from the highest leaf xor not

getAddSatToTrancheStateUpdatesParams

convenience struct holding the params needed to run addSatToTrancheStateUpdates

function getAddSatToTrancheStateUpdatesParams(
Tree storage tree,
int256 tranche,
SaturationPair memory newSaturation,
uint256 activeLiquidityInLAssets,
address account,
uint256 userSaturationRatioMAG2,
uint256 quarters
) internal view returns (AddSatToTrancheStateUpdatesStruct memory addSatToTrancheStateUpdatesParams);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
trancheint256under consideration
newSaturationSaturationPairthe saturation values to add
activeLiquidityInLAssetsuint256of the pair
accountaddresswhos position is being considered
userSaturationRatioMAG2uint256user saturation ratio
quartersuint256number of quarters for the calculation

Returns

NameTypeDescription
addSatToTrancheStateUpdatesParamsAddSatToTrancheStateUpdatesStructthe struct with required params to

addSatToTrancheStateUpdates

depending on old and new leaf of the tranche, update the sats, fields and penalties of the tree

function addSatToTrancheStateUpdates(
Tree storage tree,
AddSatToTrancheStateUpdatesStruct memory params
) internal returns (uint256);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
paramsAddSatToTrancheStateUpdatesStructconvenience struct holding params needed for these updates

addSatToTrancheStateUpdatesHigherLeaf

Add sat to tranche state updates higher leaf

function addSatToTrancheStateUpdatesHigherLeaf(
Tree storage tree,
int256 tranche,
SaturationPair memory oldTrancheSaturation,
SaturationPair memory newTrancheSaturation,
uint256 oldLeaf,
uint256 newLeaf
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
trancheint256the tranche that is being moved
oldTrancheSaturationSaturationPairthe old sat of the tranche
newTrancheSaturationSaturationPairthe new sat of the tranche
oldLeafuint256the leaf that the tranche was located in before it was removed
newLeafuint256the leaf that the tranche was located in after it was removed

removeTrancheToLeaf

removing a tranche from a leaf, update the fields and sats up the tree

function removeTrancheToLeaf(
Tree storage tree,
SaturationPair memory trancheSaturation,
int256 tranche,
uint256 leaf
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
trancheSaturationSaturationPairthe saturation of the tranche being moved
trancheint256that is being moved
leafuint256the leaf

addTrancheToLeaf

adding a tranche from a leaf, update the fields and sats up the tree

function addTrancheToLeaf(
Tree storage tree,
SaturationPair memory trancheSaturation,
int256 tranche,
uint256 leaf
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
trancheSaturationSaturationPairthe saturation of the tranche being moved
trancheint256that is being moved
leafuint256the leaf

addSatUpTheTree

recursively add sat up the tree

function addSatUpTheTree(Tree storage tree, int256 satInLAssets) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
satInLAssetsint256sat to add to the current node, usually uint112, int to allow subtracting sat up the tree

updatePenalties

update penalties in the tree given

function updatePenalties(
Tree storage tree,
uint256 thresholdLeaf,
uint256 addPenaltyInBorrowLSharesPerSatInQ72
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
thresholdLeafuint256from which leaf on the penalty needs to be added inclusive
addPenaltyInBorrowLSharesPerSatInQ72uint256the penalty to be added

getPenaltySharesPerSatFromLeaf

recursive function to sum penalties from leaf to root

function getPenaltySharesPerSatFromLeaf(
Tree storage tree,
uint256 leaf
) private view returns (uint256 penaltyInBorrowLSharesPerSatInQ72);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
leafuint256index (0 based) of the leaf

Returns

NameTypeDescription
penaltyInBorrowLSharesPerSatInQ72uint256total penalty at the leaf, non-negative but returned as an int for recursion

accrueAccountPenalty

calc penalty owed by account for repay, total over all the tranches that might contain this accounts' sat

function accrueAccountPenalty(Tree storage tree, address account) internal returns (uint256 penaltyInBorrowLShares);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
accountaddresswho's position is being considered

Returns

NameTypeDescription
penaltyInBorrowLSharesuint256the penalty owed by the account

calcNewAccountPenalty

calc penalty owed by account for repay, total over all the tranches that might contain this accounts' sat

function calcNewAccountPenalty(
Tree storage tree,
uint256 leaf,
uint256 accountSatInTrancheInLAssets,
address account,
uint256 trancheIndex
) private view returns (uint256 penaltyInBorrowLShares, uint256 accountTreePenaltyInBorrowLSharesPerSatInQ72);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
leafuint256the leaf that the tranche belongs to
accountSatInTrancheInLAssetsuint256the sat of the account in the tranche
accountaddresswho's position is being considered
trancheIndexuint256the index of the tranche that is being added to

Returns

NameTypeDescription
penaltyInBorrowLSharesuint256the penalty owed by the account
accountTreePenaltyInBorrowLSharesPerSatInQ72uint256the penalty owed by the account in the tranche

calcAndAccrueNewAccountPenalty

calc and accrue new account penalty

function calcAndAccrueNewAccountPenalty(
Tree storage tree,
SaturationPair memory oldAccountSaturationInTranche,
uint256 oldLeaf,
address account,
uint256 trancheIndex,
uint256 newTreePenaltyAtOnsetInBorrowLSharesPerSatInQ72PerTranche
) private;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
oldAccountSaturationInTrancheSaturationPairthe old sat of the account in the tranche
oldLeafuint256the leaf that the tranche was located in before it was removed
accountaddresswho's position is being considered
trancheIndexuint256the index of the tranche that is being added to
newTreePenaltyAtOnsetInBorrowLSharesPerSatInQ72PerTrancheuint256the new penalty at onset in borrow l shares per sat in q72 per tranche

accruePenalties

accrue penalties since last accrual based on all over saturated positions

function accruePenalties(
SaturationStruct storage satStruct,
address account,
uint256 externalLiquidity,
uint256 duration,
uint256 allAssetsDepositL,
uint256 allAssetsBorrowL,
uint256 allSharesBorrowL
) internal returns (uint112 penaltyInBorrowLShares, uint112 accountPenaltyInBorrowLShares);

Parameters

NameTypeDescription
satStructSaturationStructmain data struct
accountaddresswho's position is being considered
externalLiquidityuint256Swap liquidity outside this pool
durationuint256since last accrual of penalties
allAssetsDepositLuint256allAsset[DEPOSIT_L]
allAssetsBorrowLuint256allAsset[BORROW_L]
allSharesBorrowLuint256allShares[BORROW_L]

Returns

NameTypeDescription
penaltyInBorrowLSharesuint112the penalty owed by the account
accountPenaltyInBorrowLSharesuint112the penalty owed by the account

calcNewPenalties

calc new penalties

function calcNewPenalties(
SaturationStruct storage satStruct,
uint256 externalLiquidity,
uint256 duration,
uint256 allAssetsDepositL,
uint256 allAssetsBorrowL,
uint256 allSharesBorrowL
)
private
view
returns (
uint256 penaltyNetXInBorrowLShares,
uint256 penaltyNetXInBorrowLSharesPerSatInQ72,
uint256 penaltyNetYInBorrowLShares,
uint256 penaltyNetYInBorrowLSharesPerSatInQ72,
uint256 thresholdLeaf
);

Parameters

NameTypeDescription
satStructSaturationStructmain data struct
externalLiquidityuint256Swap liquidity outside this pool
durationuint256since last accrual of penalties
allAssetsDepositLuint256allAsset[DEPOSIT_L]
allAssetsBorrowLuint256allAsset[BORROW_L]
allSharesBorrowLuint256allShares[BORROW_L]

Returns

NameTypeDescription
penaltyNetXInBorrowLSharesuint256the penalty net X in borrow l shares
penaltyNetXInBorrowLSharesPerSatInQ72uint256the penalty net X in borrow l shares per sat in q72
penaltyNetYInBorrowLSharesuint256the penalty net Y in borrow l shares
penaltyNetYInBorrowLSharesPerSatInQ72uint256the penalty net Y in borrow l shares per sat in q72
thresholdLeafuint256the threshold leaf

calcNewPenaltiesGivenTree

calc new penalties given tree

function calcNewPenaltiesGivenTree(
Tree storage tree,
uint256 thresholdLeaf,
uint256 duration,
uint256 currentBorrowUtilizationInWad,
uint256 saturationUtilizationInWad,
uint256 allAssetsDepositL,
uint256 allAssetsBorrowL,
uint256 allSharesBorrowL
) private view returns (uint256 penaltyInBorrowLShares, uint256 penaltyInBorrowLSharesPerSatInQ72);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
thresholdLeafuint256the threshold leaf
durationuint256since last accrual of penalties
currentBorrowUtilizationInWaduint256current borrow utilization in WAD
saturationUtilizationInWaduint256saturation utilization in WAD
allAssetsDepositLuint256allAsset[DEPOSIT_L]
allAssetsBorrowLuint256allAsset[BORROW_L]
allSharesBorrowLuint256allShares[BORROW_L]

Returns

NameTypeDescription
penaltyInBorrowLSharesuint256the penalty net X in borrow l shares
penaltyInBorrowLSharesPerSatInQ72uint256the penalty net X in borrow l shares per sat in q72

accrueAndRemoveAccountPenalty

accrue and remove account penalty

function accrueAndRemoveAccountPenalty(
SaturationStruct storage satStruct,
address account
) internal returns (uint112 penaltyInBorrowLShares);

Parameters

NameTypeDescription
satStructSaturationStructmain data struct
accountaddresswho's position is being considered

Returns

NameTypeDescription
penaltyInBorrowLSharesuint112the penalty owed by the account

calculateHardLiquidationPremium

calculate the max liquidation premium in bips for a hard liquidation uses the tree * to determine to allow for partial liquidations as they occur.

notice that input params are mutated but then returned to their original state.

function calculateHardLiquidationPremium(
Saturation.SaturationStruct storage satStruct,
Validation.InputParams memory inputParams,
address borrower,
uint256 netBorrowRepaidLAssets,
uint256 netDepositSeizedLAssets,
bool netDebtX
) internal view returns (uint256 maxPremiumInBips, bool allAssetsSeized);

Parameters

NameTypeDescription
satStructSaturation.SaturationStructmain data struct
inputParamsValidation.InputParamsall user assets and prices
borroweraddress
netBorrowRepaidLAssetsuint256net debt repaid in liquidity assets
netDepositSeizedLAssetsuint256net collateral seized in liquidity assets
netDebtXboolwhether net debt is in X or Y

Returns

NameTypeDescription
maxPremiumInBipsuint256the max premium in bips that
allAssetsSeizedbool

mutateInputParamsForPartialLiquidation

mutate input params to only include the eligible debt and collateral for ltv calculation

function mutateInputParamsForPartialLiquidation(
Saturation.SaturationStruct storage satStruct,
Validation.InputParams memory inputParams,
address borrower,
uint256 netBorrowRepaidLAssets,
bool netDebtX
) internal view returns (uint256 netDepositInLAssets);

Parameters

NameTypeDescription
satStructSaturation.SaturationStructmain data struct
inputParamsValidation.InputParamsall user assets and prices
borroweraddressborrower address
netBorrowRepaidLAssetsuint256net debt in liquidity assets
netDebtXboolwhether net debt is in X or Y

calcPortionsForPartialLiquidation

Calculate the percent of debt and collateral that is eligible for ltv calculation

note that we assume that the min and max sqrt price are switched prior to calling this.

function calcPortionsForPartialLiquidation(
Saturation.SaturationStruct storage satStruct,
address borrower,
uint256 netBorrowRepaidLAssets,
bool netDebtX
) internal view returns (uint256 partialBorrow, uint256 totalBorrow, uint256 partialDeposit, uint256 totalDeposit);

setXorUnsetFieldBitUpTheTree

recursive function to unset the field when removing a tranche from a leaf

function setXorUnsetFieldBitUpTheTree(
Tree storage tree,
uint256 level,
uint256 nodeIndex,
uint256 lowerNodePos,
uint256 set
) internal;

Parameters

NameTypeDescription
treeTreethat is being read from or written to
leveluint256level being updated
nodeIndexuint256index is the position (0 based) of the node in its level
lowerNodePosuint256pos is the relative position (0 based) of the node in its parent
setuint2561 for set, 0 for unset

findHighestSetLeafUpwards

recursive function to find the highest set leaf starting from a leaf, first upwards, until a set field is found, then downwards to find the best set leaf

function findHighestSetLeafUpwards(
Tree storage tree,
uint256 level,
uint256 nodeIndex
) private view returns (uint256 highestSetLeaf);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
leveluint256that we are checking
nodeIndexuint256corresponding to our leaf at our level

Returns

NameTypeDescription
highestSetLeafuint256highest leaf that is set in the tree

findHighestSetLeafDownwards

recursive function to find the highest set leaf starting from a node, downwards

internal for testing only

function findHighestSetLeafDownwards(
Tree storage tree,
uint256 level,
uint256 nodeIndex
) internal view returns (uint256 leaf);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
leveluint256that we are starting from
nodeIndexuint256that we are starting from

Returns

NameTypeDescription
leafuint256highest leaf under the node that is set

calcLiqSqrtPriceQ72

Calc sqrt price at which positions' LTV would reach LTV_MAX

Output guarantees 0liqSqrtPriceXInQ72uint256(type(uint56).max)<<720 \le liqSqrtPriceXInQ72 \le uint256(type(uint56).max) << 72 (fuzz tested and logic)

Outside above range, outputs 0 (essentially no liq)

Does not revert if LTVMAX<LTVLTV_MAX < LTV, rather LTVMAX<LTVLTV_MAX < LTV causing liq points are returned as 0, as if they do not exist, based on the assumption LTVLTVMAXLTV \le LTV_MAX

function calcLiqSqrtPriceQ72(
uint256[6] memory userAssets
) internal pure returns (uint256 netDebtXLiqSqrtPriceXInQ72, uint256 netDebtYLiqSqrtPriceXInQ72);

Parameters

NameTypeDescription
userAssetsuint256[6]The position

Returns

NameTypeDescription
netDebtXLiqSqrtPriceXInQ72uint2560 if no netX liq price exists
netDebtYLiqSqrtPriceXInQ72uint2560 if no netY liq price exists

calcLiqSqrtPriceQ72HandleAllABCNonZero

calc liq price when the quadratic has all 3 terms, netY,netL,netX, i.e. X, Y, L are all significant

function calcLiqSqrtPriceQ72HandleAllABCNonZero(
CalcLiqSqrtPriceHandleAllABCNonZeroStruct memory input
) internal pure returns (uint256 netDebtXLiqSqrtPriceXInQ72, uint256 netDebtYLiqSqrtPriceXInQ72);

Parameters

NameTypeDescription
inputCalcLiqSqrtPriceHandleAllABCNonZeroStructthe position

Returns

NameTypeDescription
netDebtXLiqSqrtPriceXInQ72uint2560 if no netX liq price exists
netDebtYLiqSqrtPriceXInQ72uint2560 if no netY liq price exists

calcSatChangeRatioBips

Calculate the ratio by which the saturation has changed for account.

function calcSatChangeRatioBips(
SaturationStruct storage satStruct,
Validation.InputParams memory inputParams,
uint256 liqSqrtPriceInXInQ72,
uint256 liqSqrtPriceInYInQ72,
address account,
uint256 desiredSaturationMAG2
) internal view returns (uint256 ratioNetXBips, uint256 ratioNetYBips);

Parameters

NameTypeDescription
satStructSaturationStructThe saturation struct containing both netX and netY trees.
inputParamsValidation.InputParamsThe params containing the position of account.
liqSqrtPriceInXInQ72uint256The liquidation price for netX.
liqSqrtPriceInYInQ72uint256The liquidation price for netY.
accountaddressThe account for which we are calculating the saturation change ratio.
desiredSaturationMAG2uint256

Returns

NameTypeDescription
ratioNetXBipsuint256The ratio representing the change in netX saturation for account.
ratioNetYBipsuint256The ratio representing the change in netY saturation for account.

calcTotalSatAfterLeafInclusive

calc total sat of all accounts/tranches/leafs higher (and same) as the threshold

iterate through leaves directly since penalty range is fixed (~8 leaves from 85% to 95% sat)

function calcTotalSatAfterLeafInclusive(
Tree storage tree,
uint256 thresholdLeaf
) internal view returns (uint128 satInLAssetsInPenalty);

Parameters

NameTypeDescription
treeTreethat is being read from or written to
thresholdLeafuint256leaf to start adding sat from

Returns

NameTypeDescription
satInLAssetsInPenaltyuint128total sat of all accounts with tranche in a leaf from at least thresholdLeaf (absolute saturation)

getSatPercentageInWads

Get precalculated saturation percentage for a given delta (maxLeaf - highestLeaf)

function getSatPercentageInWads(
SaturationStruct storage satStruct
) internal view returns (uint256 saturationPercentage);

Parameters

NameTypeDescription
satStructSaturationStructThe saturation struct

Returns

NameTypeDescription
saturationPercentageuint256The precalculated saturation percentage as uint256

satToLeaf

convert sat to leaf

function satToLeaf(
uint256 satLAssets
) internal pure returns (uint256 leaf);

Parameters

NameTypeDescription
satLAssetsuint256sat to convert

Returns

NameTypeDescription
leafuint256resulting leaf from 0 to 2**12-1

calcSatAvailableToAddToTranche

calc how much sat can be added to a tranche such that it is healthy

function calcSatAvailableToAddToTranche(
uint256 activeLiquidityInLAssets,
uint128 targetSatToAddInLAssets,
uint128 currentTrancheSatInLAssets,
uint256 userSaturationRatioMAG2,
uint256 quarters
) internal pure returns (uint128 satAvailableToAddInLAssets);

Parameters

NameTypeDescription
activeLiquidityInLAssetsuint256of the pair
targetSatToAddInLAssetsuint128the sat that we want to add
currentTrancheSatInLAssetsuint128the sat that the tranche already hols
userSaturationRatioMAG2uint256
quartersuint256

Returns

NameTypeDescription
satAvailableToAddInLAssetsuint128considering the currentTrancheSatInLAssets and the max a tranche can have

calcLastTrancheAndSaturation

calc the tranche percent and the saturation of the tranche

function calcLastTrancheAndSaturation(
Validation.InputParams memory inputParams,
uint256 liqSqrtPriceInXInQ72,
uint256 desiredThresholdMag2,
bool netDebtX
) internal pure returns (int256 endOfLiquidationInTicks, SaturationPair memory saturation);

Parameters

NameTypeDescription
inputParamsValidation.InputParamsthe input params
liqSqrtPriceInXInQ72uint256the liq sqrt price in X
desiredThresholdMag2uint256the desired threshold
netDebtXboolwhether the net debt is X or Y

Returns

NameTypeDescription
endOfLiquidationInTicksint256the point at which the liquidation would end.
saturationSaturationPairthe saturation of the tranche

calculateNetDebtAndSpan

calc net debt and span

function calculateNetDebtAndSpan(
Validation.InputParams memory inputParams,
uint256 desiredThresholdMag2,
bool netDebtX
) internal pure returns (uint256 netDebtXorYAssets, uint256 netDebtLAssets, uint256 trancheSpanInTicks);

Parameters

NameTypeDescription
inputParamsValidation.InputParamsthe input params
desiredThresholdMag2uint256the desired threshold
netDebtXboolwhether the net debt is X or Y

Returns

NameTypeDescription
netDebtXorYAssetsuint256the net debt
netDebtLAssetsuint256the net debt in L assets
trancheSpanInTicksuint256the tranche span percentage

calculateSaturation

calculate the relative saturation of the position at the end of liquidation.

*Since we place saturation in tranches starting at the end and moving forward, this calculates the entire saturation as if it would fit in the last tranche, we then need to adjust the saturation each time we move to the next tranche by dividing by a factor of BB. The equation here is slightly different than the equation in our description since we multiply by a factor of BB for each tranche we move back away from the start. thus here we use, where TCountTCount is the number of tranches we need to move back,

saturationRelativeToL={debtXBT1(BTCountB1)debtYBT0(BTCountB1)\begin{equation} saturationRelativeToL = \begin{cases} \frac{debtX}{B^{T_{1}}}\left(\frac{B^{TCount}}{B-1}\right) \\ debtY\cdot B^{T_{0}}\cdot\left(\frac{B^{TCount}}{B-1}\right) \end{cases} \end{equation}

As we iterate through tranches, we divide by a factor of BB such that when we reach the final tranche, our equation from the start applies.*

function calculateSaturation(
uint256 netDebtXOrYAssets,
uint256 startSqrtPriceQ72,
uint256 trancheSpanInTicks,
bool netDebtX
) internal pure returns (uint256 saturation);

Parameters

NameTypeDescription
netDebtXOrYAssetsuint256the net debt in X or Y assets.
startSqrtPriceQ72uint256the sqrt price at the start of liquidation
trancheSpanInTicksuint256the span of the tranche in ticks.
netDebtXboolwhether the debt is in X or Y assets

Returns

NameTypeDescription
saturationuint256the saturation relative to active liquidity assets.

calcMinTrancheSpanInTicks

calc the minimum tranche count given the collateral, debt, active liquidity and desired threshold

function calcMinTrancheSpanInTicks(
uint256 collateral,
uint256 debt,
uint256 activeLiquidityAssets,
uint256 desiredThresholdMag2
) internal pure returns (uint256 trancheSpanInTicks);

Parameters

NameTypeDescription
collateraluint256the collateral amount
debtuint256the debt amount
activeLiquidityAssetsuint256the active liquidity assets
desiredThresholdMag2uint256the desired threshold

Returns

NameTypeDescription
trancheSpanInTicksuint256the tranche span a position will need. When greater than TICKS_PER_TRANCHE, multiple tranches are needed.

calcTrancheAtStartOfLiquidation

calc the tranche at start of liquidation

function calcTrancheAtStartOfLiquidation(
uint256 netDebtXorYAssets,
uint256 activeLiquidityAssets,
uint256 trancheSpanInTicks,
uint256 desiredThresholdMag2,
bool netDebtX
) internal pure returns (int256 trancheStartOfLiquidationMag2);

Parameters

NameTypeDescription
netDebtXorYAssetsuint256the net debt in X or Y assets.
activeLiquidityAssetsuint256the active liquidity assets
trancheSpanInTicksuint256the tranche span percentage
desiredThresholdMag2uint256the desired threshold
netDebtXboolwhether the net debt is X or Y

Returns

NameTypeDescription
trancheStartOfLiquidationMag2int256the tranche at start of liquidation

readFieldBitFromNode

read single bit value from the field of a node

function readFieldBitFromNode(uint256 node, uint256 bitPos) internal pure returns (uint256 bit);

Parameters

NameTypeDescription
nodeuint256the full node
bitPosuint256position of the bit 16\le 16

Returns

NameTypeDescription
bituint256the resulting bit, 0 xor 1, as a uint

writeFlippedFieldBitToNode

write to node

function writeFlippedFieldBitToNode(uint256 nodeIn, uint256 bitPos) internal pure returns (uint256 nodeOut);

Parameters

NameTypeDescription
nodeInuint256node to read from
bitPosuint256position of the bit 16\le 16

Returns

NameTypeDescription
nodeOutuint256node with bit flipped

readFieldFromNode

read field from node

function readFieldFromNode(
uint256 node
) internal pure returns (uint256 field);

Parameters

NameTypeDescription
nodeuint256node to read from

Returns

NameTypeDescription
fielduint256field of the node

calcSaturationPenaltyRatePerSecondInWads

Calculates the penalty scaling factor based on current borrow utilization and saturation

This implements the penalty rate function Formula: ((1 - u_0) * f_interestPerSecond(u_1) * allAssetsDepositL) / (WAD * satInLAssetsInPenalty) Where u_1 = (0.90 - (1 - u_0) * (0.95 - u_s) / 0.95)

function calcSaturationPenaltyRatePerSecondInWads(
uint256 currentBorrowUtilizationInWad,
uint256 saturationUtilizationInWad,
uint128 satInLAssetsInPenalty,
uint256 allAssetsDepositL
) internal pure returns (uint256 penaltyRatePerSecondInWads);

Parameters

NameTypeDescription
currentBorrowUtilizationInWaduint256Current borrow utilization of L (u_0)
saturationUtilizationInWaduint256Current saturation utilization (u_s)
satInLAssetsInPenaltyuint128The saturation in L assets in the penalty
allAssetsDepositLuint256The total assets deposited in L

Returns

NameTypeDescription
penaltyRatePerSecondInWadsuint256The penalty rate per second in WADs

Errors

MaxTrancheOverSaturated

error MaxTrancheOverSaturated();

CannotUpdateZeroAddress

error CannotUpdateZeroAddress();

Structs

SaturationStruct

struct SaturationStruct {
Tree netXTree;
Tree netYTree;
uint16 maxLeaf;
}

SaturationPair

a pair of saturation values used and stored throughout this library.

struct SaturationPair {
uint128 satInLAssets;
uint128 satRelativeToL;
}

Tree

struct Tree {
bool netX;
uint16 highestSetLeaf;
uint128 totalSatInLAssets;
uint256 tranchesWithSaturation;
uint256[][LEVELS_WITHOUT_LEAFS] nodes;
Leaf[LEAFS] leafs;
mapping(int16 => uint16) trancheToLeaf;
mapping(int16 => SaturationPair) trancheToSaturation;
mapping(address => Account) accountData;
}

Leaf

struct Leaf {
Uint16Set.Set tranches;
SaturationPair leafSatPair;
uint256 penaltyInBorrowLSharesPerSatInQ72;
}

Account

struct Account {
bool exists;
int16 lastTranche;
uint112 penaltyInBorrowLShares;
SaturationPair[] satPairPerTranche;
uint256[] treePenaltyAtOnsetInBorrowLSharesPerSatInQ72PerTranche;
}

CalcLiqSqrtPriceHandleAllABCNonZeroStruct

struct CalcLiqSqrtPriceHandleAllABCNonZeroStruct {
int256 netLInMAG2;
int256 netXInMAG2;
int256 netYInMAG2;
uint256 netYAbsInMAG2;
uint256 borrowedXAssets;
uint256 borrowedYAssets;
}

AddSatToTrancheStateUpdatesStruct

struct AddSatToTrancheStateUpdatesStruct {
int256 tranche;
uint256 newLeaf;
SaturationPair oldTrancheSaturation;
SaturationPair newTrancheSaturation;
SaturationPair satAvailableToAdd;
address account;
}