> ## Documentation Index
> Fetch the complete documentation index at: https://ouroborai.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Liquidation Monitor

> Multi-protocol health scanning for lending and perp positions

The Liquidation Monitor is a Stylus contract that scans user positions across
registered lending and perpetual protocols, identifies accounts below a
configurable health factor threshold, and emits events for at-risk positions.

## Protocol Registries

The contract maintains two independent registries using the **indexed mapping +
soft-delete pattern**: one for lending protocols and one for perp protocols.

```rust theme={"theme":{"light":"dracula","dark":"dracula"}}
sol_storage! {
    #[entrypoint]
    pub struct LiquidationMonitor {
        address owner;
        address[] tracked_accounts;
        uint256 risk_threshold;

        // Lending protocol registry
        uint256 lending_count;
        mapping(uint256 => address) lending_pools;
        mapping(uint256 => uint256) lending_types;
        mapping(uint256 => bool) lending_active;

        // Perp protocol registry
        uint256 perp_count;
        mapping(uint256 => address) perp_readers;
        mapping(uint256 => uint256) perp_types;
        mapping(uint256 => bool) perp_active;
    }
}
```

### Protocol Types

<CardGroup cols={2}>
  <Card title="Lending Protocols">
    | Constant               | Value | Protocol |
    | ---------------------- | ----- | -------- |
    | `LENDING_TYPE_AAVE_V3` | `0`   | Aave V3  |

    Calls `IAavePool.getUserAccountData(user)` to retrieve the health factor.
    Additional types (Compound V3, Radiant) are reserved for future use.
  </Card>

  <Card title="Perp Protocols">
    The perp registry is structurally complete but the health check dispatch is
    reserved for future implementation. Protocol type constants for GMX V2 and
    Kyan are defined but not yet wired to on-chain readers.
  </Card>
</CardGroup>

## Health Factor Scanning

The core scanning logic iterates over all active lending protocols for a given
account and returns the **lowest health factor** found:

```rust theme={"theme":{"light":"dracula","dark":"dracula"}}
pub fn get_health_factor(&self, account: Address) -> Result<U256, Vec<u8>> {
    // Iterates active lending protocols
    // Dispatches to protocol-specific interface (Aave V3)
    // Returns the minimum health factor across all protocols
}
```

<Info>
  Health factors are 18-decimal fixed-point values. A value of
  `1_000_000_000_000_000_000` (1.0) means the position is at the liquidation
  boundary. The default risk threshold is `1_100_000_000_000_000_000` (1.1).
</Info>

## Tracked Accounts

The contract maintains an on-chain list of accounts to monitor, capped at
**500 addresses**. The `scan_tracked_accounts` method iterates over this list
and emits `AccountAtRisk` events for any account whose health factor falls
below the threshold.

Account removal uses **swap-and-pop** -- the last element replaces the removed
element, keeping the operation O(1).

## Public Methods

<AccordionGroup>
  <Accordion title="initialize(risk_threshold) -> Result<()>">
    Set the caller as owner and configure the initial risk threshold. Can only
    be called once.
  </Accordion>

  <Accordion title="add_lending_protocol(pool_address, protocol_type) -> Result<U256>">
    Register a lending protocol pool. Owner only.

    * Rejects zero addresses.
    * Returns the new protocol index.
    * Emits `LendingProtocolAdded(index, poolAddress, protocolType)`.
  </Accordion>

  <Accordion title="remove_lending_protocol(index) -> Result<()>">
    Soft-delete a lending protocol. Owner only.

    * Reverts if index is out of bounds or already removed.
    * Emits `LendingProtocolRemoved(index, poolAddress)`.
  </Accordion>

  <Accordion title="add_perp_protocol(reader_address, protocol_type) -> Result<U256>">
    Register a perp protocol reader. Owner only. Same pattern as lending.

    * Emits `PerpProtocolAdded(index, readerAddress, protocolType)`.
  </Accordion>

  <Accordion title="remove_perp_protocol(index) -> Result<()>">
    Soft-delete a perp protocol. Owner only.

    * Emits `PerpProtocolRemoved(index, readerAddress)`.
  </Accordion>

  <Accordion title="get_health_factor(account) -> Result<U256>">
    Query the lowest health factor for an account across all active lending
    protocols. Reverts if no lending protocols are registered.
  </Accordion>

  <Accordion title="scan_accounts(accounts) -> Result<Vec<(Address, U256)>>">
    Scan an arbitrary list of addresses. Returns only those with a health factor
    below the risk threshold, as `(address, healthFactor)` tuples.
  </Accordion>

  <Accordion title="scan_tracked_accounts() -> Result<Vec<(Address, U256)>>">
    Scan the on-chain tracked account list. Emits `AccountAtRisk` events for
    each at-risk position found, including a block timestamp.
  </Accordion>

  <Accordion title="add_account(account) -> Result<()>">
    Add an address to the tracked list. Owner only. Reverts at the 500-account
    cap.

    * Emits `AccountAdded(account)`.
  </Accordion>

  <Accordion title="remove_account(account) -> Result<()>">
    Remove an address from the tracked list using swap-and-pop. Owner only.

    * Emits `AccountRemoved(account)`.
  </Accordion>

  <Accordion title="set_threshold(new_threshold) -> Result<()>">
    Update the risk threshold. Owner only.

    * Emits `ThresholdUpdated(oldThreshold, newThreshold)`.
  </Accordion>

  <Accordion title="tracked_count() -> U256">
    Number of currently tracked accounts.
  </Accordion>

  <Accordion title="threshold() -> U256">
    Current risk threshold value.
  </Accordion>
</AccordionGroup>

## Events

| Event                    | Indexed Fields | Data Fields                     |
| ------------------------ | -------------- | ------------------------------- |
| `AccountAtRisk`          | `account`      | `healthFactor`, `timestamp`     |
| `AccountAdded`           | `account`      | --                              |
| `AccountRemoved`         | `account`      | --                              |
| `ThresholdUpdated`       | --             | `oldThreshold`, `newThreshold`  |
| `LendingProtocolAdded`   | `index`        | `poolAddress`, `protocolType`   |
| `LendingProtocolRemoved` | `index`        | `poolAddress`                   |
| `PerpProtocolAdded`      | `index`        | `readerAddress`, `protocolType` |
| `PerpProtocolRemoved`    | `index`        | `readerAddress`                 |

## Off-Chain Integration

The off-chain agent uses this contract in two ways:

1. **On-demand health checks** -- when a user asks "check my health factor",
   the agent calls `get_health_factor` for their address.
2. **Periodic scanning** -- a background job calls `scan_tracked_accounts` and
   surfaces `AccountAtRisk` events to users via notifications.

<Warning>
  The perp protocol registry is structurally complete but health check dispatch
  for perp protocols is not yet implemented. Adding a perp protocol will
  register it in storage but it will not be included in health factor
  calculations until dispatch logic is wired.
</Warning>

## Test Coverage

The contract has 43 tests covering:

* Initialization (owner set, double-init rejection)
* Access control (owner pass, stranger rejection)
* Account management (add, remove, cap enforcement, swap-and-pop)
* Threshold updates and event emission
* Health factor queries with mocked Aave V3 responses
* Multi-account scanning (at-risk detection, healthy filtering, boundary values)
* Tracked account scanning with event verification
* Lending protocol registry (add, remove, soft-delete, event emission)
* Perp protocol registry (add, remove, access control)
* Multi-protocol health factor (returns lowest across pools)
* Edge cases (no protocols registered, all protocols removed, empty input)
