# Tracking Liquidity

This guide explains how to monitor liquidity in OraclePool and how to track changes via events.

### Pool Balances

OraclePool exposes a single getter for what it holds:

```solidity
function getBalances() external view returns (uint256 totalX, uint256 totalY);
```

These balances are what the oracle uses as `reserveX` / `reserveY` when computing quotes — there is no separate "reserves vs unallocated" split, no allocator role, and no `allocate()` function. Anything sitting in the pool participates in pricing.

The pair's tokens are returned together:

```solidity
function getTokens() external view returns (address tokenX, address tokenY);
```

### Reading Current State

```solidity
(address tokenX, address tokenY) = pool.getTokens();
(uint256 balanceX, uint256 balanceY) = pool.getBalances();
```

Caveat: fees that have already been forwarded to the FeeRewarder are no longer in the pool's balance — `getBalances` reflects what the pool actually owns at the moment of the call.

### Events

<details>

<summary>Swap Event</summary>

Emitted on every swap. Balances change as tokens flow in/out.

```solidity
event Swap(
    address indexed sender,
    address indexed recipient,
    bool indexed swapXtoY,
    uint256 actualAmountIn,
    uint256 amountOut,
    uint256 feeIn,
    uint256 feeOut
);
```

Parameters:

* `sender` - Address that initiated the swap (and implements the swap callback)
* `recipient` - Address that received the output tokens
* `swapXtoY` - Direction: `true` = tokenX → tokenY, `false` = tokenY → tokenX
* `actualAmountIn` - Gross amount of input token consumed (includes `feeIn`)
* `amountOut` - Amount of output token sent to `recipient`
* `feeIn` - Fee charged on the **input** side. Non-zero only for Y→X swaps (in tokenY).
* `feeOut` - Fee charged on the **output** side. Non-zero only for X→Y swaps (in tokenY).

Both fee fields cannot be non-zero in the same swap. Fees are transferred to the FeeRewarder immediately after the swap (when one is set); otherwise they remain in the pool.

Balance changes from Swap:

* `swapXtoY = true` (X → Y): `balanceX += actualAmountIn`, `balanceY -= amountOut + feeOut`
* `swapXtoY = false` (Y → X): `balanceY += actualAmountIn - feeIn`, `balanceX -= amountOut`

(Each side then loses the corresponding fee transferred to the FeeRewarder, if set.)

</details>

<details>

<summary>Deposited Event</summary>

Emitted when liquidity is added.

```solidity
event Deposited(
    address indexed user,
    address indexed receiver,
    uint256 amountX,
    uint256 amountY,
    uint256 shares
);
```

`amountX` / `amountY` are the absolute amounts pulled into the pool; `shares` is the LP token amount minted to `receiver`.

</details>

<details>

<summary>Withdrawn Event</summary>

Emitted when liquidity is removed.

```solidity
event Withdrawn(
    address indexed user,
    address indexed receiver,
    uint256 shares,
    uint256 amountX,
    uint256 amountY
);
```

`shares` is the LP token amount burned from `user`; `amountX` / `amountY` are the absolute amounts sent to `receiver`.

</details>

### TypeScript Example: Tracking Liquidity

{% code title="liquidity-tracker.ts" %}

```typescript
import { ethers } from 'ethers';

const ORACLE_POOL_ABI = [
    "function getTokens() view returns (address tokenX, address tokenY)",
    "function getBalances() view returns (uint256 totalX, uint256 totalY)",
    "event Swap(address indexed sender, address indexed recipient, bool indexed swapXtoY, uint256 actualAmountIn, uint256 amountOut, uint256 feeIn, uint256 feeOut)",
    "event Deposited(address indexed user, address indexed receiver, uint256 amountX, uint256 amountY, uint256 shares)",
    "event Withdrawn(address indexed user, address indexed receiver, uint256 shares, uint256 amountX, uint256 amountY)"
];

interface LiquidityState {
    tokenX: string;
    tokenY: string;
    balanceX: bigint;
    balanceY: bigint;
}

class LiquidityTracker {
    private pool: ethers.Contract;
    private state: LiquidityState;

    constructor(provider: ethers.Provider, poolAddress: string) {
        this.pool = new ethers.Contract(poolAddress, ORACLE_POOL_ABI, provider);
        this.state = { tokenX: "", tokenY: "", balanceX: 0n, balanceY: 0n };
    }

    async initialize(): Promise<LiquidityState> {
        const [tokens, balances] = await Promise.all([
            this.pool.getTokens(),
            this.pool.getBalances()
        ]);

        this.state = {
            tokenX: tokens.tokenX,
            tokenY: tokens.tokenY,
            balanceX: balances.totalX,
            balanceY: balances.totalY
        };

        return this.state;
    }

    getState(): LiquidityState {
        return { ...this.state };
    }
}

// Usage
async function main() {
    const provider = new ethers.JsonRpcProvider("https://rpc3.monad.xyz");

    const tracker = new LiquidityTracker(provider, "0x...poolAddress");
    const initialState = await tracker.initialize();

    console.log('Initial state:');
    console.log(`  Tokens:  ${initialState.tokenX} / ${initialState.tokenY}`);
    console.log(`  Balances: ${initialState.balanceX} / ${initialState.balanceY}`);
}
```

