# CLAP Oracle Formula

**Concentrated-Liquidity with Asymmetric Pricing (CLAP)** extends the CLCP pricing model with a two-phase swap mechanism. Trades that rebalance the pool toward equal value execute at the flat oracle price, while the remainder follows the concentrated-liquidity curve. This document traces the complete formula path from inputs to final quote output for off-chain integration.

#### How CLAP Differs from CLCP

| Aspect                                      | CLCP                                        | CLAP                                         |
| ------------------------------------------- | ------------------------------------------- | -------------------------------------------- |
| Improving trades (rebalancing toward 50/50) | Execute on the curve (mid-price discount)   | Execute at flat oracle price P (no discount) |
| Worsening trades                            | Execute on the curve                        | Execute on the curve (identical)             |
| LP impact                                   | Pool gives away value on rebalancing trades | Pool captures full fee on rebalancing trades |

CLAP is strictly better for LPs when trades rebalance the pool, because the flat-price phase produces fewer output tokens than the curve would.

#### Inputs

| Parameter         | Source      | Description                                                       | Format                                                           |
| ----------------- | ----------- | ----------------------------------------------------------------- | ---------------------------------------------------------------- |
| `reserveX`        | Pool        | Current pool balance of token X (passed via `pool.getBalances()`) | Token decimals                                                   |
| `reserveY`        | Pool        | Current pool balance of token Y (passed via `pool.getBalances()`) | Token decimals                                                   |
| `price` (P)       | Oracle data | Price of X in terms of Y                                          | 1e24 precision                                                   |
| `alpha`           | Oracle data | Concentration factor defining range **\[P/alpha, P\*alpha]**      | BPS (10000 = 1.0); valid range enforced on-chain: (10000, 65535] |
| `feeHbps`         | Oracle data | Trading fee                                                       | Hundredths of a basis point (1e6 = 100%)                         |
| `amountInWithFee` | Caller      | Gross input amount (fee included)                                 | Token decimals                                                   |
| `swapXtoY`        | Caller      | Swap direction                                                    | Boolean                                                          |

{% hint style="info" %}
All intermediate math uses **1e24 precision** (the `PRECISION` constant). Alpha is expressed in basis points where 10000 = 1.0, so `alpha = 10100` means a 1.01x concentration factor. The on-chain validator (`src/libraries/Alpha.sol`) restricts alpha to `(10000, 65535]`, so the maximum concentration factor is roughly 6.55x.
{% endhint %}

#### The Quote Formula

The `getQuote` function computes the swap output in two phases: a **stable phase** at the flat oracle price, followed by a **curve phase** on the concentrated-liquidity xy=k curve.

**Step 1 — Fee Deduction**

The fee is extracted from the gross input first:

```
feeIn    = ceil(amountInWithFee * feeHbps / 1e6)
amountIn = amountInWithFee - feeIn
```

**Step 2 — Stable Phase (Flat Oracle Price)**

The improving portion of a trade — the part that moves reserves toward 50/50 value — executes at the flat oracle price `P`. This phase only fires when the trade reduces the pool's imbalance.

**Imbalance check.** Compare the value of each reserve side:

```
scaledPrx = P * reserveX
scaledRy  = reserveY * 1e24
```

| Direction | Improving condition                      | Target input to reach balance                   |
| --------- | ---------------------------------------- | ----------------------------------------------- |
| X → Y     | `scaledPrx < scaledRy` (pool is Y-heavy) | `targetX = (scaledRy - scaledPrx) / (2 * P)`    |
| Y → X     | `scaledPrx > scaledRy` (pool is X-heavy) | `targetY = (scaledPrx - scaledRy) / (2 * 1e24)` |

If the condition is not met, the trade is worsening from the start — skip directly to Step 3 with `stableIn = 0` and `stableOut = 0`.

**Stable execution.** Consume at most enough input to reach balance:

```
stableIn = min(amountIn, target)
```

Output at flat price:

| Direction | stableOut                    |
| --------- | ---------------------------- |
| X → Y     | `floor(stableIn * P / 1e24)` |
| Y → X     | `floor(stableIn * 1e24 / P)` |

**Update working reserves** for the curve phase:

```
// X → Y
reserveX' = reserveX + stableIn
reserveY' = reserveY - stableOut

// Y → X
reserveX' = reserveX - stableOut
reserveY' = reserveY + stableIn
```

Reduce remaining input:

```
remainingIn = amountIn - stableIn
```

**Step 3 — Curve Phase (Concentrated Liquidity)**

The remaining input swaps on the concentrated xy=k curve defined by virtual reserves. This is the same CLCP formula used in the standard oracle.

**Computing Liquidity L**

Solve the concentrated-liquidity invariant for `L` using the updated reserves `(reserveX', reserveY')`:

```
pxAddY = P * reserveX' + reserveY' * 1e24

delta  = pxAddY^2 + 4 * P * reserveX' * reserveY' * 1e24 * (alpha - 10000) / 10000
```

Compute the square root of `alpha * P`:

