# Making a Swap with POE

| Error                       | Cause                                                |
| --------------------------- | ---------------------------------------------------- |
| `InvalidCallback`           | Callback didn't return correct selector `0xfa483e72` |
| `ZeroTokenReceived`         | Callback didn't transfer any tokens to the pool      |
| `InsufficientTokenReceived` | Callback transferred less than required              |
| `OraclePool__OracleNotSet`  | Pool's oracle is not configured                      |
| `OracleData__Expired`       | Oracle data has expired, operator needs to update    |

This guide explains how to integrate with OraclePool to execute token swaps. The pool uses a callback pattern similar to Uniswap V3 - you call `swap()`, receive tokens optimistically, then must pay via a callback.

### Getting a Quote

Before executing a swap, you can preview the expected output using `getQuote()`:

```solidity
function getQuote(bool swapXtoY, uint256 amountIn)
    external view
    returns (uint256 amountOut, uint256 actualAmountIn, uint256 feeIn, uint256 feeOut);
```

**Parameters:**

* `swapXtoY` - Direction of the swap. `true` = sell tokenX for tokenY, `false` = sell tokenY for tokenX
* `amountIn` - The amount of input token you want to swap (fee-inclusive)

**Returns:**

* `amountOut` - Amount of output token the recipient will receive (after `feeOut`, if any)
* `actualAmountIn` - Gross input amount the pool will consume (fee-inclusive). May be less than `amountIn` if the curve would otherwise drain the pool.
* `feeIn` - Fee charged in the input token. Non-zero only for Y → X swaps.
* `feeOut` - Fee charged in the output token. Non-zero only for X → Y swaps.

