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:
- Defining who can comment and how comments behave
- Charging a small fee to post a comment
- 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
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);
- 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
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:
- Inherit from BaseHook
- Override the desired hook functions
- 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
-
Hook Implementation
- Keep hook logic gas-efficient
- Handle errors gracefully
- Validate inputs thoroughly
-
Channel Management
- Use meaningful channel names and descriptions
- Keep track of channel IDs
-
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
-
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
-
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
-
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
-
Hook Reverts
- Check the hook implementation for errors
- Verify input validation
- Check gas limits