2024-11-07 15:33:40 +00:00
|
|
|
// 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.
|
|
|
|
|
*/
|
2025-07-06 10:08:59 +02:00
|
|
|
function performSwap(uint256 amount, bool isBuy) public {
|
2024-12-09 23:08:24 +01:00
|
|
|
uint160 limit;
|
|
|
|
|
// Determine the swap direction
|
|
|
|
|
bool zeroForOne = isBuy ? token0isWeth : !token0isWeth;
|
2024-11-07 15:33:40 +00:00
|
|
|
|
2024-12-09 23:08:24 +01:00
|
|
|
if (isBuy) {
|
|
|
|
|
vm.prank(account);
|
|
|
|
|
weth.transfer(address(this), amount);
|
|
|
|
|
} else {
|
|
|
|
|
vm.prank(account);
|
|
|
|
|
harberg.approve(address(this), amount);
|
|
|
|
|
}
|
2024-11-07 15:33:40 +00:00
|
|
|
|
2024-12-09 23:08:24 +01:00
|
|
|
// Set the sqrtPriceLimitX96 based on the swap direction
|
2025-07-06 10:08:59 +02:00
|
|
|
// Get current price to set appropriate limits
|
|
|
|
|
(uint160 currentSqrtPrice,,,,,,) = pool.slot0();
|
2025-07-08 10:33:10 +02:00
|
|
|
|
2024-12-09 23:08:24 +01:00
|
|
|
if (zeroForOne) {
|
2025-07-06 10:08:59 +02:00
|
|
|
// Swapping token0 for token1 - price goes down
|
2024-12-09 23:08:24 +01:00
|
|
|
// sqrtPriceLimitX96 must be less than current price but greater than MIN_SQRT_RATIO
|
|
|
|
|
limit = TickMath.MIN_SQRT_RATIO + 1;
|
|
|
|
|
} else {
|
2025-07-06 10:08:59 +02:00
|
|
|
// Swapping token1 for token0 - price goes up
|
2024-12-09 23:08:24 +01:00
|
|
|
// sqrtPriceLimitX96 must be greater than current price but less than MAX_SQRT_RATIO
|
2025-07-06 10:08:59 +02:00
|
|
|
// At extreme high prices, we need to be more conservative with the limit
|
|
|
|
|
uint160 maxAllowedLimit = TickMath.MAX_SQRT_RATIO - 1;
|
|
|
|
|
if (currentSqrtPrice >= maxAllowedLimit - 1000) {
|
|
|
|
|
// If we're very close to the max, use a more conservative limit
|
|
|
|
|
limit = currentSqrtPrice + (maxAllowedLimit - currentSqrtPrice) / 2;
|
|
|
|
|
} else {
|
|
|
|
|
limit = maxAllowedLimit;
|
|
|
|
|
}
|
2024-12-09 23:08:24 +01:00
|
|
|
}
|
2024-11-07 15:33:40 +00:00
|
|
|
|
2025-07-08 10:33:10 +02:00
|
|
|
pool.swap(account, zeroForOne, int256(amount), limit, abi.encode(account, int256(amount), isBuy));
|
2024-11-07 15:33:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dev The Uniswap V3 swap callback.
|
|
|
|
|
*/
|
2025-07-08 10:33:10 +02:00
|
|
|
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external {
|
2024-11-07 15:33:40 +00:00
|
|
|
require(amount0Delta > 0 || amount1Delta > 0);
|
|
|
|
|
|
2025-07-08 10:33:10 +02:00
|
|
|
(address seller,, bool isBuy) = abi.decode(_data, (address, uint256, bool));
|
2024-11-07 15:33:40 +00:00
|
|
|
|
2025-07-08 10:33:10 +02:00
|
|
|
(, uint256 amountToPay) =
|
|
|
|
|
amount0Delta > 0 ? (!token0isWeth, uint256(amount0Delta)) : (token0isWeth, uint256(amount1Delta));
|
2024-11-07 15:33:40 +00:00
|
|
|
if (isBuy) {
|
|
|
|
|
weth.transfer(msg.sender, amountToPay);
|
|
|
|
|
} else {
|
|
|
|
|
require(harberg.transferFrom(seller, msg.sender, amountToPay), "Transfer failed");
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-09 23:08:24 +01:00
|
|
|
|
|
|
|
|
/// @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);
|
|
|
|
|
}
|
2025-07-08 10:33:10 +02:00
|
|
|
|
2024-12-09 23:08:24 +01:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-07 15:33:40 +00:00
|
|
|
}
|