Created
April 29, 2023 13:58
-
-
Save bahurum/96a5a6c2082b81712392924cd2e673fd to your computer and use it in GitHub Desktop.
PoC for issue Low 2 - Possible loss of funds with price limited swaps through `DepositHelper`
This file contains hidden or 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: UNLICENSED | |
| pragma solidity ^0.8.0; | |
| import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; | |
| import { IERC721 } from '@openzeppelin/contracts/token/ERC721/IERC721.sol'; | |
| import { ABaseExit10Test } from './ABaseExit10.t.sol'; | |
| import { Exit10 } from '../src/Exit10.sol'; | |
| import { DepositHelper } from '../src/DepositHelper.sol'; | |
| import { IUniswapV3Router } from '../src/interfaces/IUniswapV3Router.sol'; | |
| import '../src/UniswapBase.sol'; | |
| // needs v3-core@0.8 | |
| import '../lib/v3-core/contracts/libraries/TickMath.sol'; | |
| import '../lib/v3-core/contracts/libraries/FullMath.sol'; | |
| import '../lib/v3-periphery/contracts/libraries/LiquidityAmounts.sol'; | |
| import '@openzeppelin/contracts/utils/math/Math.sol'; | |
| import '../lib/forge-std/src/console.sol'; | |
| contract DepositHelperTest is ABaseExit10Test { | |
| DepositHelper depositHelper; | |
| using FullMath for uint256; | |
| function setUp() public override { | |
| super.setUp(); | |
| depositHelper = new DepositHelper(address(UNISWAP_V3_ROUTER), address(exit10), weth); | |
| } | |
| function testPriceLimitSwaps() public { | |
| _skipBootstrap(); | |
| uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower); | |
| uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper); | |
| (uint160 sqrtRatioX96, int24 tick, , , , , ) = IUniswapV3Pool(vm.envAddress('POOL')).slot0(); | |
| console.log("initial USDC balance of DepositHelper:", ERC20(usdc).balanceOf(address(depositHelper))); | |
| console.log("sqrtRatioX96:", sqrtRatioX96); | |
| (uint256 amount0, uint256 amount1) = LiquidityAmounts.getAmountsForLiquidity( | |
| sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, 1e15); // amounts to obtain 1e15 liquidity | |
| uint256 swapAmount0 = convert1ToToken0(sqrtRatioX96, amount1, 6); // usdc amount to swap | |
| ERC20(usdc).transfer(alice, amount0 + swapAmount0); // give usdc to alice | |
| IUniswapV3Router.ExactInputSingleParams memory swapParams = _getSwapParams( | |
| usdc, weth, swapAmount0); | |
| swapParams.sqrtPriceLimitX96 = TickMath.getSqrtRatioAtTick(tick - 1); | |
| console.log("alice sends", amount0 + swapAmount0, "USDC"); | |
| console.log("and swaps", swapAmount0, "USDC for ETH"); | |
| console.log("swapParams.sqrtPriceLimitX96 set to:", swapParams.sqrtPriceLimitX96); | |
| vm.startPrank(alice); | |
| ERC20(usdc).approve(address(depositHelper), amount0 + swapAmount0); | |
| depositHelper.swapAndCreateBond(amount0 + swapAmount0, 0, swapParams); | |
| vm.stopPrank(); | |
| uint256 usdcLeft = ERC20(usdc).balanceOf(address(depositHelper)); | |
| console.log("Only", swapAmount0 - usdcLeft, "USDC has been swapped and used to mint bonds"); | |
| console.log("USDC Left into DepositHelper:", usdcLeft); | |
| } | |
| function _getSwapParams( | |
| address tokenIn, | |
| address tokenOut, | |
| uint256 amountIn | |
| ) internal view returns (IUniswapV3Router.ExactInputSingleParams memory) { | |
| return | |
| IUniswapV3Router.ExactInputSingleParams({ | |
| tokenIn: tokenIn, | |
| tokenOut: tokenOut, | |
| fee: 500, | |
| recipient: address(depositHelper), | |
| deadline: block.timestamp, | |
| amountIn: amountIn, | |
| amountOutMinimum: 0, | |
| sqrtPriceLimitX96: 0 | |
| }); | |
| } | |
| function sqrtPriceX96ToUint(uint160 _sqrtPriceX96, uint8 decimalsToken0) internal pure returns (uint256) { | |
| uint256 numerator1 = uint256(_sqrtPriceX96) * uint256(_sqrtPriceX96); | |
| uint256 numerator2 = 10 ** decimalsToken0; | |
| return FullMath.mulDiv(numerator1, numerator2, 1 << 192); | |
| } | |
| function convert0ToToken1( | |
| uint160 _sqrtPriceX96, | |
| uint256 amount0, | |
| uint8 decimalsToken0 | |
| ) internal pure returns (uint256 amount0ConvertedToToken1) { | |
| uint256 price = sqrtPriceX96ToUint(_sqrtPriceX96, decimalsToken0); | |
| amount0ConvertedToToken1 = amount0*(price)/(10 ** decimalsToken0); | |
| } | |
| function convert1ToToken0( | |
| uint160 _sqrtPriceX96, | |
| uint256 amount1, | |
| uint8 decimalsToken0 | |
| ) internal pure returns (uint256 amount1ConvertedToToken0) { | |
| uint256 price = sqrtPriceX96ToUint(_sqrtPriceX96, decimalsToken0); | |
| if (price == 0) return 0; | |
| amount1ConvertedToToken0 = amount1*(10 ** decimalsToken0)/(price); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment