Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
109 changes: 90 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,110 @@
# Uniswap v4 Periphery

Uniswap v4 is a new automated market maker protocol that provides extensibility and customizability to pools. `v4-periphery` hosts the logic that builds on top of the core pool logic like hook contracts, position managers, and even possibly libraries needed for integrations. The `v4-periphery` contracts in this repository are still in development and further periphery contracts have not yet been built.
A production-focused set of contracts and utilities that build on Uniswap v4 core. This repository provides routers with batched actions and per-hop slippage checks, position management via an ERC-721 position manager (PosM) supporting mint/increase/decrease/burn flows with settlement/take helpers, quoting/lens contracts for off-chain estimation via revert-encoding, base building blocks for hooks (e.g., BaseHook, SafeCallback, DeltaResolver), and libraries used by integrators.

## Contributing

If you’re interested in contributing please see the [contribution guidelines](https://github.com/Uniswap/v4-periphery/blob/main/CONTRIBUTING.md)!
The periphery is under active development; APIs may evolve as v4 matures. Review audits in `audits/` and run the comprehensive Foundry test suite before integrating. When integrating, ensure remappings point to `@uniswap/v4-core` and `v4-periphery`, target Solidity 0.8.24+ on the Cancun EVM, and prefer via-IR with the optimizer enabled.

## Local Deployment and Usage
## Contents
- `src/V4Router.sol`: internal swap routing with batched actions, per-hop slippage checks, settlement/take helpers
- `src/PositionManager.sol`: ERC-721 position NFT minting/management (PosM), liquidity modify flows, notifier integration
- `src/lens/V4Quoter.sol`: off-chain quoting via revert-encoding (not gas-efficient; do not call on-chain)
- `src/lens/StateView.sol`: read-only views into pool and position state
- `src/base/*`: shared infrastructure (`BaseActionsRouter`, `SafeCallback`, `DeltaResolver`, `ReentrancyLock`, `NativeWrapper`, `Notifier`)
- `src/libraries/*`: calldata decoding, slippage checks, path utilities, amounts math, etc.
- `src/hooks/*`: example token wrapper hooks (e.g. WETH, wstETH)
- `script/*`: Foundry deploy/util scripts
- `test/*`: extensive Foundry tests and gas benchmarks

To utilize the contracts and deploy to a local testnet, you can install the code in your repo with forge:
## Requirements
- Foundry (`forge`, `cast`)
- Uniswap v4 core as a dependency (via `remappings.txt`/`foundry.toml`)
- Solidity 0.8.24+ (some contracts use transient storage opcodes; tested on Cancun EVM)

```solidity
## Install & Build
```bash
forge install https://github.com/Uniswap/v4-periphery
forge build
```
If using as a submodule, add remappings for `@uniswap/v4-core` and `v4-periphery` and run `forge build`.

If you are building hooks, it may be useful to inherit from the `BaseHook` contract:
Example remappings (adjust to your repo):
```ini
@uniswap/v4-core/=lib/v4-core/
v4-periphery/=lib/v4-periphery/
forge-std/=lib/forge-std/src/
solmate/=lib/solmate/src/
```
Foundry config hints (`foundry.toml`):
- `solc_version = "0.8.26"` (compatible with `0.8.24+`)
- `evm_version = "cancun"`
- `via_ir = true`
- enable optimizer with appropriate runs per file

```solidity
## Quickstart
- Position management: call `modifyLiquidities` with a batched action payload to mint/increase/decrease/burn positions while atomically settling/taking credits.
- Swaps: invoke router actions (`SWAP_EXACT_IN`, `SWAP_EXACT_OUT`, single/multi-hop) with optional per-hop slippage controls, then settle/take.
- Quoting: use `V4Quoter` off-chain to estimate amounts and gas before executing on-chain.

Example: Hook skeleton
```solidity
import {BaseHook} from 'v4-periphery/src/utils/BaseHook.sol';
import {IHooks} from '@uniswap/v4-core/src/interfaces/IHooks.sol';
import {IPoolManager} from '@uniswap/v4-core/src/interfaces/IPoolManager.sol';

contract CoolHook is BaseHook {
// Override the hook callbacks you want on your hook
function beforeAddLiquidity(
address,
IPoolManager.PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params
) external override onlyByManager returns (bytes4) {
// hook logic
return BaseHook.beforeAddLiquidity.selector;
constructor(IPoolManager manager) BaseHook(manager) {}
function getHookPermissions() public pure override returns (IHooks.Permissions memory) {
// enable only the callbacks you need
return IHooks.Permissions({
beforeInitialize: true,
beforeAddLiquidity: true,
beforeSwap: true,
beforeSwapReturnDelta: false,
afterSwap: false,
afterInitialize: false,
beforeRemoveLiquidity: false,
afterAddLiquidity: false,
afterRemoveLiquidity: false,
beforeDonate: false,
afterDonate: false,
afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
}
```

## Scripts
```bash
forge script script/DeployV4Quoter.s.sol --rpc-url $RPC --private-key $PK --broadcast
forge script script/DeployPositionManager.s.sol --rpc-url $RPC --broadcast
forge script script/DeployV4Router.s.sol --rpc-url $RPC --broadcast
```
Use environment variables or `.env`; do not commit secrets.

## License
## Testing
```bash
forge test -vvv
```
- Gas snapshots in `snapshots/`
- Scenario tests cover swaps, liquidity flows, notifier behavior, and edge cases
- Fuzzing defaults can be tuned via `foundry.toml` (e.g., `fuzz_runs`)

## Design Notes
- Actions are executed inside `PoolManager.unlock` via `SafeCallback.unlockCallback`, ensuring only the v4 `PoolManager` drives execution.
- Deltas: `_take` and `_settle` in `DeltaResolver` manage positive/negative credits; special constants like `OPEN_DELTA` and `CONTRACT_BALANCE` enable ergonomic flows.
- Slippage: `SlippageCheck` validates principal deltas on liquidity operations; router enforces min-out/max-in across hops and per-hop slippage checks.
- Reentrancy: transient lock via `ReentrancyLock`; `msgSender()` returns the locker for correct attribution.

## Audits & Security
- Audit drafts and reports are available in `audits/`
- Callback protection: only `PoolManager` may call `unlockCallback`
- ETH handling: `NativeWrapper` only accepts ETH from trusted sources (e.g., `WETH9` or `PoolManager`)
- Follow best practices for key management and deployment; validate inputs and slippage parameters

The license for Uniswap V4 Periphery is the GNU General Public License (GPL 2.0), see [LICENSE](https://github.com/Uniswap/v4-periphery/blob/main/LICENSE).
## Contributing
Please read the [CONTRIBUTING.md](./CONTRIBUTING.md). Run lint/tests before submitting PRs.

## License
MIT. See [LICENSE](./LICENSE).
14 changes: 14 additions & 0 deletions script/01_PoolManager.s.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
* Script: DeployPoolManager
* Purpose: Deploy Uniswap v4 PoolManager and log its address
* Usage:
* forge script script/01_PoolManager.s.sol:DeployPoolManager --rpc-url $RPC --private-key $PK --broadcast
* Notes:
* PoolManager constructor takes an owner/controller; this script passes its own address.
*/

import "forge-std/Script.sol";
import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";

import "forge-std/console2.sol";

/// @title DeployPoolManager Script
/// @notice Deploys PoolManager and prints its address
contract DeployPoolManager is Script {
/// @notice Optional pre-run setup
function setUp() public {}

/// @notice Deploy PoolManager
/// @return manager The deployed PoolManager instance
function run() public returns (IPoolManager manager) {
vm.startBroadcast();

Expand Down
15 changes: 15 additions & 0 deletions script/02_PoolModifyLiquidityTest.s.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/**
* Script: DeployPoolModifyLiquidityTest
* Purpose: Deploy core PoolModifyLiquidityTest helper against an existing PoolManager
* Usage:
* forge script script/02_PoolModifyLiquidityTest.s.sol:DeployPoolModifyLiquidityTest --rpc-url $RPC --private-key $PK --broadcast --sig "run(address)" <POOL_MANAGER>
* Notes:
* Useful for exercising liquidity flows in local/testing environments.
*/

import {Script} from "forge-std/Script.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {PoolModifyLiquidityTest} from "@uniswap/v4-core/src/test/PoolModifyLiquidityTest.sol";

import "forge-std/console2.sol";

/// @title DeployPoolModifyLiquidityTest Script
/// @notice Deploys PoolModifyLiquidityTest bound to a PoolManager
contract DeployPoolModifyLiquidityTest is Script {
/// @notice Optional pre-run setup
function setUp() public {}

/// @notice Deploy the PoolModifyLiquidityTest helper
/// @param poolManager Address of the PoolManager to bind
/// @return testModifyRouter The deployed helper instance
function run(address poolManager) public returns (PoolModifyLiquidityTest testModifyRouter) {
vm.broadcast();
testModifyRouter = new PoolModifyLiquidityTest(IPoolManager(poolManager));
Expand Down
15 changes: 15 additions & 0 deletions script/03_PoolSwapTest.s.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/**
* Script: DeployPoolSwapTest
* Purpose: Deploy core PoolSwapTest helper against an existing PoolManager
* Usage:
* forge script script/03_PoolSwapTest.s.sol:DeployPoolSwapTest --rpc-url $RPC --private-key $PK --broadcast --sig "run(address)" <POOL_MANAGER>
* Notes:
* Helps validate swap behavior in controlled environments.
*/

import {Script} from "forge-std/Script.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {PoolSwapTest} from "@uniswap/v4-core/src/test/PoolSwapTest.sol";

import "forge-std/console2.sol";

/// @title DeployPoolSwapTest Script
/// @notice Deploys PoolSwapTest bound to a PoolManager
contract DeployPoolSwapTest is Script {
/// @notice Optional pre-run setup
function setUp() public {}

/// @notice Deploy the PoolSwapTest helper
/// @param poolManager Address of the PoolManager to bind
/// @return testSwapRouter The deployed helper instance
function run(address poolManager) public returns (PoolSwapTest testSwapRouter) {
vm.broadcast();
testSwapRouter = new PoolSwapTest(IPoolManager(poolManager));
Expand Down
18 changes: 16 additions & 2 deletions script/DeployHook.s.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**
* Script: DeployHookScript
* Purpose: Mine a CREATE2 salt for a hook address that encodes required flags, then deploy the hook
* Usage:
* forge script script/DeployHook.s.sol:DeployHookScript --rpc-url $RPC --private-key $PK --broadcast
* Customize:
* Replace MockCounterHook import with your hook and set POOLMANAGER accordingly.
* Notes:
* Hook addresses must encode flags in their address per v4; HookMiner finds a matching salt.
*/

import "forge-std/Script.sol";
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {HookMiner} from "../src/utils/HookMiner.sol";

/// @dev Replace import with your own hook
// Replace import with your own hook
import {MockCounterHook} from "../test/mocks/MockCounterHook.sol";

/// @notice Mines the address and deploys the Counter.sol Hook contract
/// @title DeployHookScript
/// @notice Mines salt and deploys a hook with the desired flags via CREATE2
contract DeployHookScript is Script {
address constant CREATE2_DEPLOYER = address(0x4e59b44847b379578588920cA78FbF26c0B4956C);

/// @dev Replace with the desired PoolManager on its corresponding chain
IPoolManager constant POOLMANAGER = IPoolManager(address(0xE03A1074c86CFeDd5C142C4F04F1a1536e203543));

/// @notice Optional pre-run setup
function setUp() public {}

/// @notice Mine salt for desired flags and deploy the hook via CREATE2
function run() public {
// hook contracts must have specific flags encoded in the address
uint160 flags = uint160(
Expand Down
25 changes: 25 additions & 0 deletions script/DeployPosm.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
* Script: DeployPosmTest
* Purpose: Deploy PositionDescriptor and PositionManager (PosM) configured for a chain
* Usage:
* forge script script/DeployPosm.s.sol:DeployPosmTest --rpc-url $RPC --private-key $PK --broadcast \
* --sig "run(address,address,uint256,address,bytes32)" <POOL_MANAGER> <PERMIT2> <UNSUBSCRIBE_GAS_LIMIT> <WETH9> <NATIVE_LABEL_BYTES>
* Params:
* poolManager: v4 PoolManager address
* permit2: Permit2 contract address for token transfers
* unsubscribeGasLimit: gas allocated for unsubscribe notifications
* wrappedNative: WETH9 (or chain’s wrapped native) address
* nativeCurrencyLabelBytes: 32-byte label used by descriptor (e.g., symbol)
*/

import "forge-std/console2.sol";
import "forge-std/Script.sol";

Expand All @@ -9,9 +23,20 @@ import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"
import {Deploy, IPositionDescriptor, IPositionManager} from "../test/shared/Deploy.sol";
import {IWETH9} from "../src/interfaces/external/IWETH9.sol";

/// @title DeployPosmTest Script
/// @notice Deploys PositionDescriptor and PositionManager for a given chain
contract DeployPosmTest is Script {
/// @notice Optional pre-run setup
function setUp() public {}

/// @notice Deploy PositionDescriptor and PositionManager
/// @param poolManager PoolManager address
/// @param permit2 Permit2 address
/// @param unsubscribeGasLimit Gas limit for unsubscribe notifications
/// @param wrappedNative Wrapped native (e.g., WETH9) address
/// @param nativeCurrencyLabelBytes 32-byte label for native currency
/// @return positionDescriptor The deployed position descriptor
/// @return posm The deployed position manager
function run(
address poolManager,
address permit2,
Expand Down
13 changes: 13 additions & 0 deletions script/DeployStateView.s.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
* Script: DeployStateView
* Purpose: Deploy read-only StateView lens bound to a PoolManager
* Usage:
* forge script script/DeployStateView.s.sol:DeployStateView --rpc-url $RPC --private-key $PK --broadcast --sig "run(address)" <POOL_MANAGER>
*/

import "forge-std/console2.sol";
import "forge-std/Script.sol";

import {Deploy, IStateView} from "../test/shared/Deploy.sol";

/// @title DeployStateView Script
/// @notice Deploys StateView bound to a PoolManager
contract DeployStateView is Script {
/// @notice Optional pre-run setup
function setUp() public {}

/// @notice Deploy the StateView lens
/// @param poolManager PoolManager address
/// @return state The deployed StateView instance
function run(address poolManager) public returns (IStateView state) {
vm.startBroadcast();

Expand Down
19 changes: 18 additions & 1 deletion script/DeployV4Quoter.s.sol
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
* Script: DeployV4Quoter
* Purpose: Deploy V4Quoter bound to a PoolManager for off-chain quoting
* Usage:
* forge script script/DeployV4Quoter.s.sol:DeployV4Quoter --rpc-url $RPC --private-key $PK --broadcast
* --sig "run(address)" <POOL_MANAGER>
* Notes:
* V4Quoter performs revert-encoded simulations and is intended for off-chain use.
*/

import "forge-std/console2.sol";
import "forge-std/Script.sol";

import {Deploy, IV4Quoter} from "../test/shared/Deploy.sol";

/// @title DeployV4Quoter Script
/// @notice Deploys V4Quoter bound to a given PoolManager for off-chain quoting
/// @dev Uses Foundry broadcast; V4Quoter is intended for off-chain simulation
contract DeployV4Quoter is Script {
/// @notice Optional pre-run setup for the script
function setUp() public {}

/// @notice Deploy the V4Quoter contract
/// @param poolManager The Uniswap v4 PoolManager address to bind the quoter to
/// @return state The deployed IV4Quoter instance
function run(address poolManager) public returns (IV4Quoter state) {
vm.startBroadcast();

// forge script --broadcast --sig 'run(address)' --rpc-url <RPC_URL> --private-key <PRIV_KEY> --verify script/DeployV4Quoter.s.sol:DeployV4Quoter <POOL_MANAGER_ADDR>
// Broadcast, deploy V4Quoter bound to poolManager, and log addresses
state = Deploy.v4Quoter(poolManager, hex"00");
console2.log("V4Quoter", address(state));
console2.log("PoolManager", address(state.poolManager()));
Expand Down