Last active
December 26, 2023 22:43
-
-
Save devtooligan/3baccb521929d8fd19f17a01a9f41119 to your computer and use it in GitHub Desktop.
There are 2 critical vulnerabilities in here. Can you find them?
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
/// @notice These functions are part of a contract that | |
/// wraps a user's Uniswap V3 liquidity positions in an ERC20 token, `wrappedToken`. | |
// *** wrappedToken is a standard ERC20 token. *** | |
uint256 public protocolFee = 5; | |
function deposit( | |
IUniswapV3Pool pool, | |
int24 tickLower, | |
int24 tickUpper, | |
address wrappedToken, | |
uint256 amount0Desired, | |
uint256 amount1Desired, | |
uint256 amount0Min, | |
uint256 amount1Min, | |
IERC20 token0 | |
) external returns (uint256 shares, uint128 addedLiquidity) { | |
(uint128 existingLiquidity,,,,) = pool.positions(keccak256(abi.encodePacked(address(this), tickLower, tickUpper))); | |
// collect fees | |
// *** assume depositor has given this contract exact allowance for this fee plus exact | |
// amounts needed to mint the UniV3LP's for both tokens | |
token0.transferFrom(msg.sender, address(this), amount0Desired * protocolFee / 1000); | |
addedLiquidity = | |
_addLiquidity(pool, tickLower, tickUpper, amount0Desired, amount1Desired, amount0Min, amount1Min); | |
uint256 existingShareSupply = wrappedToken.totalSupply(); | |
if (existingShareSupply == 0) { | |
// no existing shares, bootstrap at rate 1:1 | |
shares = addedLiquidity; | |
} else { | |
shares = existingShareSupply * addedLiquidity / existingLiquidity; | |
} | |
// mint shares to sender | |
// ***assume this contract has permission to mint** | |
wrappedToken.mint(msg.sender, shares); | |
} | |
function _addLiquidity(pool, tickLower, tickUpper, amount0Desired, amount1Desired, amount0Min, amount1Min) | |
internal | |
returns (uint128 liquidity) | |
{ | |
if (amount0Desired == 0 && amount1Desired == 0) { | |
return 0; | |
} | |
{ | |
(uint160 sqrtPriceX96,,,,,,) = pool.slot0(); | |
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower); | |
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper); | |
liquidity = LiquidityAmounts.getLiquidityForAmounts( | |
sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, amount0Desired, amount1Desired | |
); | |
} | |
pool.mint(address(this), tickLower, tickUpper, liquidity, abi.encode(pool, msg.sender)); | |
} | |
function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata data) external override { | |
(address pool, address payer) = abi.decode(data, (address, address)); | |
require(msg.sender == address(pool), "Pool only"); | |
if (amount0Owed > 0) { | |
if (payer == address(this)) { | |
// pay with tokens already in the contract (for the exact input multihop case) | |
TransferHelper.safeTransfer(token0, msg.sender, amount0Owed); | |
} else { | |
TransferHelper.safeTransferFrom(IUniswapV3Pool(pool).token0(), payer, msg.sender, amount0Owed); | |
} | |
if (payer == address(this)) { | |
// pay with tokens already in the contract (for the exact input multihop case) | |
TransferHelper.safeTransfer(token1, msg.sender, amount1Owed); | |
} else { | |
TransferHelper.safeTransferFrom(IUniswapV3Pool(pool).token1(), payer, msg.sender, amount1Owed); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment