Contract Source Code:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
interface IPool {
error NOT_AUTHORIZED();
error UNSTABLE_RATIO();
/// @dev safe transfer failed
error STF();
error OVERFLOW();
/// @dev skim disabled
error SD();
/// @dev insufficient liquidity minted
error ILM();
/// @dev insufficient liquidity burned
error ILB();
/// @dev insufficient output amount
error IOA();
/// @dev insufficient input amount
error IIA();
error IL();
error IT();
error K();
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(
address indexed sender,
uint256 amount0,
uint256 amount1,
address indexed to
);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
/// @notice Same as prices with with an additional window argument.
/// Window = 2 means 2 * 30min (or 1 hr) between observations
/// @param tokenIn .
/// @param amountIn .
/// @param points .
/// @param window .
/// @return Array of TWAP prices
function sample(
address tokenIn,
uint256 amountIn,
uint256 points,
uint256 window
) external view returns (uint256[] memory);
function observations(uint256 index) external view returns (uint256 timestamp, uint256 reserve0Cumulative, uint256 reserve1Cumulative);
function current(address tokenIn, uint256 amountIn) external view returns (uint256 amountOut);
/// @notice Provides twap price with user configured granularity, up to the full window size
/// @param tokenIn .
/// @param amountIn .
/// @param granularity .
/// @return amountOut .
function quote(address tokenIn, uint256 amountIn, uint256 granularity) external view returns (uint256 amountOut);
/// @notice Get the number of observations recorded
function observationLength() external view returns (uint256);
/// @notice Address of token in the pool with the lower address value
function token0() external view returns (address);
/// @notice Address of token in the poool with the higher address value
function token1() external view returns (address);
/// @notice initialize the pool, called only once programatically
function initialize(
address _token0,
address _token1,
bool _stable
) external;
/// @notice calculate the current reserves of the pool and their last 'seen' timestamp
/// @return _reserve0 amount of token0 in reserves
/// @return _reserve1 amount of token1 in reserves
/// @return _blockTimestampLast the timestamp when the pool was last updated
function getReserves()
external
view
returns (
uint112 _reserve0,
uint112 _reserve1,
uint32 _blockTimestampLast
);
/// @notice mint the pair tokens (LPs)
/// @param to where to mint the LP tokens to
/// @return liquidity amount of LP tokens to mint
function mint(address to) external returns (uint256 liquidity);
/// @notice burn the pair tokens (LPs)
/// @param to where to send the underlying
/// @return amount0 amount of amount0
/// @return amount1 amount of amount1
function burn(
address to
) external returns (uint256 amount0, uint256 amount1);
/// @notice direct swap through the pool
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external;
/// @notice force balances to match reserves, can be used to harvest rebases from rebasing tokens or other external factors
/// @param to where to send the excess tokens to
function skim(address to) external;
/// @notice force reserves to match balances, prevents skim excess if skim is enabled
function sync() external;
/// @notice set the pair fees contract address
function setFeeRecipient(address _pairFees) external;
/// @notice set the feesplit variable
function setFeeSplit(uint256 _feeSplit) external;
/// @notice sets the swap fee of the pair
/// @dev max of 10_000 (10%)
/// @param _fee the fee
function setFee(uint256 _fee) external;
/// @notice 'mint' the fees as LP tokens
/// @dev this is used for protocol/voter fees
function mintFee() external;
/// @notice calculates the amount of tokens to receive post swap
/// @param amountIn the token amount
/// @param tokenIn the address of the token
function getAmountOut(
uint256 amountIn,
address tokenIn
) external view returns (uint256 amountOut);
/// @notice returns various metadata about the pair
function metadata()
external
view
returns (
uint256 _decimals0,
uint256 _decimals1,
uint256 _reserve0,
uint256 _reserve1,
bool _stable,
address _token0,
address _token1
);
/// @notice returns the feeSplit of the pair
function feeSplit() external view returns (uint256);
/// @notice returns the fee of the pair
function fee() external view returns (uint256);
/// @notice returns the feeRecipient of the pair
function feeRecipient() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "../interfaces/IPool.sol";
/**
* @title PoolOracle
* @notice Provides TWAP price consultations for a liquidity pool (pair) using a new Pair implementation.
* @dev This contract reads observations from the pool to compute a time-weighted average price (TWAP).
* It also allows updating the pool state by calling sync.
*/
contract PoolOracle {
// The two tokens in the pair.
address public token0;
address public token1;
// The liquidity pool (pair) from which the oracle reads price data.
IPool public pair;
/**
* @notice Initializes the PoolOracle with the given pair.
* @param _pair The liquidity pool contract.
*/
constructor(IPool _pair) {
pair = _pair;
token0 = pair.token0();
token1 = pair.token1();
// Check that the pair has non-zero reserves.
(uint256 reserve0, uint256 reserve1,) = pair.getReserves();
require(reserve0 != 0 && reserve1 != 0, "PoolOracle: No reserves");
}
/**
* @notice Updates the pool's internal state by calling sync.
*/
function update() external {
pair.sync();
}
/**
* @notice Consults the oracle for a quote based on historical data.
* @param _token The token address for which to get the quote (must be token0 or token1).
* @param _amountIn The amount of input token.
* @return amountOut The quoted output amount.
* @dev Uses a granularity of 12 observations (e.g. 6 hours if each observation is 30min).
*/
function consult(
address _token,
uint256 _amountIn
) external view returns (uint256 amountOut) {
if (_token == token0 || _token == token1) {
amountOut = _quote(_token, _amountIn, 12);
} else {
revert("PoolOracle: Invalid token");
}
}
/**
* @notice Returns the time-weighted average price (TWAP) for a given token.
* @param _token The token address for which to get the TWAP (must be token0 or token1).
* @param _amountIn The amount of input token.
* @return amountOut The TWAP quoted output amount.
* @dev Uses a granularity of 2 observations (e.g. 1 hour if each observation is 30min).
*/
function twap(
address _token,
uint256 _amountIn
) external view returns (uint256 amountOut) {
if (_token == token0 || _token == token1) {
amountOut = _quote(_token, _amountIn, 2);
} else {
revert("PoolOracle: Invalid token");
}
}
/**
* @dev Internal function to obtain a price quote from the pool.
* @param tokenIn The input token address.
* @param amountIn The input token amount.
* @param granularity The number of historical observations to use.
* @return amountOut The quoted output amount.
* @notice The granularity parameter effectively sets the time window for TWAP calculations.
* For example, if each observation represents 30 minutes, a granularity of 12 gives a 6-hour window.
*/
function _quote(
address tokenIn,
uint256 amountIn,
uint256 granularity
) internal view returns (uint256 amountOut) {
uint256 observationLength = pair.observationLength();
require(granularity <= observationLength, "PoolOracle: Not enough observations");
amountOut = pair.quote(tokenIn, amountIn, granularity);
}
}