Skip to content

Instantly share code, notes, and snippets.

@devtooligan
Last active December 26, 2023 22:43
Show Gist options
  • Save devtooligan/3baccb521929d8fd19f17a01a9f41119 to your computer and use it in GitHub Desktop.
Save devtooligan/3baccb521929d8fd19f17a01a9f41119 to your computer and use it in GitHub Desktop.
There are 2 critical vulnerabilities in here. Can you find them?
/// @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