Hyperlane

Hyperlane Setup

Configure the Hyperlane interoperability bridge on the BitSong crescendo-1 testnet to enable cross-chain messaging and token transfers.

This guide walks you through configuring the Hyperlane interoperability bridge on the crescendo-1 testnet. Hyperlane enables cross-chain messaging between BitSong and EVM chains (such as Base Sepolia) through a system of on-chain contracts and modules.

The setup consists of 7 transactions that create the full Hyperlane messaging stack on the BitSong side.

You need a running and synced node before proceeding. Make sure catching_up is false.

Overview

The Hyperlane bridge requires several components working together to secure and route messages:

MultisigISM

Verifies cross-chain messages using a set of validator signatures.

RoutingISM

Routes message verification to the correct ISM based on the origin domain.

Mailbox

Core contract for dispatching and receiving cross-chain messages.

MerkleTreeHook

Records message hashes in a Merkle tree for proof generation.

IGP

Interchain Gas Paymaster — handles gas payment for message delivery.

Prerequisites

Before starting, ensure you have the following ready:

  • A fully synced BitSong node on the crescendo-1 testnet
  • The bitsongd binary installed and available in your PATH
  • jq installed (sudo apt install jq)
  • Agent keys generated (you need the validator EVM address for Step 1)
  • A funded account with testnet TBTSG tokens
Need testnet tokens to pay for transaction fees? Get them from the BitSong Faucet.

Verify your node is running and synced:

Terminal
bitsongd status --node tcp://localhost:26657 | jq '.sync_info.latest_block_height'

Configuration Reference

These are the parameters used throughout the setup. Replace values as needed for your environment.

CHAIN_ID
string
BitSong testnet chain ID. Default: crescendo-1.
DOMAIN_ID
string
Hyperlane domain identifier for BitSong. Default: 7171.
REMOTE_DOMAIN
string
Hyperlane domain for Base Sepolia. Default: 84532.
DENOM
string
Native token denomination. Default: utbtsg.
KEY_NAME
string required
The key used for signing your transactions.

Set these as environment variables in your terminal for convenience:

Terminal
export CHAIN_ID="crescendo-1"
export DOMAIN_ID="7171"
export REMOTE_DOMAIN="84532"
export DENOM="utbtsg"
export KEY_NAME="<your-key-name>"
export NODE="tcp://localhost:26657"

# From the agent keys step
export VALIDATOR_ADDR="<your-validator-evm-address>"

Installation Steps

Each transaction returns a txhash. The commands below capture it and extract the resource ID into a shell variable, so subsequent steps can reference it directly.

All Hyperlane transactions emit events containing the created resource ID. To inspect all Hyperlane events from any transaction:
Terminal
bitsongd query tx $TX_HASH --output json | jq '.events[] | select(.type | test("hyperlane|warp"))'
Look for the attribute ending in _id (e.g. ism_id, mailbox_id, hook_id, token_id).

Create the MultisigISM

The Message ID Multisig ISM (Interchain Security Module) verifies incoming cross-chain messages by checking that a threshold of validators have signed the message.

For a single-validator testnet setup, use your own address with a threshold of 1:

Terminal
TX_HASH=$(bitsongd tx hyperlane ism create-message-id-multisig \
    $VALIDATOR_ADDR 1 \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y | jq -r '.txhash')
echo "TX Hash: $TX_HASH"
Use your real validator EVM address here — the one generated in the Agent Keys guide ($VALIDATOR_ADDR). Do not use a placeholder address. The validator agent must sign with the same key that is registered in the MultisigISM.

For a multi-validator setup, replace the command above with a comma-separated list of all validator EVM addresses and the required signature threshold:

Terminal
TX_HASH=$(bitsongd tx hyperlane ism create-message-id-multisig \
    "$VALIDATOR_ADDR_1,$VALIDATOR_ADDR_2,$VALIDATOR_ADDR_3" 2 \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y | jq -r '.txhash')
echo "TX Hash: $TX_HASH"
The threshold defines how many validators must sign for a message to be accepted. A threshold of 2 out of 3 validators provides fault tolerance — the bridge remains operational even if one validator goes offline.

Once the transaction is included in a block, extract the ISM ID:

Terminal
MULTISIG_ISM_ID=$(bitsongd query tx $TX_HASH --output json | \
  jq -r 'first(.events[] | select(.type | test("MultisigIsm")) | .attributes[] | select(.key == "ism_id") | .value | fromjson)')
echo "MultisigISM ID: $MULTISIG_ISM_ID"

Create the RoutingISM

The RoutingISM directs message verification to the correct ISM based on the origin domain. This allows different security configurations for messages arriving from different chains.

Terminal
TX_HASH=$(bitsongd tx hyperlane ism create-routing \
    --routes="[{\"domain\":$REMOTE_DOMAIN,\"ism\":\"$MULTISIG_ISM_ID\"}]" \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y | jq -r '.txhash')
echo "TX Hash: $TX_HASH"

Extract the RoutingISM ID:

Terminal
ROUTING_ISM_ID=$(bitsongd query tx $TX_HASH --output json | \
  jq -r 'first(.events[] | select(.type | test("RoutingIsm")) | .attributes[] | select(.key == "ism_id") | .value | fromjson)')