Exactly one of `feeIn` / `feeOut` is non-zero. Both fees are denominated in tokenY (the pool's quote token), since X → Y fees come out of the output side and Y → X fees come out of the input side.

Example:

```solidity
// Check how much tokenY we'd get for 1000 tokenX
(uint256 amountOut, uint256 actualAmountIn, uint256 feeIn, uint256 feeOut) =
    pool.getQuote(true, 1000e18);
```

#### Reading Pool Info

To understand what tokens are involved:

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

### Executing a Swap

The swap function uses an optimistic transfer pattern with a callback:

```solidity
function swap(
    address recipient,
    bool swapXtoY,
    uint256 amountIn,
    bytes calldata data
) external returns (int256 deltaX, int256 deltaY);
```

**Parameters:**

* `recipient` - Address that receives the output tokens
* `swapXtoY` - Direction: `true` = tokenX → tokenY, `false` = tokenY → tokenX
* `amountIn` - Amount of input token to swap
* `data` - Arbitrary data passed through to the callback

**Returns:**

* `deltaX` - Change in tokenX from pool's perspective (positive = pool received, negative = pool sent)
* `deltaY` - Change in tokenY from pool's perspective

#### Swap Flow

{% stepper %}
{% step %}

#### Call swap()

Your contract calls `pool.swap(recipient, swapXtoY, amountIn, data)`.
{% endstep %}

{% step %}

#### Pool transfers output

Pool calculates `amountOut` and transfers `tokenOut` to `recipient`.
{% endstep %}

{% step %}

#### Pool calls back

Pool calls `swapCallback(deltaX, deltaY, data)` on `msg.sender` (your contract).
{% endstep %}

{% step %}

#### Your callback pays

Your callback must **push** `tokenIn` to the pool (`msg.sender` inside the callback) via `transfer` / `safeTransfer`. The pool checks its own `balanceOf` delta — it never calls `transferFrom` on your behalf.
{% endstep %}

{% step %}

#### Callback returns selector

Your callback must return `0xfa483e72` (the function selector).
{% endstep %}

{% step %}

#### Pool verifies payment

Pool verifies it received enough tokens (≥ `actualAmountIn`). Overpayment is kept by the pool as a donation to LPs.
{% endstep %}

{% step %}

#### swap() returns

`swap()` returns with the final deltas (from the pool's perspective).
{% endstep %}
{% endstepper %}

### Implementing the Callback

Your contract must implement the `ISwapCallback` interface:

```solidity
interface ISwapCallback {
    function swapCallback(int256 deltaX, int256 deltaY, bytes calldata data) 
        external 
        returns (bytes4);
}
```

The callback must:

{% stepper %}
{% step %}
Transfer the required input tokens to the pool (`msg.sender` in the callback).
{% endstep %}

{% step %}
Return the selector `0xfa483e72` (which is `ISwapCallback.swapCallback.selector`).
{% endstep %}
{% endstepper %}

Important: The callback is called with `deltaX` and `deltaY` from the pool's perspective:

* Positive delta = pool expects to receive that token
* Negative delta = pool has sent that token

### Solidity Example: Router Contract

Here's a complete example of a router contract that can swap through OraclePool:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface IOraclePool {
    function getTokens() external view returns (address tokenX, address tokenY);
    function getQuote(bool swapXtoY, uint256 amountIn)
        external view
        returns (uint256 amountOut, uint256 actualAmountIn, uint256 feeIn, uint256 feeOut);
    function swap(address recipient, bool swapXtoY, uint256 amountIn, bytes calldata data)
        external returns (int256 deltaX, int256 deltaY);
}

interface ISwapCallback {
    function swapCallback(int256 deltaX, int256 deltaY, bytes calldata data) 
        external returns (bytes4);
}

contract OraclePoolRouter is ISwapCallback {
    using SafeERC20 for IERC20;

    // Track expected callback to prevent unauthorized calls
    address private _expectedPool;

    /// @notice Swap exact input tokens for output tokens
    /// @param pool The OraclePool to swap through
    /// @param swapXtoY Direction: true = tokenX -> tokenY
    /// @param amountIn Amount of input token to swap
    /// @param minAmountOut Minimum output amount (slippage protection)
    /// @param recipient Address to receive output tokens
    /// @return amountOut Actual amount of output tokens received
    function swapExactInput(
        address pool,
        bool swapXtoY,
        uint256 amountIn,
        uint256 minAmountOut,
        address recipient
    ) external returns (uint256 amountOut) {
        // Get the input token
        (address tokenX, address tokenY) = IOraclePool(pool).getTokens();
        address tokenIn = swapXtoY ? tokenX : tokenY;

        // Transfer input tokens from user to this contract
        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);

        // Set expected pool for callback verification
        _expectedPool = pool;

        // Execute the swap - pool will call our swapCallback
        (int256 deltaX, int256 deltaY) = IOraclePool(pool).swap(
            recipient,
            swapXtoY,
            amountIn,
            abi.encode(tokenIn) // Pass tokenIn address in callback data
        );

        // Clear expected pool
        _expectedPool = address(0);

        // Calculate actual output (negative delta = pool sent tokens)
        int256 outDelta = swapXtoY ? deltaY : deltaX;
        amountOut = uint256(-outDelta);

        // Slippage check
        require(amountOut >= minAmountOut, "Insufficient output");
    }

    /// @notice Callback called by the pool during swap
    /// @dev Must transfer input tokens to the pool and return correct selector
    function swapCallback(
        int256 deltaX,
        int256 deltaY,
        bytes calldata data
    ) external override returns (bytes4) {
        // Verify caller is the expected pool
        require(msg.sender == _expectedPool, "Unauthorized callback");

        // Decode the token we need to pay
        address tokenIn = abi.decode(data, (address));

        // Determine amount to pay (positive delta = pool expects payment)
        uint256 amountToPay = deltaX > 0 ? uint256(deltaX) : uint256(deltaY);

        // Transfer tokens to the pool
        IERC20(tokenIn).safeTransfer(msg.sender, amountToPay);

        // Return the required selector
        return ISwapCallback.swapCallback.selector; // 0xfa483e72
    }
}
```

#### Usage Example

```solidity
// User wants to swap 100 tokenX for tokenY with 1% slippage tolerance
OraclePoolRouter router = OraclePoolRouter(routerAddress);
IOraclePool pool = IOraclePool(poolAddress);

