Building a Treasury Management Hook for Uniswap V4 — Part 1: Contract Architecture and Core Implementation

Contract Structure and Dependencies

The TreasuryManagementHook contract is built on Uniswap V4’s hook system, leveraging several key components for efficient treasury management:

contract TreasuryManagementHook is BaseHook {
using PoolIdLibrary for PoolKey;
using CurrencyLibrary for Currency;

Key Dependencies Analysis

BaseHook Inheritance: Provides the foundational hook infrastructure, handling the complex lifecycle management and integration with Uniswap V4’s PoolManager. This eliminates the need to implement low-level hook mechanics manually.

Library Usage:

PoolIdLibrary: Enables type-safe pool identification through the toId() method, ensuring consistent pool referencing across the systemCurrencyLibrary: Handles currency operations and type conversions, abstracting away the complexity of different token standards

Core Type Imports: The contract utilizes essential Uniswap V4 types like BalanceDelta, PoolKey, and BeforeSwapDelta for handling swap data and pool configuration.

State Variables and Storage Optimization

The contract’s storage design prioritizes gas efficiency while maintaining treasury functionality:

Treasury Control Variables

address public treasury; // Treasury controller address
uint24 public treasuryFeeRate; // Fee rate in basis points
uint24 public constant MAX_FEE_RATE = 1000; // 10% maximum fee cap
uint256 public constant BASIS_POINTS = 10000; // Precision denominator

Design Rationale:

uint24 for fee rates provides sufficient range (0- 16,777,215) while using minimal storage slotsThe basis points system (10,000) enables precise fee calculations with 0.01% precisionHard-coded maximum fee rate of 10% prevents excessive fee collection that could harm trading

Pool and Fee Tracking

mapping(PoolId => bool) public isPoolManaged; // Pool participation tracking
mapping(Currency => uint256) public accumulatedFees; // Fee balances by token

Storage Efficiency:

Separate mappings optimize gas costs for different query patternsisPoolManaged enables quick pool status checks during swapsaccumulatedFees tracks balances per token type for flexible withdrawals

Constructor Implementation and Validation

constructor(
IPoolManager _poolManager,
address _treasury,
uint24 _treasuryFeeRate
) BaseHook(_poolManager) {
if (_treasury == address(0)) revert InvalidTreasuryAddress();
if (_treasuryFeeRate > MAX_FEE_RATE) revert FeeRateTooHigh();

treasury = _treasury;
treasuryFeeRate = _treasuryFeeRate;
}

Critical Validation Logic

Zero Address Protection: Prevents deployment with an invalid treasury address, which would lock the contract’s functionality permanently.

Fee Rate Validation: Ensures the initial fee rate complies with the 10% maximum limit, preventing deployment with excessive fees.

Early Validation Strategy: Performing all validation in the constructor prevents invalid contract states and saves gas on failed deployments.

Hook Permissions Configuration

The hook’s capabilities are precisely defined through the permissions structure:

function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: false,
afterInitialize: true, // Pool registration
beforeAddLiquidity: false,
afterAddLiquidity: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true, // Pool validation
afterSwap: true, // Fee collection
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: false,
afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}

Permission Strategy

Minimal Permissions Approach: Only enables necessary hooks to reduce gas costs and minimize attack surface. The contract specifically needs:

afterInitialize: Automatically registers new pools for fee collectionbeforeSwap: Validates whether the pool should be processed for feesafterSwap: Executes the actual fee collection logic

Gas Optimization: By avoiding unnecessary hook permissions, the contract reduces the overhead of hook processing for operations that don’t require treasury intervention.

Access Control Implementation

The contract implements a straightforward but effective access control system:

function setTreasury(address _newTreasury) external {
if (msg.sender != treasury) revert OnlyTreasuryAllowed();
if (_newTreasury == address(0)) revert InvalidTreasuryAddress();

address oldTreasury = treasury;
treasury = _newTreasury;

emit TreasuryAddressChanged(oldTreasury, _newTreasury);
}

Security Features

Direct Address Comparison: Uses simple msg.sender comparison for gas-efficient access control, avoiding complex role-based systems that could introduce vulnerabilities.

Input Validation: Prevents transition to invalid states by validating the new treasury address before making changes.

Event Emission: Provides complete transparency for governance changes, enabling off-chain monitoring and audit trails.

Atomic Updates: All changes occur within a single transaction, ensuring state consistency.

Error Handling Strategy

The contract defines custom errors for gas-efficient and user-friendly error handling:

error InvalidTreasuryAddress();
error FeeRateTooHigh();
error OnlyTreasuryAllowed();
error InsufficientFees();

Benefits of Custom Errors

Gas Efficiency: Custom errors consume significantly less gas than require statements with string messages, especially important for functions called frequently during swaps.

Type Safety: Enables specific error handling in client applications and testing frameworks.

Clear Communication: Descriptive names improve debugging experience and help developers understand failure conditions.

Treasury Configuration Management

The contract provides secure methods to update treasury parameters during operation:

function setTreasuryFeeRate(uint24 _newFeeRate) external {
if (msg.sender != treasury) revert OnlyTreasuryAllowed();
if (_newFeeRate > MAX_FEE_RATE) revert FeeRateTooHigh();

uint24 oldRate = treasuryFeeRate;
treasuryFeeRate = _newFeeRate;

emit TreasuryFeeRateChanged(oldRate, _newFeeRate);
}

Configuration Security

Access Control: Only the treasury address can modify fee rates, preventing unauthorized changes that could affect trading economics.

Rate Limiting: Enforces the maximum 10% fee rate even for runtime changes, maintaining system safety bounds.

Audit Trail: Event emission creates a permanent record of all fee rate changes for governance tracking.

Immediate Effect: Changes take effect immediately for all subsequent swaps, enabling responsive treasury management.

Testing Infrastructure

The contract includes specific features to support testing and development:

function validateHookAddress(BaseHook) internal pure override {
// Skip validation in tests
}

function setPoolManaged(PoolKey calldata key, bool managed) external {
isPoolManaged[key.toId()] = managed;
}

Development Support Features

Validation Override: The validateHookAddress override simplifies testing by bypassing complex address validation requirements.

Manual Pool Control: setPoolManaged allows direct manipulation of pool management status for controlled testing scenarios.

Unrestricted Testing: Testing functions intentionally bypass normal access controls to enable test coverage.

Architectural Benefits

This design provides several key advantages:

Modularity: Clear separation between configuration, fee collection, and withdrawal logic enables independent testing and upgrades.

Gas Efficiency: Optimized storage layout and minimal hook permissions reduce operational costs.

Security: Simple access control model reduces complexity while maintaining effective protection.

Flexibility: Configurable fee rates and selective pool management support diverse treasury strategies.

Transparency: Event logging enables complete auditability of treasury operations.

The foundation established in this architecture enables sophisticated treasury management while maintaining the simplicity and efficiency required for high-frequency DEX operations. In Part 2, we’ll explore how this architecture supports the hook lifecycle and fee collection mechanics.

Building a Treasury Management Hook for Uniswap V4 — Part 1: Contract Architecture and Core… was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.

By

Leave a Reply

Your email address will not be published. Required fields are marked *