npm package: @revxchain/uniswap-v4-utils
Description: Solidity library with various utility functions for UniswapV4 interactions
Author: Evgeny RevxChain
Latest version: 1.0.0

# UniswapV4 Utils

<div align="center">

A Solidity library providing various utility functions for UniswapV4 interactions — spot price quotes, TWAP quotes (via hook oracle), sqrtPriceX96 calculations, calculations of amounts for providing liquidity and existing position, tick aligners and accumulated fee getters.

[![Ethereum](https://img.shields.io/badge/Ethereum-Compatible-blue?logo=ethereum)](https://ethereum.org)
[![Solidity](https://img.shields.io/badge/Solidity-^0.8.20-blue?logo=solidity)](https://soliditylang.org)
[![Uniswap V4](https://img.shields.io/badge/Uniswap-V4-ff007a?logo=uniswap)](https://docs.uniswap.org/contracts/v4/overview)
[![Hardhat](https://img.shields.io/badge/Hardhat-Toolkit-yellow?logo=hardhat)](https://hardhat.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/RevxChain/uniswap-v4-utils/.github%2Fworkflows%2Ftests.yml?label=CI)
](https://github.com/RevxChain/uniswap-v4-utils/actions)
[![NPM Version](https://img.shields.io/npm/v/%40revxchain%2Funiswap-v4-utils?color=green)](https://www.npmjs.com/package/@revxchain/uniswap-v4-utils)

</div>

---

## Table of Contents

- [Overview](#overview)
- [Main Features](#main-features)
- [Project Structure](#project-structure)
- [Key Components](#key-components)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Security Considerations](#security-considerations)
- [License](#license)
- [Contributing](#contributing)
- [Support](#support)

---

## Overview

`UniswapV4Utils` is a Solidity library that contains the common math operations and getters needed when integrating with the [UniswapV4](https://docs.uniswap.org/contracts/v4/overview) protocol. 

It consolidates spot price quotes, TWAP quotes (if a suitable [hook](https://docs.uniswap.org/contracts/v4/concepts/hooks) implementing the [IGeomeanOracle](https://github.com/Uniswap/v4-periphery/blob/1ca56ab7abc456a46e08b6ca6180bd68ec6a4770/contracts/hooks/examples/GeomeanOracle.sol) interface is available), `sqrtPriceX96` calculations (using amounts, `lowerSqrtPriceX96`, or `upperSqrtPriceX96`), calculations of amounts for providing liquidity (compute proportional amounts) and existing position (compute current position's principal), tick aligners (by `sqrtPriceX96` or `tick`) and accumulated fee getters (unclaimed and total earned) into a single module.

It also includes [Hardhat Fixtures](https://v2.hardhat.org/hardhat-runner/docs/guides/test-contracts#using-fixtures) for full local deployment of the UniswapV4 protocol and a setup for testing integration using a mainnet fork (as well as combined V3 + V4 for both). 

---

## Main Features

| Feature | Function |
|---|---|
| TWAP quote with custom window and force flag | `getTimeWeightedAmountOut(poolManager, poolKey, tokenIn, amountIn, secondsAgo, force, observeGasLimit)` |
| Spot price quote | `getAmountOut(poolManager, poolKey, tokenIn, amountIn)` |
| Upper `sqrtPriceX96` boundary for a position | `getUpperSqrtPriceX96(lowerSqrtPriceX96, currentSqrtPriceX96, amount0, amount1)` |
| Lower `sqrtPriceX96` boundary for a position | `getLowerSqrtPriceX96(currentSqrtPriceX96, upperSqrtPriceX96, amount0, amount1)` |
| `sqrtPriceX96` from tokens amounts ratio | `getSqrtPriceX96(amount0, amount1)` |
| Nearest valid tick from `sqrtPriceX96` | `getValidTick(sqrtPriceX96, tickSpacing)` |
| Nearest valid tick from `tick` | `getValidTick(tick, tickSpacing)` |
| Proportional token amounts for a providing liquidity | `getProportionalAmounts(poolManager, poolKey, amount0, amount1, tickLower, tickUpper)` |
| Liquidity units from token amounts | `getLiquidityForAmounts(poolManager, poolKey, tickLower, tickUpper, amount0, amount1)` |
| Principal token amounts in existing position | `getPositionPrincipal(poolManager, positionManager, tokenId)` |
| Unclaimed swap fees | `getUnclaimedFees(poolManager, positionManager, tokenId)` |
| Total earned swap fees | `getTotalEarnedFees(poolManager, positionManager, tokenId)` |


---

## Project Structure

```
uniswap-v4-utils/
├── src/
│   ├── UniswapV4Utils.sol              # Main Solidity library
│   │
│   ├── interfaces/
│   │   ├── IPositionManagerTyped.sol   # Struct-typed PositionManager interface
│   │   └── IGeomeanOracle.sol          # GeomeanOracle hook interface
│   │
│   └── mocks/
│       ├── ERC20Token.sol              # Test ERC20 token
│       ├── UniswapV4UtilsMock.sol      # UniswapV4Utils wrapper
│       └── GeomeanOracle/              # Test GeomeanOracle hook
│           ├── GeomeanOracle.sol
│           ├── BaseHook.sol
│           ├── ImmutableState.sol
│           ├── Oracle.sol
│           └── SafeCallback.sol
│
├── test/
│   ├── UniswapV4DeploymentFixture.js   # Full UniswapV4 local deployment + mainnet fork setup
│   ├── UniswapV3V4DeploymentFixture.js # Combined Uniswap V3 + V4 local deployment + mainnet fork setup
│   ├── UniswapV4UtilsFixture.js        # Fixture for library test
│   ├── UniswapV4UtilsTest.js           # Main test suite
│   ├── FixturesTest.js                 # Fixture tests
│   └── TestUtils.js                    # Test helpers
│
├── artifacts/                          # Compiled contract artifacts
├── coverage/                           # Code coverage reports
├── .github/                            # Github CI files
├── env.example                         # Example of .env file
├── uniswap.addresses.json              # UniswapV4 deployments per chain
├── hardhat.config.js                   # Hardhat configuration
├── .solcover.js                        # Solidity coverage configuration
├── slither.config.json                 # Slither analyzer config for CI
├── package.json                        # Dependencies and package info
└── README.md                           # This file
```

---

## Key Components

### [UniswapV4Utils.sol](https://github.com/RevxChain/uniswap-v4-utils/blob/main/src/UniswapV4Utils.sol)

The primary Solidity library. Relies on the following math libraries:

- `UniswapV3Utils`, `OracleLibrary`, `LiquidityAmounts` — UniswapV3 core math utilities from [@revxchain/uniswap-v3-utils](https://www.npmjs.com/package/@revxchain/uniswap-v3-utils).
- `FixedPoint128` — fixed-point Q128 constant from [@uniswap/v4-core](https://github.com/Uniswap/v4-core/blob/main/src/libraries/FixedPoint128.sol).
- `StateLibrary` — pool state reader from [@uniswap/v4-core](https://github.com/Uniswap/v4-core/blob/main/src/libraries/StateLibrary.sol).
- `TickMath` — UniswapV4 tick math from [@uniswap/v4-core](https://github.com/Uniswap/v4-core/blob/main/src/libraries/TickMath.sol).
- `FullMath` — overflow-safe `mulDiv` from [@uniswap/v4-core](https://github.com/Uniswap/v4-core/blob/main/src/libraries/FullMath.sol).
- `Currency`, `PoolKey` — UniswapV4 types from [@uniswap/v4-core](https://github.com/Uniswap/v4-core/tree/main/src/types).

### [UniswapV4 Deployment Fixture](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV4DeploymentFixture.js#L3)

Hardhat fixture that deploys the complete UniswapV4 protocol from pre-compiled artifacts:

| Contract | Source |
|---|---|
| [WETH9](https://github.com/gnosis/canonical-weth/blob/master/contracts/WETH9.sol) | [`github/gnosis/canonical-weth/build/gnosis/canonical-weth/`](https://github.com/gnosis/canonical-weth/blob/master/build/contracts/WETH9.json) |
| [PoolManager](https://github.com/Uniswap/v4-core/blob/main/src/PoolManager.sol) | [`@uniswap/v4-core/out/`](https://www.npmjs.com/package/@uniswap/v4-core/v/1.0.2?activeTab=code) |
| [PositionDescriptor](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionDescriptor.sol) | [`@uniswap/v4-periphery/foundry-out/`](https://www.npmjs.com/package/@uniswap/v4-periphery?activeTab=code) |
| [PositionManager](https://github.com/Uniswap/v4-periphery/blob/main/src/PositionManager.sol) | [`@uniswap/v4-periphery/foundry-out/`](https://www.npmjs.com/package/@uniswap/v4-periphery?activeTab=code) |
| [V4Quoter](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/V4Quoter.sol) | [`@uniswap/v4-periphery/foundry-out/`](https://www.npmjs.com/package/@uniswap/v4-periphery?activeTab=code) |
| [StateView](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) | [`@uniswap/v4-periphery/foundry-out/`](https://www.npmjs.com/package/@uniswap/v4-periphery?activeTab=code) |
| [Permit2](https://github.com/Uniswap/permit2/blob/main/src/Permit2.sol) | [`@revxchain/uniswap-v3-utils/build/@uniswap/permit2/`](https://www.npmjs.com/package/@revxchain/uniswap-v3-utils?activeTab=code) |
| [UniversalRouter](https://github.com/Uniswap/universal-router/blob/main/contracts/UniversalRouter.sol) | [`@uniswap/universal-router/artifacts`](https://www.npmjs.com/package/@uniswap/universal-router/v/2.1.0) |

### [UniswapV4 Deployment Fixture custom WETH](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV4DeploymentFixture.js#L19)

Hardhat fixture creation function that deploys the complete UniswapV4 protocol from compiled artifacts with custom `WETH` contract.

### [UniswapV4 Mainnet Fork Setup](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV4DeploymentFixture.js#L108)

The function uses [uniswap.addresses.json](https://github.com/RevxChain/uniswap-v4-utils/blob/main/uniswap.addresses.json) to attach to live deployments for mainnet fork testing.

### [UniswapV3-V4 Deployment Fixture](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV3V4DeploymentFixture.js#L5)

Hardhat fixture that deploys both the complete UniswapV3 and UniswapV4 protocols simultaneously, sharing a single `WETH` and `Permit2` instance, and deploying a unified `UniversalRouter` that routes through both protocol versions.

### [UniswapV3-V4 Deployment Fixture custom WETH](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV3V4DeploymentFixture.js#L21)

Hardhat fixture creation function that deploys both UniswapV3 and UniswapV4 protocols with custom `WETH` contract.

### [UniswapV3-V4 Mainnet Fork Setup](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV3V4DeploymentFixture.js#L63)

The function attaches to live UniswapV3 and UniswapV4 deployments for mainnet fork testing using [uniswap.addresses.json](https://github.com/RevxChain/uniswap-v4-utils/blob/main/uniswap.addresses.json) for V4 contracts and [uniswap.addresses.json](https://github.com/RevxChain/uniswap-v3-utils/blob/main/uniswap.addresses.json) for V3.

#### [V4 Supported networks](https://docs.uniswap.org/contracts/v4/deployments)

| Network | Chain ID |
|---|---|
| Ethereum | 1 |
| Unichain | 130 |
| Arbitrum | 42161 |
| Optimism | 10 |
| Polygon | 137 |
| Base | 8453 |
| BSC | 56 |
| Avalanche | 43114 |
| Celo | 42220 |
| Blast | 81457 |
| Zora | 7777777 |
| Monad | 143 |
| MegaETH | 4326 |
| WorldChain | 480 |
| Ink | 57073 |
| Soneium | 1868 |

> [!WARNING]
> For networks where Ether is not the native asset, the `WETH` contract is used regardless. In reality, depends on the network; this is the wrapped version of the native asset.

> [!WARNING]
> V3 and V4 deployments has a unique set of supported networks and contracts. Must familiarize with the [V4 deployments](https://github.com/RevxChain/uniswap-v4-utils/blob/main/uniswap.addresses.json) and [V3 deployments](https://github.com/RevxChain/uniswap-v3-utils/blob/main/uniswap.addresses.json) of the selected network before using it. For unsupported networks or/and contracts, zero-address placeholders will be used.

---

## Installation

### Repository

#### Step 1: Clone the repository

```bash
git clone https://github.com/RevxChain/uniswap-v4-utils.git
cd uniswap-v4-utils
```

#### Step 2: Install dependencies

```bash
npm install
```

#### Step 3: Create environment file (optional)

```bash
cp .env.example .env
```

Edit `.env` with your configuration:

```env
# PRIVATE KEYS
PRIVATE_KEY = your_private_key_here

# MAINNET FORK TEST
FORK_RPC_URL = https://eth.llamarpc.com
FORK_BLOCK_NUMBER = 24540000

# RPC URLS
ETH_RPC_URL = https://eth.llamarpc.com
```

#### Step 4: Compile contracts

```bash
npx hardhat compile
```

Compiles all Solidity contracts and generates artifacts.

#### Step 5: Run tests

```bash
npx hardhat test
```

#### Step 6: Run coverage

```bash
npx hardhat coverage
```

#### Step 7: Run tests with custom fuzzing runs

Set fuzzing runs value in the `hardhat.config.js`:

```
fuzzing: {
    enabled: true,
    runs: 100
},
```

Run tests:

```bash
npx hardhat test
```

Run coverage:

```bash
npx hardhat coverage
```

### Package

```bash
npm install @revxchain/uniswap-v4-utils
```

---

## Quick Start

### Solidity library:

#### [UniswapV4Utils.sol](https://github.com/RevxChain/uniswap-v4-utils/blob/main/src/UniswapV4Utils.sol)

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";

import {UniswapV4Utils} from "@revxchain/uniswap-v4-utils/src/UniswapV4Utils.sol";

contract MyContract {

    /// @notice Calculates the output amount for a token swap using a custom TWAP observation 
    /// window with optional force fallback.
    function getTimeWeightedAmountOut(
        IPoolManager poolManager,
        PoolKey memory poolKey,
        address tokenIn,
        uint256 amountIn,
        uint32 secondsAgo,
        bool force,
        uint256 observeGasLimit
    ) external view returns(uint256 amountOut) {
        return UniswapV4Utils.getTimeWeightedAmountOut(
            poolManager,
            poolKey,
            tokenIn,
            amountIn,
            secondsAgo,
            force,
            observeGasLimit
        );
    }

    /// @notice Calculates the output amount for a token swap at the current spot price.
    function getAmountOut(
        IPoolManager poolManager, 
        PoolKey memory poolKey, 
        address tokenIn, 
        uint256 amountIn
    ) external view returns(uint256 amountOut) {
        return UniswapV4Utils.getAmountOut(poolManager, poolKey, tokenIn, amountIn);
    }

    /// @notice Calculates the upper price boundary (sqrtPriceX96) for a liquidity position.
    function getUpperSqrtPriceX96(
        uint160 lowerSqrtPriceX96,
        uint160 currentSqrtPriceX96,
        uint256 amount0,
        uint256 amount1
    ) external pure returns(uint160 upperSqrtPriceX96) {
        return UniswapV4Utils.getUpperSqrtPriceX96(lowerSqrtPriceX96, currentSqrtPriceX96, amount0, amount1);
    }

    /// @notice Calculates the lower price boundary (sqrtPriceX96) for a liquidity position.
    function getLowerSqrtPriceX96(
        uint160 currentSqrtPriceX96,
        uint160 upperSqrtPriceX96,
        uint256 amount0,
        uint256 amount1
    ) external pure returns(uint160 lowerSqrtPriceX96) {
        return UniswapV4Utils.getLowerSqrtPriceX96(currentSqrtPriceX96, upperSqrtPriceX96, amount0, amount1);
    }

    /// @notice Calculates the effective square root of price (in Q64.96 format) from the ratio of two token 
    /// amounts.
    function getSqrtPriceX96(uint256 amount0, uint256 amount1) external pure returns(uint160 sqrtPriceX96) {
        return UniswapV4Utils.getSqrtPriceX96(amount0, amount1);
    }

    /// @notice Finds the nearest valid tick for a specified square root price, aligned to the tick spacing.
    function getValidTick(uint160 sqrtPriceX96, int24 tickSpacing) external pure returns(int24 validTick) {
        return UniswapV4Utils.getValidTick(sqrtPriceX96, tickSpacing);
    }

    /// @notice Finds the nearest valid tick for a specified tick, aligned to the specified tick spacing.
    function getValidTick(int24 tick, int24 tickSpacing) external pure returns(int24 validTick) {
        return UniswapV4Utils.getValidTick(tick, tickSpacing);
    }

    /// @notice Calculates the proportional amounts of both tokens required to provide liquidity within 
    /// a specified price range.
    function getProportionalAmounts(
        IPoolManager poolManager,
        PoolKey memory poolKey,
        uint256 amount0,
        uint256 amount1,
        int24 tickLower,
        int24 tickUpper
    ) external view returns(uint256 amount0Required, uint256 amount1Required) {
        return UniswapV4Utils.getProportionalAmounts(
            poolManager,
            poolKey,
            amount0,
            amount1,
            tickLower,
            tickUpper
        );
    }

    /// @notice Calculates liquidity units from the specified token amounts for the specified price range.
    function getLiquidityForAmounts(
        IPoolManager poolManager,
        PoolKey memory poolKey,
        int24 tickLower,
        int24 tickUpper,
        uint256 amount0,
        uint256 amount1
    ) external view returns(uint128 liquidity) {
        return UniswapV4Utils.getLiquidityForAmounts(
            poolManager,
            poolKey,
            tickLower,
            tickUpper,
            amount0,
            amount1
        );
    }

    /// @notice Calculates the actual token amounts (amount0 and amount1) represented by a position's liquidity.
    function getPositionPrincipal(
        IPoolManager poolManager,
        address positionManager,
        uint256 tokenId
    ) external view returns(uint256 amount0, uint256 amount1) {
        return UniswapV4Utils.getPositionPrincipal(poolManager, positionManager, tokenId);
    }

    /// @notice Calculates the unclaimed swap fees for the specified liquidity position.
    function getUnclaimedFees(
        IPoolManager poolManager, 
        address positionManager, 
        uint256 tokenId
    ) external view returns(uint256 amount0, uint256 amount1) {
        return UniswapV4Utils.getUnclaimedFees(poolManager, positionManager, tokenId);
    }

    /// @notice Calculates the total earned swap fees for the specified liquidity position.
    function getTotalEarnedFees(
        IPoolManager poolManager, 
        address positionManager, 
        uint256 tokenId
    ) external view returns(uint256 amount0, uint256 amount1) {
        return UniswapV4Utils.getTotalEarnedFees(poolManager, positionManager, tokenId);
    }
}
```

### Hardhat test environment:

#### [UniswapV4DeploymentFixture()](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV4DeploymentFixture.js#L3)

```js
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
const { UniswapV4DeploymentFixture } = require("@revxchain/uniswap-v4-utils/test/UniswapV4DeploymentFixture.js");

const {
    uniswapV4Deployer, weth, poolManager, positionDescriptor, positionManager, quoter, stateView, permit2, universalRouter
} = await loadFixture(UniswapV4DeploymentFixture);
```

#### [createUniswapV4DeploymentFixtureCustomWETH()](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV4DeploymentFixture.js#L19)

```js
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
const { createUniswapV4DeploymentFixtureCustomWETH } = require("@revxchain/uniswap-v4-utils/test/UniswapV4DeploymentFixture.js");

// your custom WETH's address
const wethAddress = "0x...";

// call function with custom WETH's address
const UniswapV4DeploymentFixtureCustomWETH = createUniswapV4DeploymentFixtureCustomWETH(wethAddress);

// initialize fixture
const {
    uniswapV4Deployer, weth, poolManager, positionDescriptor, positionManager, quoter, stateView,
    permit2, universalRouter
} = await loadFixture(UniswapV4DeploymentFixtureCustomWETH);
```

#### [UniswapV4MainnetForkSetup()](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV4DeploymentFixture.js#L108)

```js
const { UniswapV4MainnetForkSetup } = require("@revxchain/uniswap-v4-utils/test/UniswapV4DeploymentFixture.js");

const targetChainId = 1;

const {
    weth, poolManager, positionDescriptor, positionManager, quoter, stateView, permit2, universalRouter
} = await UniswapV4MainnetForkSetup(targetChainId);
```

#### [UniswapV3V4DeploymentFixture()](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV3V4DeploymentFixture.js#L5)

```js
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
const { UniswapV3V4DeploymentFixture } = require("@revxchain/uniswap-v4-utils/test/UniswapV3V4DeploymentFixture.js");

const {
    uniswapFactory, descriptorLibrary, tokenDescriptor, nonfungiblePositionManager, swapRouter01, swapRouter02,
    quoter01, quoter02, tickLens, multicall, multicall2, uniswapV4Deployer, weth, poolManager, positionDescriptor,
    positionManager, quoter, stateView, permit2, universalRouter, uniswapV3PoolBytecode, uniswapV3PoolAbi
} = await loadFixture(UniswapV3V4DeploymentFixture);
```

#### [createUniswapV3V4DeploymentFixtureCustomWETH()](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV3V4DeploymentFixture.js#L21)

```js
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
const { createUniswapV3V4DeploymentFixtureCustomWETH } = require("@revxchain/uniswap-v4-utils/test/UniswapV3V4DeploymentFixture.js");

// your custom WETH's address
const wethAddress = "0x...";

// call function with custom WETH's address
const UniswapV3V4DeploymentFixtureCustomWETH = createUniswapV3V4DeploymentFixtureCustomWETH(wethAddress);

// initialize fixture
const {
    uniswapFactory, descriptorLibrary, tokenDescriptor, nonfungiblePositionManager, swapRouter01, swapRouter02, 
    quoter01, quoter02, tickLens, multicall, multicall2, uniswapV4Deployer, weth, poolManager, positionDescriptor,
    positionManager, quoter, stateView, permit2, universalRouter, uniswapV3PoolBytecode, uniswapV3PoolAbi
} = await loadFixture(UniswapV3V4DeploymentFixtureCustomWETH);
```

#### [UniswapV3V4MainnetForkSetup()](https://github.com/RevxChain/uniswap-v4-utils/blob/main/test/UniswapV3V4DeploymentFixture.js#L63)

```js
const { UniswapV3V4MainnetForkSetup } = require("@revxchain/uniswap-v4-utils/test/UniswapV3V4DeploymentFixture.js");

const targetChainId = 1;

const {
    uniswapFactory, descriptorLibrary, tokenDescriptor, nonfungiblePositionManager, swapRouter01, swapRouter02,
    quoter01, quoter02, tickLens, multicall, multicall2, weth, poolManager, positionDescriptor, positionManager,
    quoter, stateView, permit2, universalRouter, uniswapV3PoolBytecode, uniswapV3PoolAbi
} = await UniswapV3V4MainnetForkSetup(targetChainId);
```

> [!NOTE]
> For Uniswap V3-V4 fixtures `uniswapV4Deployer` is actually `uniswapV3Deployer` account too.
---

## Security Considerations

- **No input validation.** `UniswapV4Utils` does not verify the correctness of any input. Passing an address that is not a valid `PoolManager`, a `PoolKey` that does not correspond to an existing pool, or a `tokenIn` that is not one of the pool's currencies, may result in silent zero returns rather than a revert. You MUST validate inputs externally.

- **TWAP requires a compatible hook.** The `getTimeWeightedAmountOut` function relies on a hook deployed at `poolKey.hooks` that implements the `IGeomeanOracle` interface. If no such hook is available and `force=false`, the call will revert. With `force=true`, it falls back to the spot price. You MUST ensure the hook matches the logic of the built-in [`UniswapV3.Oracle`](https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/Oracle.sol) and ensure the hook is not malicious.

- **Spot price susceptibility.** Functions that use the current tick (`getAmountOut`) or the `force` fallback path are susceptible to price manipulation within the same block. Use with caution — never as the sole price oracle for financial decisions.

- **Fixed-point rounding and discrete tick logic.** All calculations use integer fixed-point arithmetic with `Q64.96` precision. In cases of very wide ranges, very narrow ranges, or extremely disproportionate token amounts, computed boundaries (`getUpperSqrtPriceX96`, `getLowerSqrtPriceX96`, `getProportionalAmounts`, `getSqrtPriceX96`, `getLiquidityForAmounts`) may differ significantly from a benchmark. Always verify remaining amounts (dust) after using this calculation for interaction. If your requirements allow for off-chain calculations, it's better to do it that way. This library is useful when full on-chain autonomy and minimal risk of functionality blocking are required.

- **Oracle observation history.** `getTimeWeightedAmountOut` (without `force`) will revert if the hook does not have sufficient historical observations for the requested `secondsAgo` window. Ensure adequate observation cardinality before relying on TWAP.

- **Gas limit during observation.** The `observeGasLimit` parameter controls the gas allocated to the `staticcall` to the hook's `observe` function. Setting it too low will cause the call to fail silently (with `force=true` falling back to spot price quote). Set a reasonable gas limit to avoid unexpected fallback behaviour.

---

## License

MIT License

Permission is hereby granted to use, copy, modify, and distribute this software freely.

See [LICENSE](LICENSE) file for full terms.

---

## Contributing

Contributions welcome! Please:
1. Fork the repository;
2. Create a feature branch;
3. Add tests for new functionality;
4. Submit a pull request.

---

## Support

- [**GitHub Issues**](https://github.com/RevxChain/uniswap-v4-utils/issues) - Report bugs and/or proposals.