Skip to content

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 ​

Swap Event

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.)

Deposited Event

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.

Withdrawn Event

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.

TypeScript Example: Tracking Liquidity ​

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}`);
}

Python Example ​

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']}")

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 ​

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.

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 /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.