Created
March 26, 2022 00:01
-
-
Save pyk/292db719198d6d1ae5ca4ecdad9a0e6e to your computer and use it in GitHub Desktop.
Uniswap V3 getAmountsIn getAmountsOut via Flash Swap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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