# Liquidation process walk through

For a position to be considered safe, the below should hold true.

$$
lockedCollateralAmount \* PriceWithSafetyMargin > DebtShare \* debtAccumulateRate,
$$

where

$$
PriceWithSafetyMargin = RawPrice \* LTV.
$$

*RawPrice* is the price of collateral from `PriceFeed` and *LTV* is Loan to Ratio.

If a position's value of `lockedCollateral(lockedCollateralAmount * PriceWithSafetyMargin)` goes below the `debtValue(DebtShare * debtAccumulatedRate)`, the position is considered unsafe/underwater and will be subject to liquidation process. The liquidator bot monitors the health of each position in the Fathom protocol.

Once a position is underwater and spotted by the liquidator bot, the bot can call the `liquidate` function in the `LiquidationEngine` contract.

<pre class="language-solidity" data-overflow="wrap"><code class="lang-solidity">function liquidate(
    bytes32 _collateralPoolId, // the collateralPoolId of the position that is to be liquidated
    address _positionAddress, // the positionHandler's address of a position that is to be liquidated
    uint256 _debtShareToBeLiquidated, // a param to set how much of debtShare will be liquidated from a position
    uint256 _maxDebtShareToBeLiquidated, // the ceiling for the debtShare that can be liquidated
    address _collateralRecipient, // the recipient of the collateral once the liquidation process ends
<strong>    bytes calldata _data // is used when the liquidation process includes FlashLendingCallee
</strong>) external;
</code></pre>

Or the bot can call the `batchLiquidate` function if it spots multiple underwater positions.

{% code overflow="wrap" %}

```solidity
function batchLiquidate(
    bytes32[] calldata _collateralPoolIds,
    address[] calldata _positionAddresses,
    uint256[] calldata _debtShareToBeLiquidateds,
    uint256[] calldata _maxDebtShareToBeLiquidateds,
    address[] calldata _collateralRecipients,
    bytes[] calldata _datas
) external;
```

{% endcode %}

If a bot calls `liquidate` or `batchLiquidate` functions to `LiquidationEngine` without using the `_data` param, the liquidator bot needs to have enough balance of FXD token available and should have approved `FixedSpreadLiquidationStrategy` as a spender. This is because the process of depositing FXD to liquidate and receive collateral is done in the execute function in `FixedSpreadLiquidationStrategy`.

