Skip to content

Running a Relayer

The Hyperbridge relayer (tesseract) can be obtained through a variety of ways. For now only release artifacts for x86 linux environments are officially distributed. You can also build the relayer from source if you prefer.

Prebuilt binaries

You can install a prebuilt binary for the Tesseract relayer with the following bash script

wget -q --show-progress https://github.com/polytope-labs/hyperbridge/releases/download/tesseract-v0.3.3/tesseract-x86_64-unknown-linux-gnu.tar.gz
tar -xvzf tesseract-x86_64-unknown-linux-gnu.tar.gz
# copy to $PATH
cp tesseract-x86_64-unknown-linux-gnu/tesseract $HOME/.local/bin/

or a 1-liner

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/polytope-labs/hyperbridge/releases/download/tesseract-v0.3.3/tesseract-installer.sh | sh

Docker

Tesseract is available at the official docker repository polytopelabs/tesseract

docker pull polytopelabs/tesseract:latest

Building from source

You can follow the steps below if you'd prefer to build the tesseract relayer from source:

Install dependencies

Building the Tesseract relayer requires some dependencies

  • git
  • clang
  • curl
  • make
  • build-essential
  • libssl-dev
  • llvm
  • libudev-dev
  • protobuf-compiler
Debian
sudo apt update
sudo apt install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler

Install the rust compiler

If you don't have an already existing rust installation, you can install it using the one-liner below. Follow the prompts displayed to proceed with a default installation.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Clone the repo

Download a local copy of the repo and checkout the latest release tag

export LATEST_TAG=tesseract-v0.3.3
git clone https://github.com/polytope-labs/hyperbridge.git
cd ./hyperbridge
git checkout ${LATEST_TAG}

Build the tesseract relayer

cargo build --release -p tesseract

The Tesseract relayer will now be available at target/release/tesseract, You can move the binary to your $PATH so you can run it directly.


Update your path to include ${HOME}/.local/bin. If you are using Bash, run the following. Alternatively, replace ${HOME}/.bashrc with ${HOME}/.zshrc if using Zsh. Replace source with . if necessary.

# add .local/bin to path if it doesn't exist
export RC_PATH=${HOME}/.bashrc
echo 'export PATH="${HOME}/.local/bin:${PATH}"' >> ${RC_PATH}
source ${RC_PATH}
 
# create dir if it doesn't exist
mkdir -p $HOME/.local/bin/
mv target/release/tesseract $HOME/.local/bin/

Running the relayer

The tesseract relayer command line interface expects two arguments, which are the paths to it's configuration file and database file. If you're running it in a docker container, you will need to map the directory where you've stored the configuration files on your host into the docker container. Tesseract will write it's database to that directory once it's initialized.

docker run -d \
  --name=tesseract \
  --network=host \
  --restart=always \
  --volume=/dir/on/host:/home/root \
  polytopelabs/tesseract:latest \
  --config=/home/root/config.toml \
  --db=/home/root/tesseract.db

Access it's logs using

docker logs -f tesseract

System Requirements

At the minimum, the hyperbridge relayer should be run on a machine with at least 4GB of RAM and a quad-core cpu. This relayer should also have at least a 100Mb/s connection if it is to query nodes over the internet.

Ansible Playbooks

A community member has graciously provided their ansible playbook for running the hyperbridge relayer. You can find it here:

Configuration

The configuration file is a toml file that at the moment, that expects the following configuration options:

# Hyperbridge config, required
[hyperbridge]
state_machine = { Kusama = 4009 }
# Hyperbridge node ws rpc endpoint.
rpc_ws = "ws://127.0.0.1:9944" # example endpoint
# Sets the maximum size of an rpc request or response in bytes defaults to 150mb
max_rpc_payload_size = 150000000
 
# Relayer config, required
[relayer]
# Define your profitability configuration. 0 -> 0% i.e relay all requests, even unprofitable ones. 1 -> 1%. ie fees provided for requests must be profitable by at least 1%. etc.
minimum_profit_percentage = 0
# (Optional) If not empty, will filter requests to be delivered by originating module identifier (eg contract address/pallet ids)
module_filter = []
# How frequently to initiate withdrawals in seconds, defaults to 24 hours
withdrawal_frequency = 86400
# Minimum amount to withdraw when auto-withdrawing, defaults to $100
minimum_withdrawal_amount = 100
# (Optional) How frequently to retry unprofitable or failed messages in seconds.
# If this is value not supplied retries will not be enabled.
unprofitable_retry_frequency = 600
# (Optional) If not empty, tesseract will only deliver requests to the specified state-machines
delivery_endpoints = [
    { Ethereum = "ExecutionLayer" },
    { Ethereum = "Arbitrum" },
    { Ethereum = "Optimism" },
    { Ethereum = "Base" },
    "Bsc"
]
 
