
{"id":78771,"date":"2025-07-07T10:45:39","date_gmt":"2025-07-07T10:45:39","guid":{"rendered":"https:\/\/mycryptomania.com\/?p=78771"},"modified":"2025-07-07T10:45:39","modified_gmt":"2025-07-07T10:45:39","slug":"building-a-treasury-management-hook-for-uniswap-v4-part-2-hook-lifecycle-and-fee-collection","status":"publish","type":"post","link":"https:\/\/mycryptomania.com\/?p=78771","title":{"rendered":"Building a Treasury Management Hook for Uniswap V4\u200a\u2014\u200aPart 2: Hook Lifecycle and Fee Collection\u2026"},"content":{"rendered":"<h3>Building a Treasury Management Hook for Uniswap V4\u200a\u2014\u200aPart 2: Hook Lifecycle and Fee Collection Mechanics<\/h3>\n<h3>Pool Initialization and Registration<\/h3>\n<p>When new pools are created in Uniswap V4, our hook automatically registers them for treasury management through the afterInitialize callback:<\/p>\n<p>function _afterInitialize(<br \/>    address,<br \/>    PoolKey calldata key,<br \/>    uint160,<br \/>    int24<br \/>) internal override returns (bytes4) {<br \/>    PoolId poolId = key.toId();<br \/>    isPoolManaged[poolId] = true;<br \/>    return IHooks.afterInitialize.selector;<br \/>}<\/p>\n<h3>Registration Strategy<\/h3>\n<p><strong>Universal Coverage<\/strong>: The hook automatically registers all newly initialized pools, ensuring fee collection across the entire protocol.<\/p>\n<p><strong>Efficient Identification<\/strong>: Uses PoolKey.toId() to generate consistent, unique identifiers for each pool, leveraging Uniswap V4&#8217;s built-in hashing mechanism.<\/p>\n<p><strong>Single Storage Write<\/strong>: Each pool registration requires only one storage operation, minimizing gas costs during pool creation.<\/p>\n<p><strong>Proper Return Value<\/strong>: Returns the correct selector to confirm successful hook processing, maintaining compatibility with the hook\u00a0chain.<\/p>\n<h3>Pre-Swap Validation and Optimization<\/h3>\n<p>The beforeSwap hook implements a crucial optimization pattern that determines whether to process swaps for fee collection:<\/p>\n<p>function _beforeSwap(<br \/>    address,<br \/>    PoolKey calldata key,<br \/>    IPoolManager.SwapParams calldata,<br \/>    bytes calldata<br \/>) internal view override returns (bytes4, BeforeSwapDelta, uint24) {<br \/>    PoolId poolId = key.toId();<\/p>\n<p>    if (!isPoolManaged[poolId]) {<br \/>        return (bytes4(0), BeforeSwapDelta.wrap(0), 0);<br \/>    }<\/p>\n<p>    return (IHooks.beforeSwap.selector, BeforeSwapDelta.wrap(0), 0);<br \/>}<\/p>\n<h3>Optimization Logic<\/h3>\n<p><strong>Early Exit Strategy<\/strong>: For unmanaged pools, returning bytes4(0) signals the hook system to skip the afterSwap callback entirely, saving significant gas.<\/p>\n<p><strong>Managed Pool Processing<\/strong>: Managed pools return the proper selector, ensuring the fee collection logic in afterSwap will\u00a0execute.<\/p>\n<p><strong>Gas Efficiency<\/strong>: This pattern prevents unnecessary processing for pools that shouldn\u2019t participate in fee collection.<\/p>\n<p><strong>View Function Optimization<\/strong>: The function is marked as view, enabling the EVM to optimize storage reads during execution.<\/p>\n<h3>Core Fee Collection Implementation<\/h3>\n<p>The afterSwap hook contains the primary fee collection logic:<\/p>\n<p>function _afterSwap(<br \/>    address,<br \/>    PoolKey calldata key,<br \/>    IPoolManager.SwapParams calldata params,<br \/>    BalanceDelta delta,<br \/>    bytes calldata<br \/>) internal override returns (bytes4, int128) {<br \/>    PoolId poolId = key.toId();<\/p>\n<p>    if (treasuryFeeRate == 0) {<br \/>        return (IHooks.afterSwap.selector, 0);<br \/>    }<\/p>\n<p>    (Currency tokenIn, uint256 amountIn) = _getSwapInputDetails(key, params, delta);<\/p>\n<p>    if (amountIn &gt; 0) {<br \/>        uint256 feeAmount = (amountIn * treasuryFeeRate) \/ BASIS_POINTS;<\/p>\n<p>        if (feeAmount &gt; 0) {<br \/>            accumulatedFees[tokenIn] += feeAmount;<br \/>            poolManager.take(tokenIn, address(this), feeAmount);<\/p>\n<p>            emit TreasuryFeeCollected(poolId, tokenIn, feeAmount);<\/p>\n<p>            return (IHooks.afterSwap.selector, int128(int256(feeAmount)));<br \/>        }<br \/>    }<\/p>\n<p>    return (IHooks.afterSwap.selector, 0);<br \/>}<\/p>\n<h3>Fee Collection Flow\u00a0Analysis<\/h3>\n<p><strong>Validation Phase<\/strong>: Verifies that treasuryFeeRate is non-zero to avoid unnecessary processing<\/p>\n<p><strong>Input Extraction<\/strong>: Calls _getSwapInputDetails to determine which token and amount to charge fees\u00a0on<\/p>\n<p><strong>Fee Calculation<\/strong>: Applies the basis points calculation: (amountIn * treasuryFeeRate) \/ BASIS_POINTS<\/p>\n<p><strong>State Updates<\/strong>:<\/p>\n<p>Accumulates fees in the token-specific mappingUses poolManager.take() to transfer tokens from the pool to the hook\u00a0contract<\/p>\n<p><strong>Event Emission<\/strong>: Logs the fee collection for transparency and off-chain monitoring<\/p>\n<p><strong>Return Value<\/strong>: Reports the collected fee amount back to the pool\u00a0manager<\/p>\n<h3>Swap Input\u00a0Analysis<\/h3>\n<p>The _getSwapInputDetails function determines which token and amount to charge fees\u00a0on:<\/p>\n<p>function _getSwapInputDetails(<br \/>    PoolKey calldata key,<br \/>    IPoolManager.SwapParams calldata params,<br \/>    BalanceDelta delta<br \/>) private pure returns (Currency tokenIn, uint256 amountIn) {<br \/>    if (params.zeroForOne) {<br \/>        tokenIn = key.currency0;<br \/>        amountIn = delta.amount0() &lt; 0 ? uint256(uint128(-delta.amount0())) : 0;<br \/>    } else {<br \/>        tokenIn = key.currency1;<br \/>        amountIn = delta.amount1() &lt; 0 ? uint256(uint128(-delta.amount1())) : 0;<br \/>    }<br \/>}<\/p>\n<h3>Swap Direction Logic<\/h3>\n<p><strong>Direction Detection<\/strong>: params.zeroForOne indicates whether the swap is from currency0 to currency1 or vice\u00a0versa<\/p>\n<p><strong>Token Mapping<\/strong>: Maps the swap direction to the correct input currency from the pool\u00a0key<\/p>\n<p><strong>Amount Extraction<\/strong>: Negative delta values represent tokens being input to the pool (sold by the\u00a0trader)<\/p>\n<p><strong>Safe Type Conversion<\/strong>: Converts signed int128 delta values to unsigned uint256 amounts, handling the sign flip\u00a0properly<\/p>\n<p><strong>Zero Amount Handling<\/strong>: Returns 0 for cases where the delta is positive or zero (no\u00a0input)<\/p>\n<h3>Fee Calculation Mathematics<\/h3>\n<p>The fee calculation system uses basis points for precise\u00a0control:<\/p>\n<p>uint256 feeAmount = (amountIn * treasuryFeeRate) \/ BASIS_POINTS;<\/p>\n<h3>Mathematical Properties<\/h3>\n<p><strong>Precision Control<\/strong>: 10,000 basis points provide 0.01% precision, allowing fine-tuned fee adjustments<\/p>\n<p><strong>Range Coverage<\/strong>: Fee rates from 0 to 1000 basis points cover 0% to 10% in 0.01% increments<\/p>\n<p><strong>Overflow Protection<\/strong>: Uses 256-bit arithmetic to prevent overflow even with maximum possible\u00a0values<\/p>\n<p><strong>Trader-Favorable Rounding<\/strong>: Integer division rounds down, ensuring traders aren\u2019t overcharged due to\u00a0rounding<\/p>\n<h3>Example Calculations<\/h3>\n<p>Assume <strong>amountIn<\/strong> is\u00a01000;<\/p>\n<p><strong>1% fee<\/strong> (100 basis points): (1000 * 100) \/ 10000 = 10\u00a0tokens<strong>0.5% fee<\/strong> (50 basis points): (1000 * 50) \/ 10000 = 5\u00a0tokens<strong>0.25% fee<\/strong> (25 basis points): (1000 * 25) \/ 10000 = 2\u00a0tokens<strong>10% fee<\/strong> (1000 basis points): (1000 * 1000) \/ 10000 = 100\u00a0tokens<\/p>\n<h3>Token Transfer Integration<\/h3>\n<p>Fee collection leverages Uniswap V4\u2019s optimized transfer\u00a0system:<\/p>\n<p>poolManager.take(tokenIn, address(this), feeAmount);<\/p>\n<h3>Transfer Mechanism Benefits<\/h3>\n<p><strong>Automatic Deduction<\/strong>: Fees are automatically deducted from the swap proceeds before tokens reach the\u00a0trader<\/p>\n<p><strong>Gas Optimization<\/strong>: Leverages the pool manager\u2019s already-optimized transfer mechanisms rather than implementing custom\u00a0logic<\/p>\n<p><strong>Seamless Integration<\/strong>: Works with Uniswap V4\u2019s internal accounting system without requiring additional approvals<\/p>\n<p><strong>Multi-Token Support<\/strong>: Handles any ERC-20 token or ETH through the Currency abstraction<\/p>\n<h3>Event System for Transparency<\/h3>\n<p>The contract emits comprehensive events for all treasury operations:<\/p>\n<p>event TreasuryFeeCollected(PoolId indexed poolId, Currency indexed token, uint256 amount);<br \/>event TreasuryAddressChanged(address indexed oldTreasury, address indexed newTreasury);<br \/>event TreasuryFeeRateChanged(uint24 oldRate, uint24 newRate);<br \/>event FeesWithdrawn(Currency indexed token, uint256 amount);<\/p>\n<h3>Event Design\u00a0Benefits<\/h3>\n<p><strong>Indexed Parameters<\/strong>: Enable efficient filtering and searching in event\u00a0logs<\/p>\n<p><strong>Complete Coverage<\/strong>: All significant treasury operations emit events for full transparency<\/p>\n<p><strong>Gas Optimization<\/strong>: Events include only essential data to minimize gas\u00a0costs<\/p>\n<p><strong>Frontend Integration<\/strong>: Support real-time monitoring dashboards and analytics tools<\/p>\n<p><strong>Audit Trail<\/strong>: Create permanent, immutable records of all treasury activities<\/p>\n<h3>Gas Optimization Strategies<\/h3>\n<p>The implementation employs several techniques to minimize gas consumption:<\/p>\n<h3>Efficient Storage\u00a0Access<\/h3>\n<p><strong>Single Reads<\/strong>: Frequently accessed variables like treasuryFeeRate are read once and cached in\u00a0memory<\/p>\n<p><strong>Conditional Processing<\/strong>: Early exits prevent expensive operations when they\u2019re unnecessary<\/p>\n<p><strong>Optimized Mappings<\/strong>: Separate mappings for different data types avoid complex struct operations<\/p>\n<h3>Computational Efficiency<\/h3>\n<p><strong>Integer Arithmetic<\/strong>: All calculations use integer math to avoid expensive floating-point operations<\/p>\n<p><strong>Minimal Type Conversions<\/strong>: Carefully designed to minimize casting between different numeric\u00a0types<\/p>\n<p><strong>Basis Points System<\/strong>: Avoids decimal calculations while maintaining precision<\/p>\n<h3>Hook-Specific Optimizations<\/h3>\n<p><strong>Selective Processing<\/strong>: The beforeSwap optimization prevents unnecessary afterSwap execution<\/p>\n<p><strong>View Function Usage<\/strong>: Marking functions as view where possible enables EVM optimizations<\/p>\n<p><strong>Minimal Return Data<\/strong>: Returns only necessary data to reduce transaction costs<\/p>\n<h3>Pool Management Features<\/h3>\n<p>The contract provides both automatic and manual pool management capabilities:<\/p>\n<p>function getPoolManagedStatus(PoolKey calldata key) external view returns (bool) {<br \/>    return isPoolManaged[key.toId()];<br \/>}<\/p>\n<p>function setPoolManaged(PoolKey calldata key, bool managed) external {<br \/>    isPoolManaged[key.toId()] = managed;<br \/>}<\/p>\n<h3>Management Capabilities<\/h3>\n<p><strong>Status Queries<\/strong>: External contracts and frontends can check which pools participate in fee collection<\/p>\n<p><strong>Manual Override<\/strong>: Allows disabling fee collection for specific pools when\u00a0needed<\/p>\n<p><strong>Testing Support<\/strong>: Enables controlled testing scenarios with custom pool configurations<\/p>\n<p><strong>Flexible Configuration<\/strong>: Supports different treasury strategies for different pool\u00a0types<\/p>\n<p>The fee collection mechanics demonstrated here provide a foundation for automated treasury management. The system efficiently processes high-frequency swap operations while maintaining precise fee collection and monitoring capabilities. In Part 3, we\u2019ll explore the treasury operations and withdrawal mechanisms that complete the\u00a0system.<\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/building-a-treasury-management-hook-for-uniswap-v4-part-2-hook-lifecycle-and-fee-collection-a75c40f66658\">Building a Treasury Management Hook for Uniswap V4\u200a\u2014\u200aPart 2: Hook Lifecycle and Fee Collection\u2026<\/a> was originally published in <a href=\"https:\/\/medium.com\/coinmonks\">Coinmonks<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>","protected":false},"excerpt":{"rendered":"<p>Building a Treasury Management Hook for Uniswap V4\u200a\u2014\u200aPart 2: Hook Lifecycle and Fee Collection Mechanics Pool Initialization and Registration When new pools are created in Uniswap V4, our hook automatically registers them for treasury management through the afterInitialize callback: function _afterInitialize( address, PoolKey calldata key, uint160, int24) internal override returns (bytes4) { PoolId poolId = [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-78771","post","type-post","status-publish","format-standard","hentry","category-interesting"],"_links":{"self":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/78771"}],"collection":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=78771"}],"version-history":[{"count":0,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/78771\/revisions"}],"wp:attachment":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=78771"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=78771"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=78771"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}