# Hermes Relayer

Before you dive into Go relayers, make sure to:

  • Install Go.
  • Install Docker.
  • Install Rust.

For all installations, please see the setup page.

In this section, you will learn:

  • How to get started with the Hermes relayer.
  • Basic Hermes relayer commands.

Hermes (opens new window) is an open-source Rust implementation of a relayer for the Inter-Blockchain Communication Protocol (IBC). Hermes is most widely used in production by relayer operators. It offers great logging and debugging options, but compared to the Go relayer may require some more detailed knowledge of IBC to use properly.

Installation instructions can be found in the Hermes documentation from Informal Systems (opens new window). Check the CLI commands with hermes -h. Alternatively, check out the commands reference (opens new window) on the Hermes website.

Recently the Hermes relayer upgraded the major version to v1. This is the first stable release and contains loads of improvements which you can check out in the changelog (opens new window). It is recommended to use v1 or higher from this point forward, and the commands below assume you are using v1.x.y.

If you type:

Copy $ hermes help

You get:

Copy hermes <version> Informal Systems <> Hermes is an IBC Relayer written in Rust USAGE: hermes [OPTIONS] [SUBCOMMAND] OPTIONS: -c, --config <CONFIG> path to configuration file -h, --help Print help information -j, --json enable JSON output -V, --version Print version information SUBCOMMANDS: clear Clear objects, such as outstanding packets on a channel config Validate Hermes configuration file create Create objects (client, connection, or channel) on chains health-check Performs a health check of all chains in the config help Print this message or the help of the given subcommand(s) keys Manage keys in the relayer for each chain listen Listen to and display IBC events emitted by a chain misbehaviour Listen to client update IBC events and handles misbehaviour query Query objects from the chain start Start the relayer in multi-chain mode tx Create and send IBC transactions update Update objects (clients) on chains upgrade Upgrade objects (clients) after chain upgrade completions Generate auto-complete scripts for different shells

When comparing the list of commands with the requirements from the introduction, recognize the ability to query and submit a transaction (tx), keys management, and a config command. However, no immediate commands are available to add chains and path information. The Hermes relayer does not support fetching data from the chain-registry (opens new window) automatically yet, but this is on the roadmap.

For now, you need to manually add the data to the config file config.toml, which is by default stored at $HOME/.hermes/config.toml.

The config is not added automatically. The first time you run Hermes, you will have to copy a template and paste it into the aforementioned folder.

See the config info (opens new window) and a sample configuration (opens new window) for a detailed explanation on all aspects of the config. Take a closer look at the [[chains]] section:

Copy [[chains]] id = 'ibc-1' rpc_addr = '' grpc_addr = '' websocket_addr = 'ws://' rpc_timeout = '10s' account_prefix = 'cosmos' key_name = 'testkey' store_prefix = 'ibc' default_gas = 100000 max_gas = 400000 gas_price = { price = 0.001, denom = 'stake' } gas_multiplier = 1.1 max_msg_num = 30 max_tx_size = 2097152 clock_drift = '5s' max_block_time = '30s' trusting_period = '14days' trust_threshold = { numerator = '1', denominator = '3' } address_type = { derivation = 'cosmos' }

Pay particular attention to the RPC, gRPC, and websocket endpoints and make sure they correspond with the node you are running. Remember that it is recommended to run your own full node instead of using publicly available endpoints when relaying outside of testing purposes. Also, make sure the key_name corresponds to the funded address from which you intend to pay relayer fees. The other parameters can be found in the chain-registry (opens new window) for deployed chains or set by yourself when creating a new chain (either in production or for testing).

Hermes does not require path information in the config. By default, it will relay over all possible paths over all channels that are active on the configured chains. However, it is possible to change this by filtering. Add the following to the chain config:

