- IBC CLI installed and configured
- IBC contracts deployed on two local Besu chains
- IBC connections established between the chains
- A relayer running
- An attestor running
- An Interchain Fungible Token that can be transferred between the chains
Prerequisites
1. Set up your environment
Start by installing IBC CLI and running two local Besu chains.- Clone the IBC repository and build the binary:
bin/ibc.
- This tutorial uses two local Besu chains, which are EVM-based. Start both chains:
- You can use the following command to look at the funded accounts included in the setup script:
2. Create a config and keys
IBC CLI uses one config file for all its operations.- Start by creating the config file:
~/.ibc/ibc.yml by default.
This is the home directory for everything that follows: the config, the keystore, the deployment records, and the relayer’s database.
- Import a deployer key from the accounts created during the chain setup. This key becomes the access manager’s admin, which governs the IBC router, multicall, and the GMP app. It is also the default owner of any token you deploy.
--populate-config adds each key to the config as a named signer. Later commands then name a key by alias instead of by path.
- Import a relayer key, which signs packet delivery and pays for the relayer’s gas:
- Generate an attestor key for chain 41002. It signs attestations and needs no funding. Never reuse an attestor key across clients: see Attestors.
- Next, register the first chain’s details in the config. This passes the chain ID, RPC and websocket endpoints, and deployer key.
- Register the second chain’s details:
--deployer flag names the key that signs deployments on that chain, so you won’t need to pass it again.
3. Deploy IBC on both chains
With both chains registered, deploy the IBC contracts on each.- Deploy the IBC core on each chain. This deploys the ICS26 router, multicall, and the access manager:
--populate-config flag writes each chain’s router and multicall addresses to ~/.ibc/ibc.yml.
- Deploy an attestation light client on 41001 to verify chain 41002:
--attestors flag resolves the key alias to its address. That address becomes the client’s attestation set on chain.
The --threshold 1 flag requires one signature for each proof. The set and threshold are fixed when the client is deployed.
- Deploy a Besu consensus light client on 41002 to verify chain 41001:
Both sides share one client identifier, derived from the two chain IDs in sorted order, so it reads the same on either chain. Here it is
cli-41001-41002, which will be used later to identify the connection.
4. Deploy the application
The next steps deploy the General Message Passing (GMP) app and the Interchain Fungible Token (IFT) contracts on each chain. IFT uses GMP to send and receive messages between the chains and facilitate token transfers.- Deploy the GMP app on each chain. This allows cross-chain contract calls between the chains:
- Deploy an IFT contract on each chain. This is the token that will be transferred between the chains:
deploy ift requires one: each direction lets up to --rate-limit-capacity base units through, refilling over --rate-limit-window (24 hours by default). Here that is 1000 tokens at 18 decimals. Rerunning with a different limit updates it.
- Use the following commands to read both token addresses out of the deployment records. These will be used in later steps:
deploy show command prints a chain’s deployment record as JSON.
- The next step is to link the two token contracts together. Registering a bridge tells each token to accept mints from its counterpart on the other chain. A transfer then burns tokens on the source chain and mints them on the destination.
5. Configure and start the relayer
Next, you’ll need to configure the relayer to start sending packets between the two chains.- Generate and save the relayer’s configuration:
server, db, and signers. The --yes flag skips confirmation before writing ~/.ibc/ibc.yml.
The two signer flags select the key that submits relay transactions on each chain. Set both for this new connection; each is checked against your configured signers. You imported relayer in step 3 of section 2.
The connection uses type: attestation on 41001 and type: besu on 41002. The attestors section declares one local attestor for 41002, using signer attestor-41002. The relayer runs that attestor in the same process.
- Use the validate command to check the result against both chains before starting anything:
- Open a new terminal in the
clidirectory to start the relayer and attestor. Leave your first terminal open for the transfer commands.
6. Transfer a token
The next steps will mint the token and send it to the other chain.- Mint an initial supply of the token into the deployer account.
--from key must be the token’s owner, which is the deployer. Amounts are in base units, so this is 100 tokens at 18 decimals.
- Send 10 tokens from one chain to the other, keeping the transaction hash.
$IFT_Ais the token address you stored earlier.
Note: The --timeout flag sets how long the packet has to be delivered, counted from when you send. It defaults to 15 minutes and cannot exceed one day. A packet that misses that window can only be timed out and refunded, and the deadline cannot be changed after the send.
{"txHash": "0x..."}. That hash is stored in $TX, which the next command uses:
- The relayer will automatically detect that the IBC packet was created and relay it.
- Now you can check the status of the transfer:
PACKET_STATE_PENDING for up to a minute, then it should read PACKET_STATE_SUCCEEDED:
- Confirm the tokens arrived on the destination chain:
What you built
Two local Besu chains now have IBC contracts and connected token contracts. One relayer hosts an attestor for 41002 and relays using both client types. Your first transfer moved 10 tokens from 41001 to 41002.Next steps
- Overview explains what the three parts are made of and how they fit together.
- Run a standalone attestor moves an attestor out of the relayer’s process and into its own.
- Run a standalone relayer brings up a relayer against a deployment, including one that replaces a relayer that stopped.