The StopLoss contract leverages Uniswap V4’s custom user hook functionality to implement a stop loss feature. This hook automatically sells tokens when the price in a specified pool meets the user-defined stop-loss conditions, thereby preventing further losses. The functionality of this hook is summarized as follows:
Users create a stop-loss order by executing the placeStopLoss() function.
After each swap, the afterSwap function compares the previous and current ticks, executing the order if the conditions are met.
Let's take a look at the afterSwap function in this hook.
functionafterSwap( address, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta, bytes calldata ) externaloverridereturns (bytes4, int128) { int24 prevTick = tickLowerLasts[key.toId()]; (, int24 tick,,) =poolManager.getSlot0(key.toId()); int24 currentTick =getTickLower(tick,key.tickSpacing); tick = prevTick; int256 swapAmounts;// fill stop losses in the opposite direction of the swap// avoids abuse/attack vectors bool stopLossZeroForOne =!params.zeroForOne;// TODO: test for off by one because of inequalityif (prevTick < currentTick) {for (; tick < currentTick;) { swapAmounts = stopLossPositions[key.toId()][tick][stopLossZeroForOne];if (swapAmounts >0) {fillStopLoss(key, tick, stopLossZeroForOne, swapAmounts); } unchecked { tick +=key.tickSpacing; } } } else {for (; currentTick < tick;) { swapAmounts = stopLossPositions[key.toId()][tick][stopLossZeroForOne];if (swapAmounts >0) {fillStopLoss(key, tick, stopLossZeroForOne, swapAmounts); } unchecked { tick -=key.tickSpacing; } } }return (StopLoss.afterSwap.selector,0); }
The function lacks a modifier and any additional ACL mechanism. Through this function, the fillStopLoss() function can be called separately, allowing storage values to be altered. This may lead to unexpected behavior in the hook, and appropriate measures are necessary. Herbicide can detect these risks and alert users accordingly.
ArrakisHook
This ArrakisHook stores information about the pool key in beforeInitialize right before initialization, enabling it to manage liquidity through ERC1155.
This contract is a hook implemented to initialize information about the pool key in beforeInitialize. However, this hook does not implement any access control for beforeInitialize, nor does it take any measures regarding the number of initializations. Since the pool key is initialized in beforeInitialize, if a new initialization is performed on this hook, all existing hook assets will be locked.
Herbicide detects the storage values of such cases to ensure that users can use the hook safely.