Documentation Index
Fetch the complete documentation index at: https://tech.ramses.xyz/llms.txt
Use this file to discover all available pages before exploring further.
This example shows how to implement single-path swaps using two functions:
swapExactInputSingle - swap a fixed input amount for maximum output
swapExactOutputSingle - swap minimum input for a fixed output amount
Both use the ISwapRouter interface.
Note: The swap examples are not production ready code, and are implemented in a simplistic manner for the purpose of learning.
Set Up the Contract
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
contract SwapExamples {
ISwapRouter public immutable swapRouter;
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
uint24 public constant poolFee = 3000;
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
The caller must approve the contract to withdraw tokens. Since the contract itself holds the tokens after safeTransferFrom, it must also approve the router.
/// @notice swapExactInputSingle swaps a fixed amount of DAI for a maximum possible amount of WETH9
/// using the DAI/WETH9 0.3% pool by calling `exactInputSingle` in the swap router.
/// @dev The calling address must approve this contract to spend at least `amountIn` worth of its DAI for this function to succeed.
/// @param amountIn The exact amount of DAI that will be swapped for WETH9.
/// @return amountOut The amount of WETH9 received.
function swapExactInputSingle(uint256 amountIn) external returns (uint256 amountOut) {
// msg.sender must approve this contract
// Transfer the specified amount of DAI to this contract.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountIn);
// Approve the router to spend DAI.
TransferHelper.safeApprove(DAI, address(swapRouter), amountIn);
Parameters for ExactInputSingleParams (reference):
-
fee - pool fee tier, used to determine the correct pool contract
-
amountOutMinimum - set to zero here, but in production use an oracle or the SDK to calculate this. Without slippage protection you are vulnerable to sandwich attacks.
-
sqrtPriceLimitX96 - set to zero (inactive). Can be used to cap the price impact of a swap.
WARNING: A non-zero sqrtPriceLimitX96 can cause fewer tokens than amountIn to be swapped. Any contract using this must refund unswapped tokens.
Call the function
// Naively set amountOutMinimum to 0. In production, use an oracle or other data source to choose a safer value for amountOutMinimum.
// We also set the sqrtPriceLimitx96 to be 0 to ensure we swap our exact input amount.
ISwapRouter.ExactInputSingleParams memory params =
ISwapRouter.ExactInputSingleParams({
tokenIn: DAI,
tokenOut: WETH9,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
// The call to `exactInputSingle` executes the swap.
amountOut = swapRouter.exactInputSingle(params);
}
Exact Output Swaps
Swaps a minimum possible amount of the input token for a fixed amount of the outbound token. Any leftover input is refunded to the caller.
Call the function
/// @notice swapExactOutputSingle swaps a minimum possible amount of DAI for a fixed amount of WETH.
/// @dev The calling address must approve this contract to spend its DAI for this function to succeed. As the amount of input DAI is variable,
/// the calling address will need to approve for a slightly higher amount, anticipating some variance.
/// @param amountOut The exact amount of WETH9 to receive from the swap.
/// @param amountInMaximum The amount of DAI we are willing to spend to receive the specified amount of WETH9.
/// @return amountIn The amount of DAI actually spent in the swap.
function swapExactOutputSingle(uint256 amountOut, uint256 amountInMaximum) external returns (uint256 amountIn) {
// Transfer the specified amount of DAI to this contract.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountInMaximum);
// Approve the router to spend the specified `amountInMaximum` of DAI.
// In production, you should choose the maximum amount to spend based on oracles or other data sources to achieve a better swap.
TransferHelper.safeApprove(DAI, address(swapRouter), amountInMaximum);
ISwapRouter.ExactOutputSingleParams memory params =
ISwapRouter.ExactOutputSingleParams({
tokenIn: DAI,
tokenOut: WETH9,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountOut: amountOut,
amountInMaximum: amountInMaximum,
sqrtPriceLimitX96: 0
});
// Executes the swap returning the amountIn needed to spend to receive the desired amountOut.
amountIn = swapRouter.exactOutputSingle(params);
// For exact output swaps, the amountInMaximum may not have all been spent.
// If the actual amount spent (amountIn) is less than the specified maximum amount, we must refund the msg.sender and approve the swapRouter to spend 0.
if (amountIn < amountInMaximum) {
TransferHelper.safeApprove(DAI, address(swapRouter), 0);
TransferHelper.safeTransfer(DAI, msg.sender, amountInMaximum - amountIn);
}
}