Swapping Tokens in Uniswap v3
Now that we understand how pools are created and liquidity is provided, letās look at how swaps work in Uniswap v3.
Understanding Swaps in Uniswap v3
When you swap tokens in Uniswap v3:
- The swap moves along the bonding curve, potentially crossing multiple tick boundaries
- The price changes according to the constant product formula within each price range
- The swap might use liquidity from multiple providers who have positions in different price ranges
- The swap incurs a fee based on the poolās fee tier (0.05%, 0.3%, or 1%)
The SwapRouter Contract
Uniswap v3 uses a contract called SwapRouter
to handle swaps. This contract:
- Routes your swap through the appropriate pool(s)
- Handles the complex logic of crossing tick boundaries
- Ensures you receive at least your specified minimum output amount
Code Example: Performing a Swap
Hereās how to perform a swap in Uniswap v3:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract TokenSwapper {
ISwapRouter public swapRouter;
constructor(address _swapRouter) {
swapRouter = ISwapRouter(_swapRouter);
}
function swapExactInputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountIn,
uint256 amountOutMinimum
) external returns (uint256 amountOut) {
// Approve the router to spend the token
IERC20(tokenIn).approve(address(swapRouter), amountIn);
// Create the params for the swap
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: fee,
recipient: address(this),
deadline: block.timestamp + 15 minutes,
amountIn: amountIn,
amountOutMinimum: amountOutMinimum,
sqrtPriceLimitX96: 0 // No price limit
});
// Execute the swap
amountOut = swapRouter.exactInputSingle(params);
return amountOut;
}
function swapExactOutputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountOut,
uint256 amountInMaximum
) external returns (uint256 amountIn) {
// Approve the router to spend the token
IERC20(tokenIn).approve(address(swapRouter), amountInMaximum);
// Create the params for the swap
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: fee,
recipient: address(this),
deadline: block.timestamp + 15 minutes,
amountOut: amountOut,
amountInMaximum: amountInMaximum,
sqrtPriceLimitX96: 0 // No price limit
});
// Execute the swap
amountIn = swapRouter.exactOutputSingle(params);
// Refund any unused input tokens
if (amountIn < amountInMaximum) {
IERC20(tokenIn).approve(address(swapRouter), 0);
}
return amountIn;
}
}
Types of Swaps
Uniswap v3 supports several types of swaps:
1. Exact Input Single
You specify exactly how much of token A you want to swap, and get at least a minimum amount of token B.
2. Exact Output Single
You specify exactly how much of token B you want to receive, and spend at most a maximum amount of token A.
3. Exact Input (Multi-hop)
Swap a fixed amount of one token for another through multiple pools (e.g., A ā B ā C).
4. Exact Output (Multi-hop)
Receive a fixed amount of one token by spending another through multiple pools.
Example: Swapping ETH for USDC
Letās say we want to swap 1 ETH for at least 1,950 USDC:
// Addresses for Ethereum mainnet
address WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
uint24 fee = 3000; // 0.3%
// Amount to swap
uint256 amountIn = 1 ether;
uint256 amountOutMinimum = 1950 * 10**6; // 1,950 USDC
// Execute the swap
uint256 amountOut = swapExactInputSingle(
WETH,
USDC,
fee,
amountIn,
amountOutMinimum
);
Understanding Price Impact and Slippage
When performing swaps, two important concepts to understand are:
Price Impact
The effect your trade has on the market price. Larger trades relative to pool size have higher price impact.
Slippage
The difference between the expected price and the executed price. To protect against slippage, you set:
amountOutMinimum
for exact input swapsamountInMaximum
for exact output swaps
What Happens During a Swap?
During a swap, several things happen:
- The pool calculates the output amount based on the constant product formula
- The price moves along the bonding curve
- If the price crosses tick boundaries, liquidity is added or removed as needed
- Fees are collected and distributed to liquidity providers
- The tokens are transferred between the user and the pool
In the next section, weāll explore the differences between Uniswap v3 and v4, and how v4 improves upon v3ās design.