Skip to content

Instantly share code, notes, and snippets.

@pyk
Created March 26, 2022 00:01
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save pyk/292db719198d6d1ae5ca4ecdad9a0e6e to your computer and use it in GitHub Desktop.
Uniswap V3 getAmountsIn getAmountsOut via Flash Swap
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;
pragma experimental ABIEncoderV2;
import "lib/ds-test/src/test.sol";
import { IERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import { gohm, usdc, weth, uniswapV2Router } from "chain/Tokens.sol";
import { IUniswapV2Router02 } from "../../interfaces/IUniswapV2Router02.sol";
import { IUniswapV2Factory } from "../../interfaces/IUniswapV2Factory.sol";
import { IUniswapV2Pair } from "../../interfaces/IUniswapV2Pair.sol";
import { IUniswapV3Pool } from "../../interfaces/IUniswapV3Pool.sol";
contract UniswapPreviewTest is DSTest {
using SafeERC20 for IERC20;
enum SwapType { PreviewSwapETHForExactTokens, PreviewSwapExactTokensForETH, PreviewSwapTokensForExactETH }
/// Given exact amountOut output tokens, returns the amountIn of WETH
function testPreviewSwapETHForExactTokensUniswapV3() public {
uint256 gohmAmount = 1 ether;
// Initialize pool
address poolAddress = 0xcF7e21b96a7DAe8e1663b5A266FD812CBE973E70; // gOHM/ETH 1% Pool on Uniswap V3
IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);
address token0 = pool.token0();
address token1 = pool.token1();
// true: token0 -> token1
// false: token1 -> token0
uint8 ethTokenNumber = gohm == token0 ? 1 : 0;
bool zeroForOne = gohm == token1 ? true : false;
// Exact input: positive
// Exact output: negative
int256 amountSpecified = -1 * int256(gohmAmount);
uint160 sqrtPriceLimitX96 = (zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341);
// Perform swap
bytes memory data = abi.encode(SwapType.PreviewSwapETHForExactTokens, ethTokenNumber);
uint256 wethAmount;
try pool.swap(address(this), zeroForOne, amountSpecified, sqrtPriceLimitX96, data) {} catch Error(string memory revertData) {
wethAmount = abi.decode(bytes(revertData), (uint256));
}
emit log_named_uint("wethAmount", wethAmount);
assertTrue(false);
}
/// Given exact amountIn of tokens, returns the amountOut of WETH
function testPreviewSwapExactTokensForETHUniswapV3() public {
uint256 gohmAmount = 1 ether;
// Initialize pool
address poolAddress = 0xcF7e21b96a7DAe8e1663b5A266FD812CBE973E70; // gOHM/ETH 1% Pool on Uniswap V3
IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);
address token0 = pool.token0();
address token1 = pool.token1();
// true: token0 -> token1
// false: token1 -> token0
bool zeroForOne = gohm == token0 ? true : false;
uint8 ethTokenNumber = gohm == token0 ? 1 : 0;
// Exact input: positive
// Exact output: negative
int256 amountSpecified = int256(gohmAmount);
uint160 sqrtPriceLimitX96 = (zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341);
// Perform swap
bytes memory data = abi.encode(SwapType.PreviewSwapExactTokensForETH, ethTokenNumber);
uint256 wethAmount;
try pool.swap(address(this), zeroForOne, amountSpecified, sqrtPriceLimitX96, data) {} catch Error(string memory revertData) {
wethAmount = abi.decode(bytes(revertData), (uint256));
}
emit log_named_uint("wethAmount", wethAmount);
assertTrue(false);
}
/// Given exact amountOut of ETH, returns the amountIn of tokens
function testPreviewSwapTokensForExactETH() public {
uint256 wethAmount = 1 ether;
// Initialize pool
address poolAddress = 0xcF7e21b96a7DAe8e1663b5A266FD812CBE973E70; // gOHM/ETH 1% Pool on Uniswap V3
IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);
address token0 = pool.token0();
address token1 = pool.token1();
// true: token0 -> token1
// false: token1 -> token0
bool zeroForOne = gohm == token0 ? true : false;
uint8 tokenNumber = gohm == token0 ? 0 : 1;
// Exact input: positive
// Exact output: negative
int256 amountSpecified = -1 * int256(wethAmount);
uint160 sqrtPriceLimitX96 = (zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341);
// Perform swap
bytes memory data = abi.encode(SwapType.PreviewSwapTokensForExactETH, tokenNumber);
uint256 amountIn;
try pool.swap(address(this), zeroForOne, amountSpecified, sqrtPriceLimitX96, data) {} catch Error(string memory revertData) {
amountIn = abi.decode(bytes(revertData), (uint256));
}
emit log_named_uint("amountIn", amountIn);
assertTrue(false);
}
function uniswapV3SwapCallback(int256 _amount0Delta, int256 _amount1Delta, bytes memory _data) external {
(SwapType swapType, uint8 tokenNumber) = abi.decode(_data, (SwapType, uint8));
if (swapType == SwapType.PreviewSwapETHForExactTokens || swapType == SwapType.PreviewSwapTokensForExactETH) {
if (tokenNumber == 0) {
revert(string(abi.encode(uint256(_amount0Delta))));
} else {
revert(string(abi.encode(uint256(_amount1Delta))));
}
}
// WETH amount is negative, coz ETH was sent by the pool
if (swapType == SwapType.PreviewSwapExactTokensForETH) {
if (tokenNumber == 0) {
revert(string(abi.encode(uint256(-1 * _amount0Delta))));
} else {
revert(string(abi.encode(uint256(-1 * _amount1Delta))));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment