pragma solidity ^0.8.26;
import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol";
import {ReFiSwapRebateHook} from "../src/RebateFiHook.sol";
import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol";
import {Currency} from "v4-core/types/Currency.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {Hooks} from "v4-core/libraries/Hooks.sol";
import {HookMiner} from "v4-periphery/src/utils/HookMiner.sol";
import {LPFeeLibrary} from "v4-core/libraries/LPFeeLibrary.sol";
import {CustomRevert} from "v4-core/libraries/CustomRevert.sol";
* @title PoC: Initialization DoS
* @notice Explains and demonstrates the Denial of Service vulnerability during pool initialization.
*
* The `RebateFiHook` intends to ensure the ReFi token is part of the pool. However, `_beforeInitialize`
* incorrectly checks `key.currency1` twice, ignoring `key.currency0`. If ReFi is the lower address
* (currency0), the check fails and reverts, preventing pool creation.
*
* Mitigation:
* Modify `_beforeInitialize` to check `key.currency0` as well.
* if (Currency.unwrap(key.currency0) != ReFi && Currency.unwrap(key.currency1) != ReFi) ...
*/
contract InitializationDoS is Deployers {
MockERC20 reFi;
MockERC20 other;
ReFiSwapRebateHook hook;
function setUp() public {
deployFreshManager();
reFi = new MockERC20("ReFi", "REFI", 18);
do { other=new MockERC20("Other", "OTHR", 18); } while (address(other) <= address(reFi));
(, bytes32 salt) = HookMiner.find(
address(this),
uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG),
type(ReFiSwapRebateHook).creationCode,
abi.encode(manager, address(reFi))
);
hook = new ReFiSwapRebateHook{salt: salt}(manager, address(reFi));
}
function test_WhenReFiIsCurrency0() public {
try manager.initialize(PoolKey(Currency.wrap(address(reFi)), Currency.wrap(address(other)), LPFeeLibrary.DYNAMIC_FEE_FLAG, 60, hook), 79228162514264337593543950336) {
fail("Should revert");
} catch (bytes memory err) {
bytes4 sel; assembly { sel := mload(add(err, 32)) }
assertEq(sel, CustomRevert.WrappedError.selector);
bytes memory data = new bytes(err.length - 4);
for (uint i = 0; i < data.length; i++) data[i] = err[i + 4];
(,, bytes memory reason,) = abi.decode(data, (address, bytes4, bytes, bytes));
assembly { sel := mload(add(reason, 32)) }
assertEq(sel, ReFiSwapRebateHook.ReFiNotInPool.selector);
}
}
}