Saturation
Author: imi@1m1.io
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 if LAssets. Keeps the sat 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 combine 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. The liq price belongs to a tranche. 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 over saturated. Each account, tranche and leaf has a total penalty that needs to be repayed 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 = 0x6e;
MAX_HEALTHY_SATURATION_RATIO_IN_MAG2
uint256 internal constant MAX_HEALTHY_SATURATION_RATIO_IN_MAG2 = 0x50;
START_SATURATION_PENALTY_RATIO_IN_MAG2
uint256 internal constant START_SATURATION_PENALTY_RATIO_IN_MAG2 = 0x46;
PENALTY_FACTOR_IN_MAG2
uint256 private constant PENALTY_FACTOR_IN_MAG2 = 0xa;
MAX_SATURATION_DISTRIBUTION_TRANCHES
uint256 internal constant MAX_SATURATION_DISTRIBUTION_TRANCHES = 0x3;
AMMALGAMPAIR_MINIMUM_LIQUIDITY
uint256 internal constant AMMALGAMPAIR_MINIMUM_LIQUIDITY = 0x3e8;
SAT_TO_LEAF_MIN_IN_Q128
uint256 private constant SAT_TO_LEAF_MIN_IN_Q128 = 0x3e272007bf43f051cf6f4632ccfc54317b;
SAT_TO_LEAF_MAX_IN_Q128
uint256 private constant SAT_TO_LEAF_MAX_IN_Q128 = 0xfe93f7ee948afecf9909a051669b4878e0846dd2ce13c096be6a7890625;
SAT_TO_LEAF_BASE_CHANGE_IN_Q128
uint256 private constant SAT_TO_LEAF_BASE_CHANGE_IN_Q128 = 0x1cfc0000000000000000000000000000;
SAT_TO_LEAF_SHIFT
uint256 private constant SAT_TO_LEAF_SHIFT = 0xef;
INT_ONE
int256 private constant INT_ONE = 1;
LEVELS_WITHOUT_LEAFS
uint256 internal constant LEVELS_WITHOUT_LEAFS = 0x3;
LOWEST_LEVEL_INDEX
uint256 internal constant LOWEST_LEVEL_INDEX = 0x2;
LEAFS_IN_BITS
uint256 private constant LEAFS_IN_BITS = 0xc;
LEAFS
uint256 internal constant LEAFS = 0x1000;
CHILDREN_PER_NODE_IN_BITS
uint256 private constant CHILDREN_PER_NODE_IN_BITS = 0x4;
CHILDREN_PER_NODE
uint256 internal constant CHILDREN_PER_NODE = 0x10;
TICKS_PER_TRANCHE
int256 private constant TICKS_PER_TRANCHE = 0x64;
TWO_DIV_ONE_MINUS_ONE_OVER_BASE_TRANCHE_OVER_IN_Q128
uint256 constant TWO_DIV_ONE_MINUS_ONE_OVER_BASE_TRANCHE_OVER_IN_Q128 = 0xb4337207340fdfaf6710a40d6e575cd9c;
MIN_TRANCHE
int256 internal constant MIN_TRANCHE = -0xc7;
MAX_TRANCHE
int256 internal constant MAX_TRANCHE = 0xc6;
TOTAL_BITS
uint256 private constant TOTAL_BITS = 0x100;
FIELD_BITS
uint256 private constant FIELD_BITS = 0x10;
SAT_BITS
uint256 private constant SAT_BITS = 0x70;
TRANCHE_COUNT_BITS
uint256 private constant TRANCHE_COUNT_BITS = 0x10;
EMPTY_BITS
uint256 private constant EMPTY_BITS = 0x70;
FIELD_NODE_MASK
uint256 private constant FIELD_NODE_MASK = 0xffff;
SAT_NODE_MASK
uint256 private constant SAT_NODE_MASK = 0xffffffffffffffffffffffffffff0000;
TRANCHE_COUNT_NODE_MASK
uint256 private constant TRANCHE_COUNT_NODE_MASK = 0xffff00000000000000000000000000000000;
Functions
initTree
initializes the satStruct, allocating storage for all nodes
initCheck can be removed once the tree structure is fixed
function initTree(
SaturationStruct storage satStruct
) external;
Parameters
Name | Type | Description |
---|---|---|
satStruct | SaturationStruct | contains the entire sat data |
update
update the borrow position of an account and potentially check (and revert) if the resulting sat is too high
function update(
SaturationStruct storage satStruct,
Validation.InputParams memory inputParams,
address account
) external;
Parameters
Name | Type | Description |
---|---|---|
satStruct | SaturationStruct | main data struct |
inputParams | Validation.InputParams | contains the position and pair params, like account borrows/deposits, current price and active liquidity |
account | address | for which is position is being updated |
accruePenalties
accrue penalties since last accrual based on all over saturated positions
function accruePenalties(
SaturationStruct storage satStruct,
uint256 activeLiquidityInLAssets,
uint256 duration,
uint256 maxUtilizationInWad
) external returns (uint256 penaltyInLAssets);
Parameters
Name | Type | Description |
---|---|---|
satStruct | SaturationStruct | main data struct |
activeLiquidityInLAssets | uint256 | of the pair |
duration | uint256 | since last accrual of penalties |
maxUtilizationInWad | uint256 | is the max of the L, X, Y borrow vs deposit utilizations |
Returns
Name | Type | Description |
---|---|---|
penaltyInLAssets | uint256 | the amount to be added to the liquidity assets to benefit the liquidity providers from the penalty available |
calcPenaltyForRepay
calc and decrease the penalty that an account owes to repay for its borrow position
function is called with 0<repayAmountLInLAssets && repayAmountXInXAssets==repayAmountYInYAssets==0
xor 0==repayAmountLInLAssets && (0<repayAmountXInXAssets || 0<repayAmountYInYAssets)
function calcPenaltyForRepay(
SaturationStruct storage satStruct,
address account,
uint256 currentSqrtPriceInXInQ128,
uint256 repayAmountLInLAssets,
uint256 repayAmountXInXAssets,
uint256 repayAmountYInYAssets
)
external
returns (
uint256 overSaturationPenaltyRemovedLInLAssets,
uint256 overSaturationPenaltyRemovedXInXAssets,
uint256 overSaturationPenaltyRemovedYInYAssets
);
Parameters
Name | Type | Description |
---|---|---|
satStruct | SaturationStruct | main data struct |
account | address | whos position is being considered |
currentSqrtPriceInXInQ128 | uint256 | of the pair |
repayAmountLInLAssets | uint256 | being repayed on behalf of the account |
repayAmountXInXAssets | uint256 | being repayed on behalf of the account |
repayAmountYInYAssets | uint256 | being repayed on behalf of the account |
Returns
Name | Type | Description |
---|---|---|
overSaturationPenaltyRemovedLInLAssets | uint256 | over sat penalty decreased for the account, upto repaid amount |
overSaturationPenaltyRemovedXInXAssets | uint256 | over sat penalty decreased for the account, upto repaid amount |
overSaturationPenaltyRemovedYInYAssets | uint256 | over sat penalty decreased for the account, upto repaid amount |
updatePenaltyForRepayXYGivenTree
function updatePenaltyForRepayXYGivenTree(
Tree storage tree,
address account,
uint256 currentSqrtPriceInXInQ128,
uint256 repayAmountInAssets
) internal returns (uint256 overSaturationPenaltyRemovedInAssets);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | to be considered |
account | address | whos position is being considered |
currentSqrtPriceInXInQ128 | uint256 | of the pair |
repayAmountInAssets | uint256 | being repayed on behalf of the account |
Returns
Name | Type | Description |
---|---|---|
overSaturationPenaltyRemovedInAssets | uint256 | over sat penalty decreased for the account, upto repaid amount |
initTree
init the nodes of the tree
function initTree(
Tree storage tree
) internal;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
check
check and revert in case the highest sat is too high
function check(uint256 activeLiquidityInLAssets, uint256 highestSetLeaf) internal pure;
Parameters
Name | Type | Description |
---|---|---|
activeLiquidityInLAssets | uint256 | of the pair |
highestSetLeaf | uint256 | highest leaf with a tranche |
updateTreeGivenAccountAndInputParams
internal update function given a tree, an account, its params and liq price
function updateTreeGivenAccountAndInputParams(
Tree storage tree,
Validation.InputParams memory inputParams,
address account,
uint256 liqSqrtPriceInXInQ128
) internal;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
inputParams | Validation.InputParams | contains the position and pair params, like account borrows/deposits, current price and active liquidity |
account | address | whos position is being considered |
liqSqrtPriceInXInQ128 | uint256 | price at which the positions' LTV would reach LTVMAX |
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,
address account,
int256 newTranche,
uint256 newSatInLAssets,
uint256 activeLiquidityInLAssets
) internal;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
account | address | whos position is being considered |
newTranche | int256 | the new tranche of the account |
newSatInLAssets | uint256 | the new sat of the account, in units of LAssets |
activeLiquidityInLAssets | uint256 | of the pair |
removeSatFromTranche
remove sat from tree, for each tranche in a loop that could hold sat for the account
function removeSatFromTranche(
Tree storage tree,
uint112[MAX_SATURATION_DISTRIBUTION_TRANCHES] memory accountToSatPerTranche,
uint256[MAX_SATURATION_DISTRIBUTION_TRANCHES] memory accountTreePenaltyAtOnsetInLAssetsPerSatInQ128PerTranche,
address account,
int256 trancheDirection
) internal returns (bool highestSetLeafRemoved);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
accountToSatPerTranche | uint112[MAX_SATURATION_DISTRIBUTION_TRANCHES] | array holding the sat per tranche given the account |
accountTreePenaltyAtOnsetInLAssetsPerSatInQ128PerTranche | uint256[MAX_SATURATION_DISTRIBUTION_TRANCHES] | |
account | address | whos position is being considered |
trancheDirection | int256 | direction of sat distribution depending on netX/netY |
Returns
Name | Type | Description |
---|---|---|
highestSetLeafRemoved | bool | flag 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,
int256 tranche,
uint256 oldLeaf,
uint256 oldTrancheSatInLAssets,
uint256 oldAccountSatInTrancheInLAssets,
address account,
uint256 oldAccountTreePenaltyAtOnsetInLAssetsPerSatInQ128
) internal returns (uint256 newLeafPenaltyAtOnsetInLAssetsPerSatInQ128);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
tranche | int256 | under consideration |
oldLeaf | uint256 | |
oldTrancheSatInLAssets | uint256 | tranche sat |
oldAccountSatInTrancheInLAssets | uint256 | account sat |
account | address | whos position is being considered |
oldAccountTreePenaltyAtOnsetInLAssetsPerSatInQ128 | uint256 | penalty offset at the old leaf |
Returns
Name | Type | Description |
---|---|---|
newLeafPenaltyAtOnsetInLAssetsPerSatInQ128 | uint256 | the new offset penalty to be stored |
addSatToTranche
add sat to tree, for each tranche in a loop as needed. we add to each tranche as much as it can bear.
function addSatToTranche(
Tree storage tree,
uint112[MAX_SATURATION_DISTRIBUTION_TRANCHES] memory accountToSatPerTranche,
uint256[MAX_SATURATION_DISTRIBUTION_TRANCHES] memory accountTreePenaltyAtOnsetInLAssetsPerSatInQ128PerTranche,
address account,
int256 trancheDirection,
int256 newTranche,
uint256 newSatInLAssets,
uint256 activeLiquidityInLAssets
) internal returns (bool highestSetLeafAdded);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
accountToSatPerTranche | uint112[MAX_SATURATION_DISTRIBUTION_TRANCHES] | array holding the sat per tranche given the account, set at the end of this function |
accountTreePenaltyAtOnsetInLAssetsPerSatInQ128PerTranche | uint256[MAX_SATURATION_DISTRIBUTION_TRANCHES] | |
account | address | whos position is being considered |
trancheDirection | int256 | direction of sat distribution depending on netX/netY |
newTranche | int256 | the new tranche of the account |
newSatInLAssets | uint256 | the new sat of the account, in units of LAssets |
activeLiquidityInLAssets | uint256 | of the pair |
Returns
Name | Type | Description |
---|---|---|
highestSetLeafAdded | bool | flag 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,
uint256 newSatInLAssets,
uint256 activeLiquidityInLAssets,
address account
) internal view returns (AddSatToTrancheStateUpdatesStruct memory addSatToTrancheStateUpdatesParams);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
tranche | int256 | under consideration |
newSatInLAssets | uint256 | in units of LAssets |
activeLiquidityInLAssets | uint256 | of the pair |
account | address | whos position is being considered |
Returns
Name | Type | Description |
---|---|---|
addSatToTrancheStateUpdatesParams | AddSatToTrancheStateUpdatesStruct | the 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;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
params | AddSatToTrancheStateUpdatesStruct | convenience struct holding params needed for these updates |
removeTrancheToLeaf
removing a tranche from a leaf, update the fields and sats up the tree
function removeTrancheToLeaf(Tree storage tree, int256 tranche, uint256 trancheSatInLAssets, uint256 leaf) internal;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
tranche | int256 | that is being moved |
trancheSatInLAssets | uint256 | sat of tranche being moved |
leaf | uint256 | the leaf |
addTrancheToLeaf
adding a tranche from a leaf, update the fields and sats up the tree
function addTrancheToLeaf(Tree storage tree, int256 tranche, uint256 trancheSatInLAssets, uint256 leaf) internal;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
tranche | int256 | that is being moved |
trancheSatInLAssets | uint256 | sat of tranche being moved |
leaf | uint256 | the leaf |
addSatUpTheTree
recursively add sat up the tree
function addSatUpTheTree(Tree storage tree, uint256 level, uint256 nodeIndex, int256 satInLAssets) internal;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
level | uint256 | level being updated |
nodeIndex | uint256 | index is the position (0 based) of the node in its level |
satInLAssets | int256 | sat to add to the current node, usually uint112, int to allow subtracting sat up the tree |
accruePenaltiesGivenTree
internal function to calc and add penalty for all over saturation positions
function accruePenaltiesGivenTree(
Tree storage tree,
uint256 thresholdLeaf,
uint256 duration,
uint256 maxUtilizationInWad
) internal returns (uint256 penaltyInLAssets);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
thresholdLeaf | uint256 | where penalties begin |
duration | uint256 | in seconds since last update |
maxUtilizationInWad | uint256 | is the max of the L, X, Y borrow vs deposit utilizations |
Returns
Name | Type | Description |
---|---|---|
penaltyInLAssets | uint256 | the amount to be added to the liquidity assets to benefit the liquidity providers from the penalty available |
updatePenalties
update penalties in the tree given
function updatePenalties(Tree storage tree, uint256 thresholdLeaf, uint256 addPenaltyInLAssetsPerSatInQ128) internal;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
thresholdLeaf | uint256 | from which leaf on the penalty needs to be added inclusive |
addPenaltyInLAssetsPerSatInQ128 | uint256 | the penalty to be added |
updatePenaltiesDownTheTree
update penalties in the tree given recursively downwards, always with perfect cover if a node exactly covers our threshold, we only update that node and all right siblings. the node that is over covering our threshold is used as the parent for the recursion to get the coverage exactly
function updatePenaltiesDownTheTree(
Tree storage tree,
uint256 thresholdLeaf,
uint256 addPenaltyInLAssetsPerSatInQ128,
uint256 level,
uint256 nodeIndex,
uint256 totalNodesAtLevel
) private;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
thresholdLeaf | uint256 | from which leaf on the penalty needs to be added inclusive |
addPenaltyInLAssetsPerSatInQ128 | uint256 | the penalty to be added |
level | uint256 | |
nodeIndex | uint256 | |
totalNodesAtLevel | uint256 |
calcPenaltyPerSatFromLeafToRoot
recursive function to sum penalties from leaf to root
function calcPenaltyPerSatFromLeafToRoot(
Tree storage tree,
uint256 level,
uint256 nodeIndex,
uint256 leaf
) internal view returns (uint256 penaltyInLAssetsPerSatInQ128);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
level | uint256 | level being read |
nodeIndex | uint256 | index is the position (0 based) of the node in its level |
leaf | uint256 | index (0 based) of the leaf |
Returns
Name | Type | Description |
---|---|---|
penaltyInLAssetsPerSatInQ128 | uint256 | total 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;
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
account | address | whos position is being considered |
calcTotalSatAfterLeafInclusive
calc total sat of all accounts/tranches/leafs higher (and same) as the threshold
function calcTotalSatAfterLeafInclusive(
Tree storage tree,
uint256 thresholdLeaf
) internal view returns (uint256 satInLAssets, uint256 trancheCount);
Parameters
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
thresholdLeaf | uint256 | leaf to start adding sat from |
Returns
Name | Type | Description |
---|---|---|
satInLAssets | uint256 | total sat of all accounts with tranche in a leaf from at least thresholdLeaf |
trancheCount | uint256 |
satToLeaf
convert sat to leaf
function satToLeaf(
uint256 satLAssets
) internal pure returns (uint256 leaf);
Parameters
Name | Type | Description |
---|---|---|
satLAssets | uint256 | sat to convert |
Returns
Name | Type | Description |
---|---|---|
leaf | uint256 | resulting leaf from 0 to 2**12-1 |
calcMaxSatInTranche
calc max sat in a tranche such that a liq swap does not cross an entire tranches worth
function calcMaxSatInTranche(
uint256 activeLiquidityInLAssets
) internal pure returns (uint256 maxSatInTranche);
Parameters
Name | Type | Description |
---|---|---|
activeLiquidityInLAssets | uint256 | in the pair |
Returns
Name | Type | Description |
---|---|---|
maxSatInTranche | uint256 | in LAssets |
calcSatAvailableToAddToTranche
calc how much sat can be added to a tranche such that it is healthy
function calcSatAvailableToAddToTranche(
uint256 activeLiquidityInLAssets,
uint256 targetSatToAddInLAssets,
uint256 currentTrancheSatInLAssets
) internal pure returns (uint256 satAvailableToAddInLAssets);
Parameters
Name | Type | Description |
---|---|---|
activeLiquidityInLAssets | uint256 | of the pair |
targetSatToAddInLAssets | uint256 | the sat that we want to add |
currentTrancheSatInLAssets | uint256 | the sat that the tranche already hols |
Returns
Name | Type | Description |
---|---|---|
satAvailableToAddInLAssets | uint256 | considering the currentTrancheSatInLAssets and the max a tranche can have |
calcSat
calc sat given position using LTV calc from Validation
function calcSat(
Validation.InputParams memory inputParams,
uint256 liqSqrtPriceInXInQ128
) internal pure returns (uint256 satInLAssets);
Parameters
Name | Type | Description |
---|---|---|
inputParams | Validation.InputParams | position |
liqSqrtPriceInXInQ128 | uint256 | liq price |
Returns
Name | Type | Description |
---|---|---|
satInLAssets | uint256 | sat |
convertLToXXorY
function convertLToXXorY(
Tree storage tree,
uint256 currentSqrtPriceInXInQ128,
uint256 amountInLAssets,
Math.Rounding rounding
) private view returns (uint256);
convertXorYToL
function convertXorYToL(
Tree storage tree,
uint256 currentSqrtPriceInXInQ128,
uint256 amountInAssets,
Math.Rounding rounding
) private view returns (uint256);
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
Name | Type | Description |
---|---|---|
tree | Tree | that is being read from or written to |
level | uint256 | level being updated |
nodeIndex | uint256 | index is the position (0 based) of the node in its level |
lowerNodePos | uint256 | pos is the relative position (0 bsaed) of the node in its parent |
set | uint256 | 1 for set, 0 for unset |