function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: true,
afterInitialize: true,
beforeAddLiquidity: false,
afterAddLiquidity: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: false,
afterDonate: false,
@> beforeSwapReturnDelta: false,
@> afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
function _beforeSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
bytes calldata
) internal override returns (bytes4, BeforeSwapDelta, uint24) {
return (
BaseHook.beforeSwap.selector,
@> BeforeSwapDeltaLibrary.ZERO_DELTA,
fee | LPFeeLibrary.OVERRIDE_FEE_FLAG
);
}
pragma solidity ^0.8.26;
import {Test} from "forge-std/Test.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {Currency} from "v4-core/types/Currency.sol";
import {SwapParams} from "v4-core/types/PoolOperation.sol";
import {BeforeSwapDelta} from "v4-core/types/BeforeSwapDelta.sol";
import {ReFiSwapRebateHook} from "../src/RebateFiHook.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract NoRevenueTest is Test {
ReFiSwapRebateHook hook;
address refiToken;
address otherToken;
PoolKey poolKey;
function setUp() public {
refiToken = address(new MockERC20());
otherToken = address(new MockERC20());
IPoolManager poolManager = IPoolManager(address(0x789));
hook = new ReFiSwapRebateHook(poolManager, refiToken);
poolKey = PoolKey({
currency0: Currency.wrap(otherToken),
currency1: Currency.wrap(refiToken),
fee: 0x800000,
tickSpacing: 60,
hooks: hook
});
deal(refiToken, address(this), 1_000_000e18);
}
function testHookNeverCollectsFees() public {
uint256 hookBalanceBefore = IERC20(refiToken).balanceOf(address(hook));
SwapParams memory params = SwapParams({
zeroForOne: false,
amountSpecified: -100_000e18,
sqrtPriceLimitX96: 0
});
(bytes4 selector, BeforeSwapDelta delta, uint24 fee) = hook.beforeSwap(
address(this),
poolKey,
params,
""
);
assertEq(BeforeSwapDelta.unwrap(delta), 0, "Hook should return ZERO_DELTA");
uint256 hookBalanceAfter = IERC20(refiToken).balanceOf(address(hook));
assertEq(hookBalanceAfter, hookBalanceBefore, "Hook balance should remain zero");
assertEq(hookBalanceAfter, 0, "Hook collected no fees");
}
function testWithdrawTokensHasNothingToWithdraw() public {
vm.expectRevert();
hook.withdrawTokens(refiToken, address(this), 1e18);
}
}
contract MockERC20 is IERC20 {
mapping(address => uint256) public override balanceOf;
function transfer(address to, uint256 amount) external override returns (bool) {
require(balanceOf[msg.sender] >= amount);
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
return true;
}
function totalSupply() external pure override returns (uint256) { return 0; }
function allowance(address, address) external pure override returns (uint256) { return 0; }
function approve(address, uint256) external pure override returns (bool) { return true; }
function transferFrom(address, address, uint256) external pure override returns (bool) { return false; }
}
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
// ... other permissions ...
beforeSwap: true,
+ beforeSwapReturnDelta: true, // Enable delta returns
// ... other permissions ...
});
}
function _beforeSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
bytes calldata
) internal override returns (bytes4, BeforeSwapDelta, uint24) {
bool isReFiBuy = _isReFiBuy(key, params.zeroForOne);
uint256 swapAmount = params.amountSpecified < 0
? uint256(-params.amountSpecified)
: uint256(params.amountSpecified);
uint24 fee;
+ BeforeSwapDelta delta = BeforeSwapDeltaLibrary.ZERO_DELTA;
if (isReFiBuy) {
fee = buyFee;
emit ReFiBought(sender, swapAmount);
} else {
fee = sellFee;
+ // Take protocol fee (e.g., 30% of the 3% sell fee = 0.9%)
+ uint256 protocolFee = (swapAmount * sellFee * 30) / 10000000;
+ // Specify delta to take protocolFee from the swap
+ delta = toBeforeSwapDelta(int128(int256(protocolFee)), 0);
emit ReFiSold(sender, swapAmount, protocolFee);
}
return (
BaseHook.beforeSwap.selector,
- BeforeSwapDeltaLibrary.ZERO_DELTA,
+ delta,
fee | LPFeeLibrary.OVERRIDE_FEE_FLAG
);
}