Copy [chains.packet_filter] policy = 'allow' list = [ ['transfer', 'channel-141'], # osmosis-1 ]

This filters only the transfer channel for the Hub to Osmosis in this example.

# Hermes start

When the chains have been configured, you can start the relayer with the start command:

Copy $ hermes start

This powerful command bundles a lot of functionality where Hermes will be listening for events signaling IBC packet send requests, submitting ReceivePacket and AcknowledgePacket messages, and periodically checking if the clients on serviced chains need updating. However, during the tutorials, it makes sense to look at the commands in a more granular way to understand what is going on.

When starting the Hermes relayer, it will assume that the channels you wish to relay over are set up. This will be the case if you want to start relaying on an existing canonical channel, meaning the official and agreed-upon channel (for example, used for fungible token transfers).

This is perfectly possible and the right approach, given that creating a new channel would make assets relayed over it non-fungible with assets relayed over the canonical channel. Most tutorials will create new channels (and possibly clients and connections) as this provides more insight into the software. However, it is important to note that you only need to create new channels if no canonical channel is present (for example, for a newly deployed chain).

# Testing locally

The Hermes documentation provides a guided tutorial (opens new window) to start relaying between two local gaia chains. Furthermore, demos are available that spin up a Hermes relayer between two Ignite CLI (opens new window) chains, like this one (opens new window). Be sure to check those out.

Here you will use a docker-compose network with two local checkers chains and a relayer between them.

The example presented is based on the demo in the b9lab/cosmos-ibc-docker (opens new window) repository.

Start by cloning the repository:

Copy $ git clone

Then build the images for the checkers blockchain if you did not already do so in the Go Relayer section:

Copy $ cd cosmos-ibc-docker/tokentransfer/checkers $ ./

You can build the relayer image manually, or just start the network via docker-compose and let it build the missing image for the hermes relayer:

Copy $ cd cosmos-ibc-docker/tokentransfer $ docker-compose -f tokentransfer.yml --profile hermes up

Observe the output of docker-compose until the chains are ready - the chains will take some time.

When the chains are ready, go into the relayer container and run a bash:

Copy $ docker exec -it relayer bash

First, check the Hermes version with:

Copy $ hermes version

In this section, you have to run commands both inside the Docker container and on your local terminal. By default, coding examples will indicate the Docker terminal; a comment will inform you when you have to use the local terminal.

You can check the CLI commands with hermes -h. The Hermes CLI offers help for each CLI command you can use when trying hermes <command> -h.

You can find the configuration in cosmos-ibc-docker/tokentransfer/relayer_hermes/config.toml.

You will see two [[chains]] sections in the config.toml. The first one includes comments about configuration.

Chain IDs need to be specified, as well as the RPC, GRPC, and WebSocket addresses.

Do a validation check on the configuration file:

Copy $ hermes config validate

Next, do a health check:

Copy $ hermes health-check

You should see that both chains are healthy. The demo includes a script to start the relayer, but do the steps manually to practice a bit.

# Manual testing - setting up relayer keys

You need some keys to sign a transaction. Populate the aliases:

Copy $ hermes keys add --chain checkersa --mnemonic-file "alice.json" $ hermes keys add --chain checkersb --mnemonic-file "bob.json"

Get the user addresses for the checkersa chain:

Copy $ hermes keys list --chain checkersa

Now get the user addresses for the checkersb chain:

Copy $ hermes keys list --chain checkersb

In the config.toml the default user key is set to alice for checkersa and bob for checkersb, so you do not need to specify a user if you want to sign a transaction with those.

Now check the balance of those accounts in another terminal:

Copy # use your local terminal $ docker exec checkersa checkersd query bank balances cosmos14y0kdvznkssdtal2r60a8us266n0mm97r2xju8 $ docker exec checkersb checkersd query bank balances cosmos173czeq76k0lh0m6zcz72yu6zj8c6d0tf294w5k

# Manual testing - create a channel

It is time to create a channel in order to send some tokens from checkersa to checkersb. In the relayer container, run:

Copy $ hermes create channel --a-chain checkersa --b-chain checkersb --a-port transfer --b-port transfer --new-client-connection

To query the clients for the chain checkersa, run:

Copy $ hermes query clients --host-chain checkersa

There should be one CometBFT client for the chain checkersb.

Query the connections for checkersa:

Copy $ hermes query connections --chain checkersa

There should be one connection established between checkersa and checkersb.

Query the channels for checkersa:

Copy $ hermes query channels --chain checkersa

You should see one channel and the port binding transfer. All this is part of the create channel command. It will create a client, a connection, and a channel as well as a binding to a port. You can redo some steps to better understand the CLI.

Create another connection for both chains:

Copy $ hermes create connection --a-chain checkersa --b-chain checkersb

In the output of this command, you receive the connection_ids for both chains. Use the connection_id for the checkersa chain and create a channel:

Copy $ hermes create channel --a-port transfer --b-port transfer --a-chain checkersa --a-connection connection-1

This repeats the port binding transfer. Check that the channel is created again with:

Copy $ hermes query channels --chain checkersa

# Manual testing - send an IBC transfer

Next up, send an IBC transfer using the second channel that was created:

Copy $ hermes tx ft-transfer --src-chain checkersa --dst-chain checkersb --src-port transfer --src-channel channel-1 --amount 100 --denom token --timeout-height-offset 1000

In case you do not want to test with the default user, you can specify the sender with a -k flag and the receiver on the other chain with a -r flag.

Usually, the Hermes relayer automatically relays packets between the chains if it runs via:

Copy $ hermes start

In this case, you want to relay the transfer transaction by hand.

First, query packet commitments on checkersa:

Copy $ hermes query packet commitments --chain checkersa --port transfer --channel channel-1

You can see that there is one packet:

Copy SUCCESS PacketSeqs { height: Height { revision: 0, height: 2382, }, seqs: [ Sequence( 1, ), ], }

You can also query for unreceived packets:

Copy $ hermes query packet pending --chain checkersb --port transfer --channel channel-1

The output should be similar to:

Copy SUCCESS Summary { src: PendingPackets { unreceived_packets: [], unreceived_acks: [], }, dst: PendingPackets { unreceived_packets: [ Sequence( 1, ), ], unreceived_acks: [], }, }

There you can observe an unreceived packet.

You can get the connection_id and channel_id for checkersb in the output of the hermes create connection and hermes create channel commands.

If you check the balances again, you should only see a change for checkersa. You should see no change in the balance of bob on checkersb because the transfer is initiated but it is not relayed yet.

Now submit the RecvPacket message to checkersb:

Copy $ hermes tx packet-recv --dst-chain checkersb --src-chain checkersa --src-port transfer --src-channel channel-1

In case of success, you will see an output like:

Copy SUCCESS [ SendPacket( SendPacket - seq:1, path:channel-1/transfer->channel-1/transfer, toh:0-3368, tos:Timestamp(NoTimestamp)), ), ]

Send an acknowledgement to checkersa:

Copy $ hermes tx packet-ack --dst-chain checkersa --src-chain checkersb --src-port transfer --src-channel channel-1

Check the balances again. A new denom should appear because of the recent transfer. As an exercise, transfer the tokens back to checkersa.

If you are finished with the tests, make sure to shut down your network with:

Copy $ docker-compose -f tokentransfer.yml --profile hermes down

To summarize, this section has explored:

  • Hermes, an open-source Rust implementation of an IBC relayer, which is widely used in production by relayer operators due to its great logging and debugging options, but may require more detailed knowledge of IBC for effective use.
  • How to install and configure Hermes, and then perform automated and manual end-to-end testing of Docker containers for two-chain instances and a relayer instance.