The code of
BasePool is the core component of the f(x) protocol, implementing mechanisms for opening and adjusting xPOSITIONs, as well as handling redemptions and liquidations.
BasePool is then inherited by specialized pools, such as
AaveFundingPool. This design allows the pool to work with any asset that has a reliable price oracle for calculating collateral-debt ratios. Pools
must override and implement the
_updateCollAndDebtIndex() and
_deductProtocolFees() functions to manage collateral, debt, and fees independently.
BasePool itself is responsible for minting and burning fxUSD, operating with collateral-debt positions using a "concentrated liquidity" pattern (with price ticks) and rebalancing the fxUSD/collateral reserves.
The main functions of
BasePool, detailed below, are accessed through
PoolManager, where all token transfers, minting/burning operations (in
FxUSDRegeneracy), and updates to global "per-pool" parameters occur in pre- and post-operation hooks. Meanwhile,
BasePool operates on its internal positions, ticks, amounts, and shares.
One of the key functions is
operate(), which creates or adjusts xPOSITIONs. By providing
newRawColl and
newRawDebt parameters, this function can add or remove collateral and debt assets. The first step involves
receiving the
oracle price of a given collateral asset. Each xPOSITION is associated with a specific price tick, and changes in collateral, debt, or oracle price may move the position to a different tick. So, at the start, a position is first
removed from the current tick.
Each xPOSITION is "measured" in terms of collateral and debt shares rather than raw amounts. That's why all operations with raw amounts of collateral and debt require a conversion from shares to amounts (like
here). This "shares-based" approach simplifies operations on groups of positions, handles rebaseable assets like stETH, and allows fees or operation surpluses to be distributed proportionally across the total supply of pool tokens, effectively increasing the holdings of token owners.
After
verifying the liquidation threshold (which prevents borrowing or withdrawals beyond limits), the protocol executes collateral
operations, applies fees, updates collateral shares, and then performs debt
operations, which adjust debt shares.
Finally, the protocol
checks whether the debt ratio of the position falls within valid undercollateralization levels. The updated position may have a new collateral-debt ratio, associating it with a different price tick, where it is
added. The position's details are
updated, and the pool's global collateral and debt amounts are refreshed.
To maintain the health of the pool, the f(x) protocol employs two mechanisms to lower the debt ratio of positions and ticks. The "lighter" mechanism is
rebalance(), used when a
tick (which groups multiple positions) or an individual
position has a debt ratio greater than
rebalanceDebtRatio but less than
liquidateDebtRatio. This mechanism prevents critical states by recalibrating debt ratios early. In cases where collateral price changes are gradual, rebalancing can entirely avoid liquidations by gradually adjusting positions and ensuring the protocol remains overcollateralized.
A single position encompasses a share of the "total tick debt" and "total tick collateral," meaning the logic for single positions and ticks is quite similar. The "tick"-level version operates on total tick amounts and shares, while the "position"-level version operates on individual position amounts and shares. In both cases, shares are converted to raw amounts (
here or
here), and, after verifying debt ratios, the necessary amount of debt to be converted into collateral is calculated using
_getRawDebtToRebalance(). This function addresses the question: "How much debt should be removed to achieve the target debt ratio?" Once the target debt and collateral amounts have been converted back to shares, the function behavior diverges slightly:
- The "position-level" version, after being removed from its original tick, is added to a tick aligned with the new debt ratio.
- The "tick-level" version uses _liquidateTick() to adjust and redistribute rebalanced share amounts across ticks (moved to the next tick).
This "shares based" logic enables rebalancing of multiple positions in a single tick simultaneously, without directly modifying individual positions. The debt and collateral shares of all user positions within a tick remain unchanged, but the "totalSupply" of collateral and debt shares in the new tick differs, adjusting each user’s debt-collateral ratio accordingly.
The next step involves the
liquidate() function. This function can only be applied to specific positions and only when the debt ratio exceeds
liquidateDebtRatio. Liquidation is triggered when collateral price changes are significant and
rebalance() fails to address the issue in time. Similar to rebalancing, the position is
removed from its tick and the necessary
debt and
collateral shares are recalculated (including a liquidation
bonus).
A critical aspect of liquidation is
redistributing potential bad debt. In the f(x) protocol, bad debt is "socialized," meaning it is
subtracted from the total pool debt. This operation
increases the pool's total debt index, which is an unavoidable aspect of this process.
The functions
rebalance() and
liquidate() utilize fxUSD and USDC from the Stability Pool. This mechanism ensures that liquidations and rebalancing do not affect fxUSD's price on the external market, which is critical to maintaining stability. Excess fxUSD is
burned, and any uncovered rest is
covered with USDC from users.
The function
redeem() is another key part of the stabilization mechanism. It operates at the pool level, redeeming debt by swapping it into collateral when fxUSD is undervalued external to the protocol (always being profitable for redeemers). Redemptions may be
paused for governance-related concerns about the fxUSD peg and are
disallowed when the pool's debt ratio exceeds 1.
To summarize,
BasePool manages these key functions:
• operate(): creating and adjusting xPOSITIONs.
• redeem(): swapping debt into collateral when debt is undervalued.
• rebalance(): calibrating debt ratios when they exceed safe limits.
• liquidate(): addressing critical debt ratio states.
Stabilization functions aim to utilize the Stability Pool whenever possible, ensuring fxUSD price stability in the external market.