Hooks and ISMs have a complementary relationship: you can customize your behavior from origin and they use a pairwise ISM contract on the destination to verify your custom hook behavior.

You can implement and utilize your own hook and ISM pattern as per your requirements. You can use an external bridge provider like Wormhole or Chainlink’s CCIP by implementing the IPostDispatchHook interface on the source chain and IInterchainSecurityModule on the destination chain.

Hooks currently expect metadata to be formatted with the StandardHookMetadata library.

You can also inherit from our AbstractMessageIdAuthorizedIsm which allows for access control for an intermediate verifyMessageId function call which sets in storage the messageId to true if received from the authorized AbstractMessageIdAuthHook hook. This pattern is used currently in the OpStackHook <> OpStackIsm pattern.

Workflow

Interface

After implementing the above interfaces, you can override default hook along the hook metadata by using the overloaded dispatch call in our mailbox. On the source chain:

  • mailbox.dispatch() calls your custom hook via AbstractMessageIdAuthHook.postDispatch().
  • _postDispatch checks whether latestDispatchedId is the id being dispatched from the hook to make the mailbox is the contract calling the hook (since calling postDispatch isn’t access controlled)
  • _sendMessageId calls your custom external bridge logic like calling the CCIP router contract.

On the destination chain:

  • The external bridge will call verifyMessageId function (which is access-controlled) and sets the messageId in the verifiedMessages mapping to true.
  • On receiving the message for the relayer, the mailbox will call your ISM contract (specified in your recipient address) which checks if the messageId in the verifiedMessages mapping is true and returns true to the mailbox and vice versa.

AbstractMessageIdAuthorizedIsm can send msg.value through postDispatch calls and we utilize the verifiedMessages’ little endian 255 bits for storing the msg.value and the top bit for the actual receipt of the messageId delivery. Therefore, you can send up to 2^255 amount of value of the native token from origin and the destination ISM can only receive 2^255 amount of value of native token on the destination chain.

Access Control

If postDispatch must only be called with a message that was just dispatched, the latestDispatchedId function on the Mailbox can be used to verify the message was actually dispatched.

This is used instead of some require(mailbox == msg.sender) to support composition where a hook may pass a message along to another hook.

The following utility is provided in the MailboxClient library for convenience.