echo "RoutingISM ID: $ROUTING_ISM_ID"

Create the Mailbox

The Mailbox is the central contract for Hyperlane messaging. It dispatches outgoing messages and processes incoming messages, using the RoutingISM for verification.

Terminal
TX_HASH=$(bitsongd tx hyperlane mailbox create \
    $ROUTING_ISM_ID $DOMAIN_ID \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y | jq -r '.txhash')
echo "TX Hash: $TX_HASH"

Extract the Mailbox ID:

Terminal
MAILBOX_ID=$(bitsongd query tx $TX_HASH --output json | \
  jq -r 'first(.events[] | select(.type | test("Mailbox")) | .attributes[] | select(.key == "mailbox_id") | .value | fromjson)')
echo "Mailbox ID: $MAILBOX_ID"

Create the MerkleTreeHook

The MerkleTreeHook records dispatched message hashes into a Merkle tree. Hyperlane validators use this tree to generate proofs that messages were actually sent.

Terminal
TX_HASH=$(bitsongd tx hyperlane hooks merkle create \
    $MAILBOX_ID \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y | jq -r '.txhash')
echo "TX Hash: $TX_HASH"

Extract the MerkleTreeHook ID:

Terminal
MERKLE_HOOK_ID=$(bitsongd query tx $TX_HASH --output json | \
  jq -r 'first(.events[] | select(.type | test("MerkleTreeHook")) | .attributes[] | select(.key == "merkle_tree_hook_id") | .value | fromjson)')
echo "MerkleTreeHook ID: $MERKLE_HOOK_ID"

Create the IGP

The Interchain Gas Paymaster (IGP) allows message senders to pay for gas on the destination chain. This ensures that relayers are compensated for delivering messages.

Terminal
TX_HASH=$(bitsongd tx hyperlane hooks igp create \
    $DENOM \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y | jq -r '.txhash')
echo "TX Hash: $TX_HASH"

Extract the IGP ID:

Terminal
IGP_ID=$(bitsongd query tx $TX_HASH --output json | \
  jq -r 'first(.events[] | select(.type | test("Igp")) | .attributes[] | select(.key == "igp_id") | .value | fromjson)')
echo "IGP ID: $IGP_ID"

Configure IGP Gas Oracle

Set the destination gas configuration for the remote chain. This tells the IGP how to calculate gas costs for messages going to Base Sepolia.

Terminal
bitsongd tx hyperlane hooks igp set-destination-gas-config \
    $IGP_ID $REMOTE_DOMAIN \
    <token-exchange-rate> <gas-price> <gas-overhead> \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y

The gas parameters control pricing:

token-exchange-rate
integer required
Ratio of remote/local token price, scaled by 10^10 and adjusted for decimal difference (ETH 18 decimals, BTSG 6 decimals). See the calculation example below.
gas-price
integer required
Gas price on the destination chain in wei. Base Sepolia typically uses ~1 gwei = 1000000000.
gas-overhead
integer required
Fixed gas units added per message for EVM processing overhead. 75000 is standard for Hyperlane message delivery.
The exchange rate converts wei (remote) to utbtsg (local). The formula is:
tokenExchangeRate = (ETH_price / BTSG_price) × (10^6 / 10^18) × 10^10
Example (prices will vary — check current market rates before using):With ETH = $1,851.30 and BTSG = $0.000825:
tokenExchangeRate = (1851.30 / 0.000825) × (10^6 / 10^18) × 10^10
                  = 2,244,000 × 10^-12 × 10^10
                  = 2,244,000 × 10^-2
                  = 22440
Terminal
# Example with the values above — adjust to current prices
bitsongd tx hyperlane hooks igp set-destination-gas-config \
    $IGP_ID $REMOTE_DOMAIN \
    22440 1000000000 75000 \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y
Since the testnet relayer runs with --gaspaymentenforcement '[{"type": "none"}]', messages are delivered regardless of whether gas was paid. The exchange rate primarily affects how much TBTSG is quoted to users for gas prepayment. For production, keep this value updated with real market prices.

Set Mailbox Hooks

Link the IGP and MerkleTreeHook to the Mailbox. The default hook (IGP) runs on every dispatched message unless overridden, and the required hook (MerkleTree) always runs.

Terminal
bitsongd tx hyperlane mailbox set \
    $MAILBOX_ID \
    --default-hook=$IGP_ID \
    --required-hook=$MERKLE_HOOK_ID \
    --from $KEY_NAME \
    --keyring-backend test \
    --chain-id $CHAIN_ID \
    --node $NODE \
    --gas auto --gas-adjustment 1.5 \
    --fees 10000${DENOM} \
    --output json -y

Verify the Setup

After completing all steps, verify that every component was created correctly using the query commands below.

bitsongd query hyperlane mailboxes --output json | jq '.'

Troubleshooting

Next Steps

The Hyperlane messaging stack is deployed. Next, start the validator and relayer agents, then deploy the warp route for cross-chain token transfers.

Run Validators

Start the Hyperlane validator and relayer Docker agents to relay messages between BitSong and Base Sepolia.

Warp Token Deployment

Deploy a cross-chain warp route to enable token transfers between BitSong and Base Sepolia.

Agent Keys Reference

Review key management options including AWS KMS for production environments.
Copyright © 2026