{% endcode %}

### Python Example

{% code title="get:liquidity:state.py" %}

```python
from web3 import Web3
from typing import Dict, Any

def get_liquidity_state(w3: Web3, pool_address: str) -> Dict[str, Any]:
    """
    Get current liquidity state from the pool.
    """

    pool_abi = [
        {"name": "getBalances", "type": "function", "inputs": [],
         "outputs": [{"name": "totalX", "type": "uint256"}, {"name": "totalY", "type": "uint256"}],
         "stateMutability": "view"},
        {"name": "getTokens", "type": "function", "inputs": [],
         "outputs": [{"name": "tokenX", "type": "address"}, {"name": "tokenY", "type": "address"}],
         "stateMutability": "view"},
    ]

    pool = w3.eth.contract(address=w3.to_checksum_address(pool_address), abi=pool_abi)

    # Get current state
    token_x, token_y = pool.functions.getTokens().call()
    balance_x, balance_y = pool.functions.getBalances().call()

    return {
        'token_x': token_x,
        'token_y': token_y,
        'balances': {'x': balance_x, 'y': balance_y},
    }

# Usage
if __name__ == "__main__":
    w3 = Web3(Web3.HTTPProvider("https://rpc3.monad.xyz"))

    result = get_liquidity_state(w3, "0x...poolAddress")

    print(f"Token X: {result['token_x']}")
    print(f"Token Y: {result['token_y']}")
    print(f"Balances: X={result['balances']['x']}, Y={result['balances']['y']}")
```

{% endcode %}

### Understanding Balance Changes

#### From Swaps

For X → Y swaps (`swapXtoY = true`):

```
balanceX_after = balanceX_before + actualAmountIn
balanceY_after = balanceY_before - amountOut - feeOut
```

(`feeIn` is 0 in this direction.)

For Y → X swaps (`swapXtoY = false`):

```
balanceY_after = balanceY_before + actualAmountIn - feeIn
balanceX_after = balanceX_before - amountOut
```

(`feeOut` is 0 in this direction.)

After the swap, the pool transfers `feeIn` (in tokenIn) and `feeOut` (in tokenOut) to the FeeRewarder when one is configured. If `feeRewarder == address(0)`, the fees stay in the pool's balance.

#### From Deposits and Withdrawals

* `Deposited`: `balanceX += amountX`, `balanceY += amountY` (and `shares` LP tokens minted).
* `Withdrawn`: `balanceX -= amountX`, `balanceY -= amountY` (and `shares` LP tokens burned).

Pool size is capped by `getMaxValue()` (set by the Factory admin via `setMaxValue`). Deposits self-cap to `getMaxDeposit()` so the pool's tokenY-denominated value stays under that cap.

### Key Points

{% hint style="info" %}

* The pool uses its raw `tokenX` / `tokenY` balance as the "reserves" fed to the oracle — there is no separate allocated-vs-unallocated split.
* The `Swap` event carries seven fields, including both `feeIn` and `feeOut`. Only one is non-zero per swap.
* Fees are transferred out to the FeeRewarder immediately after the swap (when set), so `getBalances` reflects the post-fee state.
* Pool size is gated by `getMaxValue()` / `setMaxValue()`, not by an `allocate()` function.
  {% endhint %}

***

## Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.lfj.gg/poe/tracking-liquidity.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.lfj.gg/poe/tracking-liquidity.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