If a bot calls `liquidate` or `batchLiquidate` functions and has the `_data` param filled with encoded data that can be used for [flashLendingCalle's](https://github.com/Into-the-Fathom/fathom-stablecoin-smart-contracts/blob/master/contracts/main/stablecoin-core/liquidation-strategies/FixedSpreadLiquidationStrategy.sol#L214) address, the liquidator bot doesn't necessarily need to have an FXD token available.

Once liquidation functions are called, the liquidation process starts from the internal function [\_liquidate](https://github.com/Into-the-Fathom/fathom-stablecoin-smart-contracts/blob/master/contracts/main/stablecoin-core/LiquidationEngine.sol#L239).

{% code overflow="wrap" %}

```solidity
function _liquidate(
    bytes32 _collateralPoolId,
    address _positionAddress,
    uint256 _debtShareToBeLiquidated,
    uint256 _maxDebtShareToBeLiquidated,
    address _collateralRecipient,
    bytes calldata _data,
    address sender
) internal; 
```

{% endcode %}

`_liquidate` function first goes through some checks in the function args and checks whether the position targeted for liquidation is safe.

{% code overflow="wrap" %}

```solidity
require(
    _collateralPoolLocalVars.priceWithSafetyMargin > 0 &&
    _vars.positionLockedCollateral * _collateralPoolLocalVars.priceWithSafetyMargin <
    _vars.positionDebtShare * _collateralPoolLocalVars.debtAccumulatedRate,
    "LiquidationEngine/position-is-safe"
);
```

{% endcode %}

Once the position is underwater, the liquidation task is tossed to `FixedSpeadLiquidationStrategy` execute function.

<pre class="language-solidity" data-overflow="wrap"><code class="lang-solidity">_strategy.execute(
    _collateralPoolId,
    _vars.positionDebtShare,
<strong>    _vars.positionLockedCollateral,
</strong>    _positionAddress,
<strong>    _debtShareToBeLiquidated,
</strong>    _maxDebtShareToBeLiquidated,
    sender,
    _collateralRecipient,
    _data
);
</code></pre>

The [`execute`](https://github.com/Into-the-Fathom/fathom-stablecoin-smart-contracts/blob/master/contracts/main/stablecoin-core/liquidation-strategies/FixedSpreadLiquidationStrategy.sol#L148) function in  `FixedSpeadLiquidationStrategy` starts by verifying if the caller has the `LIQUIDATION_ENGINE_ROLE` from the `AccessControlConfig` associated with the `BookKeeper`. This ensures that only authorized entities can initiate the liquidation process.

{% code overflow="wrap" %}

```solidity
require(
    IAccessControlConfig(bookKeeper.accessControlConfig()).hasRole(
        IAccessControlConfig(bookKeeper.accessControlConfig()).LIQUIDATION_ENGINE_ROLE(),
        msg.sender
    ),
    "!liquidationEngingRole"
);
```

{% endcode %}

The function then validates the input parameters, such as the debt share, collateral amount, and position address. It checks for zero values and valid price feeds to ensure the integrity of the liquidation data.

{% code overflow="wrap" %}

```solidity
_validateValues(_collateralPoolId, _positionDebtShare, _positionCollateralAmount, _positionAddress);
```

{% endcode %}

Then, it calculates the liquidation information based on the current market price, debt share, and collateral amount. This step involves determining the actual debt value to be liquidated, the collateral amount that will be seized, and any applicable treasury fees.

<pre class="language-solidity" data-overflow="wrap"><code class="lang-solidity">LiquidationInfo memory info = _calculateLiquidationInfo(
<strong>    _collateralPoolId,
</strong>    _debtShareToBeLiquidated,
    getFeedPrice(_collateralPoolId),
    _positionCollateralAmount,
    _positionDebtShare
);
</code></pre>

The function then calls `confiscatePosition` on the `BookKeeper`, adjusting the collateral and debt share of the position in question.

```solidity
bookKeeper.confiscatePosition(
    _collateralPoolId,
    _positionAddress,
    address(this),
    address(systemDebtEngine),
    -int256(info.collateralAmountToBeLiquidated),
    -int256(info.actualDebtShareToBeLiquidated)
);
```

Treasury fees are moved from the liquidation contract to the `SystemDebtEngine` if any treasury fees are due.

{% code overflow="wrap" %}

```solidity
if (info.treasuryFees > 0) {
    bookKeeper.moveCollateral(_collateralPoolId, address(this), address(systemDebtEngine), info.treasuryFees);
}
```

{% endcode %}

If flash lending is enabled and valid, the contract facilitates flash lending calls. This step is optional and based on the encoded data provided.

{% code overflow="wrap" %}

```solidity
if (info.treasuryFees > 0) {
    bookKeeper.moveCollateral(_collateralPoolId, address(this), address(systemDebtEngine), info.treasuryFees);
}
```

{% endcode %}

Without flash lending, the contract withdraws the collateral to the liquidator and transfers the required FXD amount to cover the debt. The FXD is then deposited in the `BookKeeper` to clear the debt.

{% code overflow="wrap" %}

```solidity
IGenericTokenAdapter(ICollateralPoolConfig(bookKeeper.collateralPoolConfig()).getAdapter(_collateralPoolId)).withdraw(
    _collateralRecipient,
    info.collateralAmountToBeLiquidated - info.treasuryFees,
    abi.encode(0)
);
address _stablecoin = address(stablecoinAdapter.stablecoin());
_stablecoin.safeTransferFrom(_liquidatorAddress, address(this), ((info.actualDebtValueToBeLiquidated / RAY) + 1));
_stablecoin.safeApprove(address(stablecoinAdapter), ((info.actualDebtValueToBeLiquidated / RAY) + 1));
stablecoinAdapter.depositRAD(_liquidatorAddress, info.actualDebtValueToBeLiquidated, _collateralPoolId, abi.encode(0));
```

{% endcode %}

Finally, an event `LogFixedSpreadLiquidate` is emitted to record the details of the liquidation transaction.

{% code overflow="wrap" %}

```solidity
emit LogFixedSpreadLiquidate(
    _collateralPoolId,
    info.positionDebtShare,
    info.positionCollateralAmount,
    _positionAddress,
    info.debtShareToBeLiquidated,
    info.maxDebtShareToBeLiquidated,
    _liquidatorAddress,
    _collateralRecipient,
    info.actualDebtShareToBeLiquidated,
    info.actualDebtValueToBeLiquidated,
    info.collateralAmountToBeLiquidated,
    info.treasuryFees
);
```

{% endcode %}

\
After the `execute` function of the `FixedSpreadLiquidationStrategy` completes, the remaining liquidation process in the `LiquidationEngine` is verification of successful liquidation and recording of `systemBadDebt` if needed.&#x20;

First, updated position data is retrieved:

{% code overflow="wrap" %}

```solidity
(_vars.newPositionLockedCollateral, _vars.newPositionDebtShare) = bookKeeper.positions(_collateralPoolId, _positionAddress);
```

{% endcode %}

Then, the decrease of position debt Share is validated, ensuring effective liquidation:

{% code overflow="wrap" %}

```solidity
require(_vars.newPositionDebtShare < _vars.positionDebtShare, "LiquidationEngine/debt-not-liquidated");
```

{% endcode %}

Once the liquidation validation is good with `debtShare`, receipt of the expected stablecoin amount by the `SystemDebtEngine` is checked:

{% code overflow="wrap" %}

```solidity
_vars.wantStablecoinValueFromLiquidation = (_vars.positionDebtShare - _vars.newPositionDebtShare) * _collateralPoolLocalVars.debtAccumulatedRate; // [rad]
require(
    bookKeeper.stablecoin(address(systemDebtEngine)) - _vars.systemDebtEngineStablecoinBefore >= _vars.wantStablecoinValueFromLiquidation,
    "LiquidationEngine/payment-not-received"
);
```

{% endcode %}

In case the collateral is fully liquidated with remaining debt, the function records this as `systemBadDebt`:

{% code overflow="wrap" %}

```solidity
if (_vars.newPositionLockedCollateral == 0 && _vars.newPositionDebtShare > 0) {
    require(_vars.newPositionDebtShare < 2 ** 255, "LiquidationEngine/overflow");
    bookKeeper.confiscatePosition(
        _collateralPoolId,
        _positionAddress,
        _positionAddress,
        address(systemDebtEngine),
        0,
        -int256(_vars.newPositionDebtShare)
    );
}

```

{% endcode %}
