Copy // 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 getTokenX() external view returns (address);
function getTokenY() external view returns (address);
function getQuote(bool swapXtoY, uint256 amountIn)
external view returns (uint256 amountOut, uint256 actualAmountIn, uint256 feeIn);
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 tokenIn = swapXtoY
? IOraclePool(pool).getTokenX()
: IOraclePool(pool).getTokenY();
// 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)
amountOut = uint256(swapXtoY ? -deltaY : -deltaX);
// 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
}
}