// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; import {TickMath} from "@aperture/uni-v3-lib/TickMath.sol"; import "../../src/interfaces/IWETH9.sol"; import {Harberg} from "../../src/Harberg.sol"; /** * @title UniswapTestBase * @dev Base contract for Uniswap V3 testing, providing reusable swap logic. */ abstract contract UniswapTestBase is Test { address account = makeAddr("alice"); IUniswapV3Pool public pool; IWETH9 public weth; Harberg public harberg; bool public token0isWeth; /** * @dev Performs a swap in the Uniswap V3 pool. * @param amount The amount to swap. * @param isBuy True if buying WETH, false if selling. */ function performSwap(uint256 amount, bool isBuy) internal { uint160 limit; // Determine the swap direction bool zeroForOne = isBuy ? token0isWeth : !token0isWeth; if (isBuy) { vm.prank(account); weth.transfer(address(this), amount); } else { vm.prank(account); harberg.approve(address(this), amount); } // Set the sqrtPriceLimitX96 based on the swap direction if (zeroForOne) { // Swapping token0 for token1 // sqrtPriceLimitX96 must be less than current price but greater than MIN_SQRT_RATIO limit = TickMath.MIN_SQRT_RATIO + 1; } else { // Swapping token1 for token0 // sqrtPriceLimitX96 must be greater than current price but less than MAX_SQRT_RATIO limit = TickMath.MAX_SQRT_RATIO - 1; } pool.swap( account, zeroForOne, int256(amount), limit, abi.encode(account, int256(amount), isBuy) ); } /** * @dev The Uniswap V3 swap callback. */ function uniswapV3SwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata _data ) external { require(amount0Delta > 0 || amount1Delta > 0); (address seller, , bool isBuy) = abi.decode(_data, (address, uint256, bool)); (, uint256 amountToPay) = amount0Delta > 0 ? (!token0isWeth, uint256(amount0Delta)) : (token0isWeth, uint256(amount1Delta)); if (isBuy) { weth.transfer(msg.sender, amountToPay); } else { require(harberg.transferFrom(seller, msg.sender, amountToPay), "Transfer failed"); } } /// @notice Callback function that Uniswap V3 calls for liquidity actions requiring minting or burning of tokens. /// @param amount0Owed The amount of token0 owed for the liquidity provision. /// @param amount1Owed The amount of token1 owed for the liquidity provision. /// @dev This function mints Harberg tokens as needed and handles WETH deposits for ETH conversions during liquidity interactions. function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata) external { // CallbackValidation.verifyCallback(factory, poolKey); // take care of harb uint256 harbPulled = token0isWeth ? amount1Owed : amount0Owed; if (harbPulled > 0) { harberg.mint(harbPulled); harberg.transfer(msg.sender, harbPulled); } // pack ETH uint256 ethOwed = token0isWeth ? amount0Owed : amount1Owed; if (weth.balanceOf(address(this)) < ethOwed) { weth.deposit{value: address(this).balance}(); } if (ethOwed > 0) { weth.transfer(msg.sender, amount1Owed); } } }