Ticks
Ticks are a fundamental concept in f(x) protocol that represent discrete price points for debt-to-collateral ratios. They serve as a risk categorization system that groups user positions with similar risk levels together. For more details, you can check _getTick
function in contracts.
// From TickMath.sol:
int24 internal constant MIN_TICK = -32767;
int24 internal constant MAX_TICK = 32767;
// Tick calculation uses 1.0015 as the base
// ratioX96 = (1.0015^tick) * 2^96
1. Mathematical Foundation
The core formula for the tick system is:
Ratio = (1.0015^tick) Γ 2^96
This ratio represents the debts/collaterals proportion
Each tick represents approximately 0.15% price change (1.0015 - 1 = 0.0015)
2. Primary functions of Ticks
All user positions with similar debt ratios are grouped into the same tick, enabling:
Efficient batch processing: Multiple similar-risk positions can be handled simultaneously
Precise risk management: Positions within the same tick share similar liquidation risks
3. topTick
The system processes ticks starting from the highest risk tick (topTick) in descending risk order.
The system maintains a topTick that points to the highest-risk tick with outstanding debt. Operations always start from this point and work downward through decreasing risk levels.
4 Trace ticks
There are many ways to trace ticks. Fundamentally, they are stored in the nodes of each pool contract. For more details, please view [PoolStorage.sol][https://github.com/AladdinDAO/fx-protocol-contracts/blob/main/contracts/core/pool/PoolStorage.sol)
As for a position, you can use getPosition
in each pool to get the tick of the position.
/// @notice Return the details of the given position.
/// @param tokenId The id of position to query.
/// @return rawColls The amount of collateral tokens supplied in this position.
/// @return rawDebts The amount of debt tokens borrowed in this position.
function getPosition(uint256 tokenId) external view returns (uint256 rawColls, uint256 rawDebts);
As for a tick, you can use tickData
to fetch which node it belongs to:
/// @dev Mapping from tick to tree node id.
mapping(int256 => uint48) public tickData;
As for a node, you can fetch the node's info through tickTreeData
and tickData
.
/// @dev Mapping from tree node id to tree node data.
mapping(uint256 => TickTreeNode) public tickTreeData;
tickData: tick -> node
tickTreeData: node -> tick
However, the tick number returned by getPosition
cannot be directly used in rebalance or liquidation operations unless its node's parent is 0. If the node's parent is not 0, you must trace the parent tick number upward until you reach a node whose parent is 0 (i.e., the root node).
/// @dev Internal function to get the root of the given tree node.
/// @param node The id of the given tree node.
/// @return root The root node id.
/// @return collRatio The actual collateral ratio of the given node, multiplied by 2^60.
/// @return debtRatio The actual debt ratio of the given node, multiplied by 2^60.
function _getRootNode(uint256 node) internal view returns (uint256 root, uint256 collRatio, uint256 debtRatio) {
collRatio = E60;
debtRatio = E60;
while (true) {
bytes32 metadata = tickTreeData[node].metadata;
uint256 parent = metadata.decodeUint(PARENT_OFFSET, 48);
collRatio = (collRatio * metadata.decodeUint(COLL_RATIO_OFFSET, 64)) >> 60;
debtRatio = (debtRatio * metadata.decodeUint(DEBT_RATIO_OFFSET, 64)) >> 60;
if (parent == 0) break;
node = parent;
}
root = node;
}
Additionally, you can also use TickBitmap
contract to track active root ticks.
Last updated