```
scaledAlphaP   = alpha * P * 1e24 / 10000
sqrtAlphaP     = floor(sqrt(scaledAlphaP))
ceilSqrtAlphaP = ceil(sqrt(scaledAlphaP))
```

Then:

```
L = floor( (pxAddY + sqrt(delta)) * alpha / (2 * ceilSqrtAlphaP * (alpha - 10000)) )
```

**Computing Virtual Reserves**

| Direction | virtualIn (rounded up)                                          | virtualOut (rounded down)                                    | reserveOut  |
| --------- | --------------------------------------------------------------- | ------------------------------------------------------------ | ----------- |
| X → Y     | `reserveX' + ceil(L * 1e24 / sqrtAlphaP)`                       | `reserveY' + floor(L * sqrtAlphaP * 10000 / (alpha * 1e24))` | `reserveY'` |
| Y → X     | `reserveY' + ceil(L * ceilSqrtAlphaP * 10000 / (alpha * 1e24))` | `reserveX' + floor(L * 1e24 / ceilSqrtAlphaP)`               | `reserveX'` |

{% hint style="info" %}
Virtual reserves are rounded to favor the protocol: `virtualIn` rounds up, `virtualOut` rounds down.
{% endhint %}

**Constant-Product Output**

Apply the standard constant-product formula on the virtual reserves:

```
cpAmountOut = floor(remainingIn * virtualOut / (virtualIn + remainingIn))
```

**Reserve Cap**

If `cpAmountOut > reserveOut`, the pool cannot deliver the full curve output. The amounts are capped and the fee is recomputed against the **adjusted** side. The recomputation branch differs by direction:

For **X → Y** (the fee is on the output):

```
amountOut      = stableOut + reserveOut
actualAmountIn = stableIn + ceil(virtualIn * reserveOut / (virtualOut - reserveOut))
feeOut         = ceil(amountOut * feeHbps / 1e6)        // fee-inclusive
amountOut      = amountOut - feeOut
```

For **Y → X** (the fee is on the input):

```
amountOut      = stableOut + reserveOut
actualAmountIn = stableIn + ceil(virtualIn * reserveOut / (virtualOut - reserveOut))
feeIn          = ceil(actualAmountIn * feeHbps / (1e6 - feeHbps))   // fee-exclusive
actualAmountIn = actualAmountIn + feeIn
```

Otherwise (normal case — entire input consumed):

```
amountOut      = stableOut + cpAmountOut
actualAmountIn = amountInWithFee
```

For X → Y in the normal case, the output-side fee is still applied:

```
feeOut    = ceil(amountOut * feeHbps / 1e6)
amountOut = amountOut - feeOut
```

**Step 4 — Outputs**

| Field            | Description                                              |
| ---------------- | -------------------------------------------------------- |
| `amountOut`      | Tokens the trader receives (after `feeOut` for X → Y)    |
| `actualAmountIn` | Gross tokens consumed (includes `feeIn` for Y → X)       |
| `feeIn`          | Fee charged on the input side. Non-zero only for Y → X.  |
| `feeOut`         | Fee charged on the output side. Non-zero only for X → Y. |

#### Bid/Ask Spread

The `getCurrentPrice` function returns a bid/ask pair rather than a single price. It computes the curve-implied price from the current reserves and pairs it with the oracle price.

**Curve-implied price:**

```
sqrtP   = sqrtAlphaP * 10000 / alpha + reserveY * 1e24 / L
cpPrice = floor(sqrtP * sqrtP / 1e24)
cpPrice = clamp(cpPrice, ceil(P * 10000 / alpha), floor(P * alpha / 10000))
```

**Bid/Ask assignment:**

```
if cpPrice > P:
    bidPrice = P
    askPrice = cpPrice
else:
    bidPrice = cpPrice
    askPrice = P
```

The oracle price is always one of the two bounds; the other is the curve-derived price from the current reserve ratio.

#### Solidity Entry Points

```solidity
// Get bid/ask spread (1e24 precision) — CLAP-specific shape
function getCurrentPrice(bytes32 key, uint256 reserveX, uint256 reserveY)
    external view returns (uint256 bidPriceE24, uint256 askPriceE24);

// Quote a swap — returns output, actual input consumed, and split fees
function getQuote(
    bytes32 key,
    uint256 reserveX,
    uint256 reserveY,
    uint256 amountInWithFee,
    bool swapXtoY
) external view returns (uint256 amountOut, uint256 actualAmountIn, uint256 feeIn, uint256 feeOut);

// Read raw oracle parameters
function getData(bytes32 key)
    external view returns (uint256 price, uint256 feeHbps, uint256 alpha, uint256 expiry);
```

Note: `ClcpOracle` exposes the same `getQuote` signature but a single-value `getCurrentPrice(...) returns (uint256 priceE24)`. The deployed POE oracle is `ClapOracle`.

The oracle key is derived from the token pair:

```solidity
bytes32 key = keccak256(abi.encodePacked(tokenX, tokenY));
```

{% hint style="warning" %}
Order matters! `tokenX` must come first when computing the key.
{% 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/clap-oracle-formula.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/clap-oracle-formula.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.
