Testing Warp Route
This guide covers how to test cross-chain token transfers on your deployed warp route. You will send tokens from BitSong to Base Sepolia (Cosmos → EVM) and back (EVM → Cosmos).
TOKEN_ID, HYP_ERC20_ADDR, and other environment variables from that guide.Prerequisites
Before testing, ensure:
- Your warp route is deployed and both sides have routers enrolled
- Your validators and relayer are running (required to relay messages between chains)
- You have the environment variables from the deployment guide set in your terminal
Configuration Reference
These environment variables are needed for testing. If you followed the Warp Token Deployment guide, they should already be set:
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 deployment guide
export TOKEN_ID="<your-token-id>"
export HYP_ERC20_ADDR="<your-hyp-erc20-address>"
export EVM_KEY="0x<your-evm-private-key>"
export EVM_RPC="https://sepolia.base.org"
Cosmos to EVM
Send tokens from BitSong to Base Sepolia. The tokens are locked as collateral on Cosmos and minted as HypERC20 on the EVM chain.
# Convert recipient EVM address to bytes32
RECIPIENT_ADDR="0x<evm-recipient-address>"
RECIPIENT_BYTES32=$(printf "0x%064s" "${RECIPIENT_ADDR#0x}" | tr ' ' '0')
# Look up the IGP ID
IGP_ID=$(bitsongd query hyperlane hooks igps -o json --node $NODE | jq -r '.igps[0].id')
echo "IGP ID: $IGP_ID"
# Query the required interchain gas fee
REQUIRED_FEE=$(bitsongd query hyperlane hooks quote-gas-payment $IGP_ID $REMOTE_DOMAIN 300000 -o json --node $NODE | jq -r '.gas_payment[0].amount')
echo "Required fee: ${REQUIRED_FEE}utbtsg"
# Transfer 1000 utbtsg (0.001 TBTSG) to Base Sepolia
bitsongd tx warp transfer \
$TOKEN_ID $REMOTE_DOMAIN $RECIPIENT_BYTES32 1000 \
--max-hyperlane-fee "${REQUIRED_FEE}utbtsg" \
--from $KEY_NAME \
--keyring-backend test \
--chain-id $CHAIN_ID \
--node $NODE \
--gas auto --gas-adjustment 1.5 \
--fees 10000${DENOM} \
--output json -y
After the relayer delivers the message, check the HypERC20 supply and recipient balance on Base Sepolia:
# Total supply across all holders
cast call "$HYP_ERC20_ADDR" "totalSupply()(uint256)" --rpc-url "$EVM_RPC"
# Balance for the specific recipient
cast call "$HYP_ERC20_ADDR" "balanceOf(address)(uint256)" "$RECIPIENT_ADDR" --rpc-url "$EVM_RPC"
EVM to Cosmos
Send tokens back from Base Sepolia to BitSong. The HypERC20 tokens are burned on EVM and the collateral is unlocked on Cosmos.
bitsongd keys parse command below requires the bitsongd binary. Run this on your BitSong node, or any machine where bitsongd is installed.# Convert Cosmos recipient (bech32) to bytes32
COSMOS_RECIPIENT="bitsong1..." # replace with the actual bitsong address
ADDR_HEX=$(bitsongd keys parse "$COSMOS_RECIPIENT" 2>&1 | grep bytes | awk '{print $2}')
RECIPIENT_BYTES32="0x$(printf '%064s' "$ADDR_HEX" | tr ' ' '0')"
echo "Recipient bytes32: $RECIPIENT_BYTES32"
transferRemoteburns tokens from the caller. If you haven't done a Cosmos → EVM transfer first, the transaction will revert with ERC20: burn amount exceeds balance. Check your balance before sending:EVM_SENDER=$(cast wallet address --private-key "$EVM_KEY")
cast call "$HYP_ERC20_ADDR" "balanceOf(address)(uint256)" "$EVM_SENDER" --rpc-url "$EVM_RPC"
0, send tokens from Cosmos first using the section above.cast send "$HYP_ERC20_ADDR" \
"transferRemote(uint32,bytes32,uint256)" \
"$DOMAIN_ID" "$RECIPIENT_BYTES32" 1000 \
--value 1 \
--private-key "$EVM_KEY" \
--rpc-url "$EVM_RPC"
--value 1 sends 1 wei as interchain gas payment. On testnet with gas enforcement disabled, any non-zero value works.Troubleshooting
This means the relayer hasn't delivered the message yet. Check:
- Validators running:
docker psshould show the validator and relayer containers - Validator checkpoints: The bitsong validator must produce checkpoints after the transfer
- Relayer logs:
docker logs hyperlane-relayer --tail 50— look for delivery attempts - Router enrollment: Both sides must be enrolled. An incorrect or missing enrollment silently drops messages
This error occurs when calling transferRemote on the HypERC20 contract without holding any tokens. The function burns tokens from the caller (msg.sender) before dispatching the cross-chain message.
How to check your balance:
EVM_SENDER=$(cast wallet address --private-key "$EVM_KEY")
cast call "$HYP_ERC20_ADDR" "balanceOf(address)(uint256)" "$EVM_SENDER" --rpc-url "$EVM_RPC"
If the balance is 0, you need to perform a Cosmos → EVM transfer first to mint HypERC20 tokens to your address. The EVM-to-Cosmos direction only works as a return trip — tokens must be locked as collateral on Cosmos before they can be burned on the EVM side.
The IGP (Interchain Gas Paymaster) gas oracle may quote a very high fee depending on its configuration. For example, you might see a required fee of hundreds of TBTSG.
Check the quoted fee before transferring:
IGP_ID=$(bitsongd query hyperlane hooks igps -o json | jq -r '.igps[0].id')
bitsongd query hyperlane hooks quote-gas-payment $IGP_ID $REMOTE_DOMAIN 300000 -o json | jq '.gas_payment'
If the fee is unexpectedly high:
- Fund your account: Ensure you have enough TBTSG to cover both the transfer amount and the gas fee
- Check gas oracle config: The oracle's
exchange_rateandgas_pricedetermine the fee. These are set by the IGP owner - Reduce gas limit: The
300000value above is the gas limit for the destination chain — lowering it reduces the fee, but may cause delivery failure if too low