# Here you'll declare a new chain entry for every chain you want to support.
# eg ethereum
[ethereum]
# configuration type can be either "evm" or "substrate"
type = "evm"
# State machine identifier for the this evm chain. The possible values:
# state_machine = { Ethereum = "ExecutionLayer" } # Ethereum L1
# state_machine = { Ethereum = "Arbitrum" }
# state_machine = { Ethereum = "Optimism" }
# state_machine = { Ethereum = "Base" }
# state_machine = "Bsc" # Binance smart chain
state_machine = { Ethereum = "ExecutionLayer" }
# http(s) rpc urls for evm based chains
# Multiple rpc endpoints supported for increased reliability
rpc_urls = ["http://127.0.0.1:8545", ""]
# The consensus state identifier for this chain on hyperbridge.
# For Ethereum and it's L2s this will be "ETH0", for BSC this will be "BSC0".
consensus_state_id = "ETH0"
# Etherscan api key for querying the state machine's native token price.
# Do not that ethereum and all it's L2's use ETH as the native token
# So they can all share the same key gotten from etherscan.
#
# Bsc on the other hand uses the BNB token and would need its own API key
# gotten from bscscan
etherscan_api_key = ""
# The Handler contract address on this chain
handler = ""
# The IsmpHost contract address on this chain
ismp_host = ""
# (Optional)
# Maximum block range that should be used to query eth_getLogs in a single rpc call
# if this parameter is not supplied the range will not be split into smaller chunks
# and will be queried at once. This might be needed if you encounter rate limits
# from your rpc provider.
query_batch_size = 1000
# hex-encoded private key for the relayer account on this chain
signer = ""
# (Optional)
# Maximum number of concurrent rpc requests that can be used when tracing. If not supplied
# will trace entire transaction batches at once. This increases tracing speeds by performing
# tracing operations concurrently
#
# Note: Your rpc provider must support handling a large number of requests/sec(> 200 requests / sec) in parallel.
# Note: if you use the same provider for all your endpoints then a very high request/sec threshhold is required (> 1000 / sec)
tracing_batch_size = 5 # 5 transactions would be traced concurrently on this client
# (Optional)
# Buffer to add to gas price as a percentage of the current gas price
# to increase likelihood of the transactions going through e.g 1%, 2%
gas_price_buffer = 1
# (Optional)
# The execution client implementation only Geth or Erigon are fully supported
# The possible values:
# client_type = Geth
# client_type = Erigon
# If this field is not set, the default is Geth
client_type = Erigon
 
[substrate]
type = "substrate"
# The state machine identifier for this substrate based chain.
# must be one of:
# - { Polkadot = paraId }
# - { Kusama = paraId }
state_machine = { Kusama = 4009 }
# substrate node ws(s) rpc endpoint.
rpc_ws = "ws://127.0.0.1:9944" # example endpoint
# Configures the maximum size of an rpc request/response in bytes
max_rpc_payload_size = 150000000 # 150MB
# The consensus state identifier for this chain on hyperbridge.
# Only parachains are supported for now
consensus_state_id = "PARA"
# hex-encoded private key for the relayer account on this chain
signer = ""

It is optional to provide the configuration option for any of the connected chains, The only consequence is your relayer will not deliver requests from the ommitted chain as it has no way of querying the associated fees for requests originating from this chain.

You can obtain the required etherscan API key by following this guide for the appropriate network. Do note that since Ethereum and it's L2s all use Ether as the gas token. They can all share the same etherscan API key.

Contract addresses

You can find the up-to-date contract addresses for the Handler & IsmpHost contracts here

Withdrawing Fees

Tesseract maintains a local sqlite database where it keeps track of successfully delivered messages. The withdrawal process happens in two phases, the first phase is the fee accumulation on hyperbridge, then withdrawal on any of the connected chains. In the fee accumulation phase, tesseract submits state proofs of messages all delivered to hyperbridge. If the proof verification is successful, the total amount of unclaimed fees for the relayer address is updated onchain. Next, the relayer can submit a withdrawal transaction to hyperbridge. This initiates potentially multiple ISMP requests to any chain where it has unclaimed fees, that instructs the Hyperbridge contract to credit the relayer their owed funds. This request will not timeout, allowing it to be submitted to the destination chain at any time.

Accumulating fees

Stop any running relayer instances, run the following command in the terminal and wait for it to complete. The command will submit proofs for messages delivered to all chains present in the config file.

tesseract --config=$HOME/config.toml --db=$HOME/tesseract.db accumulate-fees

When accumulating fees the required state machine update to verify the proofs might not yet be available on hyperbridge, by default accumulating fees for those state machines will be skipped. To wait for all state machine updates.

tesseract --config=$HOME/config.toml --db=$HOME/tesseract.db accumulate-fees --wait

Initiating withdrawals

To initiate a withdrawal from hyperbridge, a relayer needs to submit a transaction to hyperbridge with triggers the withdrawal request. This extrinsic is unsigned and will not require any native tokens for execution fees.Once the extrinsic is executed, hyperbridge dispatches a POST request that when executed on it's destination, will provide the relayer with the fees they've accrued. The relayer account must have sufficient funds to deliver this request to it's destination chain.


To use this feature run the following command:

tesseract --config=$HOME/config.toml --db=$HOME/tesseract.db accumulate-fees --withdraw

Automatic accumulation and withdrawals

The relayer also runs background tasks for automatic fee accumulation and withdrawals. Whenever a batch of messages is successfully delivered, the fee accumulation task receives the delivery receipts and starts the process of accumulating the fees on hyperbridge. This process happens concurrently for all successfully delivered message batches. For redundancy, the delivery receipts are stored in the database prior to accumulation so they can be retried manually if any error is encountered.


Withdrawing fees from hyperbridge is triggered at fixed intervals based on the configured withdrawal_frequency and minimum_withdrawal_amount. Feel free to the adjust these values as desired. The task will only make a withdrawal attempt if your balance on hyperbridge is greater than or equal to the configured minimum_withdrawal_amount. Any failed withdrawal attempts will be retried each time the withdrawal task is triggered. The manual processes described in the previous sections can be used as fallbacks when errors are encountered by their automated conterparts.