// First, get a quote
(uint256 expectedOut, , , ) = pool.getQuote(true, 100e18);
uint256 minOut = expectedOut * 99 / 100; // 1% slippage

// Approve router to spend tokens
(address tokenX, ) = pool.getTokens();
IERC20(tokenX).approve(address(router), 100e18);

// Execute swap
uint256 received = router.swapExactInput(
    address(pool),
    true,           // swapXtoY
    100e18,         // amountIn
    minOut,         // minAmountOut
    msg.sender      // recipient
);
```

### TypeScript Example: Reading Quotes

```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)",
    "function getQuote(bool swapXtoY, uint256 amountIn) view returns (uint256 amountOut, uint256 actualAmountIn, uint256 feeIn, uint256 feeOut)"
];

async function getSwapQuote(
    provider: ethers.Provider,
    poolAddress: string,
    swapXtoY: boolean,
    amountIn: bigint
) {
    const pool = new ethers.Contract(poolAddress, ORACLE_POOL_ABI, provider);

    // Get pool info
    const [tokens, balances] = await Promise.all([
        pool.getTokens(),
        pool.getBalances()
    ]);

    console.log(`Pool: ${tokens.tokenX} / ${tokens.tokenY}`);
    console.log(`Balances: ${balances.totalX} / ${balances.totalY}`);

    // Get quote
    const [amountOut, actualAmountIn, feeIn, feeOut] =
        await pool.getQuote(swapXtoY, amountIn);

    console.log(`Input (gross): ${actualAmountIn}`);
    console.log(`Output: ${amountOut}`);
    console.log(`Fee in / Fee out: ${feeIn} / ${feeOut}`);

    return { amountOut, actualAmountIn, feeIn, feeOut };
}

// Example usage
const provider = new ethers.JsonRpcProvider("https://rpc.example.com");
await getSwapQuote(
    provider,
    "0x...", // pool address
    true,    // swap tokenX to tokenY
    ethers.parseEther("100") // 100 tokens
);
```

### Error Handling

Selected reverts you may encounter on the swap path:

| Error                                                            | Source               | Cause                                                                      |
| ---------------------------------------------------------------- | -------------------- | -------------------------------------------------------------------------- |
| `Callback__InvalidCallback()`                                    | `Callback` library   | Callback didn't return the selector `0xfa483e72`, or the call reverted.    |
| `Callback__ZeroTokenReceived(address token)`                     | `Callback` library   | Pool's balance of `token` did not increase during the callback.            |
| `Callback__InsufficientTokenReceived(address, uint256, uint256)` | `Callback` library   | Pool received some `token` but less than `amountIn`.                       |
| `Callback__Overflow()`                                           | `Callback` library   | `amountIn` or `amountOut` ≥ 2^255 (would overflow signed delta).           |
| `OraclePool__OracleNotSet()`                                     | `OraclePool`         | Pool's oracle is `address(0)`.                                             |
| `OraclePool__ZeroAmount()`                                       | `OraclePool`         | Quote produced `amountOut == 0`.                                           |
| `OracleData__Expired()`                                          | `OracleData` library | Oracle data for this pair has expired; operator must call `setData` again. |
| `OracleData__InvalidPrice()`                                     | `OracleData` library | Oracle has no data (or price == 0) for this pair.                          |

### Security Considerations

1. Callback Verification: Always verify that the callback caller is the expected pool to prevent malicious contracts from draining your tokens.
2. Slippage Protection: Always use `minAmountOut` checks to protect against price movement between quote and execution.
3. Reentrancy: The callback pattern can be vulnerable to reentrancy. Use reentrancy guards if your callback does anything complex.
4. Token Approvals: Be careful with token approvals. Only approve what's needed for the specific swap.

***

## 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/making-a-swap-with-poe.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/making-a-swap-with-poe.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.
