# 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 reserve of token X                                 | Token decimals                    |
| `reserveY`        | Pool        | Current reserve of token Y                                 | Token decimals                    |
| `price` (P)       | Oracle data | Price of X in terms of Y                                   | 1e24 precision                    |
| `alpha`           | Oracle data | Concentration factor defining range **\[P/alpha, Palpha]** | BPS (10000 = 1.0)                 |
| `feeHbps`         | Oracle data | Trading fee                                                | Hundred basis points (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.
{% 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.

{% stepper %}
{% step %}

### Step 1 — Fee Deduction

The fee is extracted from the gross input first:

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

{% endstep %}

{% step %}

### 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
```

{% endstep %}

{% step %}

### 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 input is capped and fees are recalculated:

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

Otherwise (normal case — entire input consumed):

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

{% endstep %}

{% step %}

### Step 4 — Outputs

| Field            | Description                          |
| ---------------- | ------------------------------------ |
| `amountOut`      | Tokens the trader receives           |
| `actualAmountIn` | Gross tokens consumed (includes fee) |
| `feeIn`          | Fee portion of `actualAmountIn`      |
| {% endstep %}    |                                      |
| {% endstepper %} |                                      |

## 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)
function getCurrentPrice(bytes32 key, uint256 reserveX, uint256 reserveY)
    external view returns (uint256 bidPriceE24, uint256 askPriceE24);

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

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

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 %}
