Skip to content

Hooks

Hooks are smart contracts that can be set on a Channel. A Hook defines the rules for commenting on the specified Channel. They unlock flexible and powerful behaviors, such as:

  1. Defining who can comment and how comments behave
  2. Charging a small fee to post a comment
  3. Integrating with other protocols, such as tokens or NFTs

Protocol Fee

When sending ETH value to a hook, a protocol fee is automatically deducted from the value. This fee is used to support the protocol's development and maintenance. The remaining value is passed to the hook. The current protocol fee is set to 2%, but this value is subject to change in the future. Any fee changes will be announced at least 48 hours in advance.

Available hook Functions

onInitialize - Called when a channel is initialized onCommentAdd - Called when a comment is added onCommentEdit - Called when a comment is edited onCommentDelete - Called when a comment is deleted onChannelUpdate - Called when a channel is updated onCommentHookDataUpdate - Called when hook metadata is updated for an existing comment

See IHook or BaseHook

Discover community created hooks

Awesome hooks is a collection of hooks that can be used to build on top of ECP.

Modifying the hook on a channel

channelManager.setHook(channelId, hookAddress);

Updating hook metadata

The updateCommentHookData function allows you to trigger a hook to update its metadata for an existing comment. This is useful for scenarios where hook metadata needs to be refreshed based on external conditions or time-based changes.

// Trigger hook metadata update for a specific comment
commentManager.updateCommentHookData(commentId);
Key Features:
  • Gas Efficient: Only updates specified metadata fields using SET and DELETE operations
  • Explicit Operations: Hooks return precise operations instead of replacing all metadata
  • Non-Payable: No ETH required for metadata updates
  • Permission-Based: Only works if the channel's hook has onCommentHookDataUpdate: true
Hook Implementation Example:
function _onCommentHookDataUpdate(
  Comments.Comment calldata commentData,
  Metadata.MetadataEntry[] calldata metadata,
  Metadata.MetadataEntry[] calldata hookMetadata,
  address msgSender,
  bytes32 commentId
) internal override returns (Comments.MetadataEntryOp[] memory) {
  Comments.MetadataEntryOp[] memory operations = new Comments.MetadataEntryOp[](
    2
  );
 
  // Update an existing field
  operations[0] = Comments.MetadataEntryOp({
    operation: Comments.MetadataOperation.SET,
    key: "string score",
    value: abi.encode(calculateNewScore(commentData))
  });
 
  // Delete a field
  operations[1] = Comments.MetadataEntryOp({
    operation: Comments.MetadataOperation.DELETE,
    key: "string temp_data",
    value: "" // Ignored for DELETE operations
  });
 
  return operations;
}

Writing Your Own hooks

Hooks allow you to customize channel behavior. To create a custom hook:

  1. Inherit from BaseHook
  2. Override the desired hook functions
  3. Implement the required permissions

Basic hook example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
 
import { BaseHook } from "@ecp.eth/protocol/src/hooks/BaseHook.sol";
import { Hooks } from "@ecp.eth/protocol/src/types/Hooks.sol";
import { Comments } from "@ecp.eth/protocol/src/types/Comments.sol";
import { Channels } from "@ecp.eth/protocol/src/types/Channels.sol";
 
contract MyCustomHook is BaseHook {
  function _getHookPermissions()
    internal
    pure
    override
    returns (Hooks.Permissions memory)
  {
    return
      Hooks.Permissions({
        onInitialize: true, // Enable channel initialization hook
        onCommentAdd: true, // Enable comment addition hook
        onCommentEdit: false, // Disable comment editing hook
        onCommentDelete: false, // Disable comment deletion hook
        onChannelUpdate: false, // Disable channel update hook
        onCommentHookDataUpdate: false // Disable comment hook update hook
      });
  }
 
  function _onInitialize(
    address channelManager,
    Channels.Channel memory channelData,
    uint256 channelId,
    Metadata.MetadataEntry[] calldata metadata
  ) internal override returns (bool) {
    // Your initialization logic here
    return true;
  }
 
  function _onCommentAdd(
    Comments.Comment calldata commentData,
    address msgSender,
    bytes32 commentId
  ) internal override returns (string memory) {
    // Your comment addition logic here
    return "";
  }
}

Best Practices

  1. Hook Implementation
    • Keep hook logic gas-efficient
    • Handle errors gracefully
    • Validate inputs thoroughly
  2. Channel Management
    • Use meaningful channel names and descriptions
    • Keep track of channel IDs
  3. Security
    • Validate all inputs in hooks
    • Be cautious with external calls
    • Test hooks thoroughly
    • Consider implementing a ReentrancyGuard in your hook if you want to prevent reentrancy attacks
  4. Fees
    • Consider refunding any excess fees to the user
    • use channelManager.calculateMsgValueWithHookFee to calculate the correct amount of ETH to send to your hook

Troubleshooting

  1. Hook Not Working
    • Check if hook permissions are set correctly
    • Verify the hook implementation
    • Ensure the hook is properly set on the channel
    • Remember that hooks are called after modifying contract state, and any revert in a hook will revert the entire transaction
  2. Channel Creation Fails
    • Ensure you have enough ETH for the creation fee
    • Check if the channel name/description is valid
    • Verify that metadata is valid JSON
    • Verify the hook is approved by the ECP team
  3. Hook Reverts
    • Check the hook implementation for errors
    • Verify input validation
    • Check gas limits