NoOp Hooks are Uniswap V4 hooks offering an alternative to the conventional AMM swap formula (x * y = k), allowing developers to implement custom swap logic tailored to specific needs. These hooks provide a flexible framework where default operations can be bypassed. This capability enables developers to create bespoke trading mechanisms without altering the underlying protocol, ensuring seamless integration with existing systems. By using NoOp Hooks, developers can either extend functionality or maintain existing frameworks by simply acting as placeholders when no additional logic is required.
NoOp Implementation
Two things is need for hook to be considered NoOp - to return delta amount from respective function and to have enabled permission. For example (as a reference code was taken from this ):
contract RugPullSwap is BaseHook {
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return
Hooks.Permissions({
beforeInitialize: false,
afterInitialize: false,
beforeAddLiquidity: false,
beforeRemoveLiquidity: false,
afterAddLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true, // hook function permission
afterSwap: false,
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: true, // permission to return delta amount
afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
function beforeSwap(
address,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
bytes calldata
) external override returns (bytes4, BeforeSwapDelta, uint24) {
// if swap amount less then 1 ether - rekt it!
if (params.amountSpecified < 1 ether) {
Currency input = params.zeroForOne ? key.currency0 : key.currency1;
// take the amount and omit returns to user
input.take(
poolManager,
address(this),
uint256(-params.amountSpecified),
false
);
return (
BaseHook.beforeSwap.selector,
// returns BeforeSwapDelta, where first value is for taking
// all the amount and second for returning 0 back to user
toBeforeSwapDelta(int128(-params.amountSpecified), 0),
0
);
}
// if amount is more then 1 ether - swap noramlly by returning zero delta
return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
}
}
NoOp Usecases
contract BtcAccHook is BaseHook {
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
// returns permissions for afterSwap()
}
function afterSwap(
address sender,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
BalanceDelta delta,
bytes calldata
) external override onlyPoolManager returns (bytes4, int128) {
// ... security checks
// determine user
address user = IValidRouter(sender).msgSender();
// Calculate fee based on the swap amount
bool specifiedTokenIs0 = (params.amountSpecified < 0 == params.zeroForOne);
(Currency feeCurrency, int128 swapAmount) =
(specifiedTokenIs0) ? (key.currency1, delta.amount1()) : (key.currency0, delta.amount0());
Currency otherCurrency = (specifiedTokenIs0) ? key.currency0 : key.currency1;
if (swapAmount < 0) swapAmount = -swapAmount;
// Get the user's custom fee rate or default rate
uint128 userFeeRate = getWalletSwapFee(user);
uint256 feeAmount = uint128(swapAmount) * userFeeRate / TOTAL_BIPS;
// take fee amount from pool manager
manager.take(feeCurrency, address(this), feeAmount);
address feeToken = Currency.unwrap(feeCurrency);
address otherToken = Currency.unwrap(otherCurrency);
// Handle fee token deposit or conversion
if (isAllowedBaseToken[feeToken]) {
_depositBase(user, feeAmount, feeToken, otherToken);
} else {
uint256 baseAmount = _swapToBase(key, feeAmount);
address baseToken =
Currency.unwrap(isAllowedBaseToken[Currency.unwrap(key.currency0)] ? key.currency0 : key.currency1);
_depositBase(user, baseAmount, baseToken, feeToken);
}
// returns delta to account
return (BaseHook.afterSwap.selector, feeAmount.toInt128());
}
}
Custom curve hook:
In certain scenarios, it may be more profitable to use an alternative pool mechanism that does not rely on the constant product AMM formula (x * y = k). For example, a pool could instead operate using a formula like x + y = k, which may better suit specific market conditions or trading strategies.
contract CustomCurveHook is BaseHook {
int256 private constant K = 1000000;
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
// before swap permissions returned
}
function beforeSwap(
address sender,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
bytes calldata hookData
) external onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) {
return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(params.amountSpecified, 1000000 - params.amountSpecified), 0);
}
}
Conclusion
Developing an accumulation hook (reference from TokenWorks ):
The main idea of such hook is to have a layer between BTC assets and Uniswap V4 ecosystem and deposit user fees from swap into BTC holdings.
In conclusion, NoOp Hooks in Uniswap V4 allow developers to customize swap logic and it`s parameters, offering flexibility for unique trading strategies and easy integration with existing systems. If you want to have broader conception about NoOp hooks - please check HookRank for all hooks labeled NoOp.