A type casting while executing the token swap can potentially change the semantics of the swap action in the UniswapV3 pool.
While executing the swap, the implementation of the JBXBuybackDelegate needs to cast the amount parameter from uint256
to int256
, as the swap()
function from UniswapV3 takes an int256
as the type of the amount to be swapped.
263: try pool.swap({
264: recipient: address(this),
265: zeroForOne: !_projectTokenIsZero,
266: amountSpecified: int256(_data.amount.value),
267: sqrtPriceLimitX96: _projectTokenIsZero ? TickMath.MAX_SQRT_RATIO - 1 : TickMath.MIN_SQRT_RATIO + 1,
268: data: abi.encode(_minimumReceivedFromSwap)
269: }) returns (int256 amount0, int256 amount1) {
270: // Swap succeded, take note of the amount of projectToken received (negative as it is an exact input)
271: _amountReceived = uint256(-(_projectTokenIsZero ? amount0 : amount1));
272: } catch {
273: // implies _amountReceived = 0 -> will later mint when back in didPay
274: return _amountReceived;
275: }
Line 266 casts the variable _data.amount.value
to int256
to pass it as the amountSpecified
member of swap parameters. This casting can potentially turn the value into a negative one. If the leading bit is 1 (i.e. any value greater or equal to 0x8000000000000000000000000000000000000000000000000000000000000000
), then the resulting amount will be interpreted as a negative value.
This creates a potential conflict with the swap()
function of UniswapV3, which interprets negative values differently:
/// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
This means that if the value is negative, Uniswap will interpret the value as an "exact output", changing the semantics of the swap operation.
Use a library to implement the type casting from uint256
to int256
, such as SafeCast.toInt256()
from the OpenZeppelin contracts library, which correctly checks that the given value does not overflow into a negative one while being converted.