Manually relay messages through Amplifier

Chains integrated with Amplifier can pass GMP messages to one another through the following flow:

  1. When a user sends a message to the chain, a callContract event is emitted.
  2. The relayer picks up the event and calls verify_messages on the chain’s Axelar (internal) gateway contract.
  3. For chains that rely on verifier votes, the Axelar gateway calls verify_messages on the voting verifier, which prompts verifiers to begin voting.
  4. Once a threshold of successful votes have been cast for the message, an event is emitted.
  5. A relayer listens for this event and calls route_messages on the chain’s Axelar gateway.
  6. The chain’s Axelar gateway passes the messages to the Amplifier router.
  7. The router passes the message to the destination chain’s Axelar gateway.
  8. A relayer calls construct_proof, which starts the process of creating a signed batch that can be relayed back to the source chain. This method also passes the now-outbound message from the gateway to the prover.
  9. The prover starts a signing session with the multisig contract.
  10. Verifiers participate in the signing session.
  11. Once enough signatures have been submitted, the relayer gets the fully signed proof from the prover and relays the proof to the destination chain to approve the transactions. This relayer can now execute any approved transactions on the destination chain.
  1. Devnet Deployments.
  2. Stagenet Deployments.
  3. Testnet Deployments.
  4. Mainnet Deployments.

Once a call has been executed on an external chain such as Ethereum, the first step is to get that message verified with your integration’s unique Verifier Contract

The verify_messages command on the gateway prompts verifiers (in the case of a VotingVerifier verification choice) to begin voting so that a message can be verified and then routed. It takes a vector of messages to be verified. This example will trigger the verify_messages function in the Voting Verifier contract.

Note: A Voting Verifier verification is just one of many possibilities to verify messages. Other options include but are not limited to the use of ZK Proofs or light clients.

Terminal window
export SOURCE_CHAIN_GATEWAY="axelar1exenpcymaxz5gpkl0lmv85kxzgm6rk0czn9ysxn0ssf8j7c4rkts8ltcvw" # Chain integrations unique gateway
export CHAIN_NAME="toronto" # Chain name
export TX_HASH="0x4f5b0ed0dc717b317f16c841a407c79226deba7bc15e82ba98b4d4aab1169baf" # Hash of tx on Toronto chain
export TX_EVENT="1" # Index of call-contract event on Toronto chain
export DESTINATION_CHAIN="avalanche-fuji" # Name of dest chain as indicated in devnet-deployments.json file
export DESTINATION_ADDRESS="0x8f8dedd09E23E22E1555e9D2C25D7c7332291919" # Receiving dapp address on Avalanche
export SOURCE_ADDRESS="0x4501dc2E5Da9C3c8Eb6A68ecdE7a351229AA3134" # Sending dapp address on Toronto
export PAYLOAD_HASH="510904852D8E69EBFD1F84DC0DD0BF7A75C97A8C7EF873BF5BEF26DC41AD1C0F" # Hash of GMP payload sent
export RPC="http://devnet-amplifier.axelar.dev:26657" # Devnet RPC
export GAS_PRICE="0.007uamplifier" # Gas price for devnet tx
export CHAIN_ID="devnet-amplifier" # Devnet chain id
Terminal window
export SOURCE_CHAIN_GATEWAY="axelar1vnfn0e4vnn58ydpm05wqcc9zp8t5ztwd5rw5f895lykqaezmuccqujmwl2"
export CHAIN_NAME="avalanche"
export TX_HASH="0x41d08bd627d7be301e0858312b63b3ede840d7fad145a9da333c26adf5d98614"
export TX_EVENT="0"
export DESTINATION_CHAIN="ethereum-sepolia"
export DESTINATION_ADDRESS="0x8f8dedd09E23E22E1555e9D2C25D7c7332291919"
export SOURCE_ADDRESS="0x0a3b8dc7706c47b6dd87d771df63875b1c5cd867"
export PAYLOAD_HASH="220f68445e3cec114bff50cd6b251e3deabc7684b10280c2116b20bcc6795a96"
export RPC="https://tm.axelar-testnet.lava.build:443"
export GAS_PRICE="0.007uverifiers"
export CHAIN_ID="axelar-testnet-lisbon-3 "

Running this command will trigger a vote among the chain’s registered verifiers to come to consensus about the message that was sent.

Terminal window
axelard tx wasm execute $SOURCE_CHAIN_GATEWAY \
'{
"verify_messages": [
{
"cc_id": {
"source_chain":"'"$CHAIN_NAME"'",
"message_id":"'"$TX_HASH-$TX_EVENT"'"
},
"destination_chain":"'"$DESTINATION_CHAIN"'",
"destination_address":"'"$DESTINATION_ADDRESS"'",
"source_address":"'"$SOURCE_ADDRESS"'",
"payload_hash":"'"$PAYLOAD_HASH"'"
}
]
}' \
--keyring-backend test \
--from wallet \
--gas auto --gas-adjustment 1.5 --gas-prices $GAS_PRICE \
--chain-id $CHAIN_ID \
--node $RPC

With the message now verified, the next step is to route the message to the destination chain’s Prover contract. To do this you can run the route_messages command on the gateway. It takes a vector of verified messages to be routed.

Terminal window
export SOURCE_CHAIN_GATEWAY="axelar1exenpcymaxz5gpkl0lmv85kxzgm6rk0czn9ysxn0ssf8j7c4rkts8ltcvw" # Chain integrations unique gateway
export CHAIN_NAME="toronto" # Chain name
export TX_HASH="0x4f5b0ed0dc717b317f16c841a407c79226deba7bc15e82ba98b4d4aab1169baf" # Hash of tx on Toronto chain
export TX_EVENT="1" # Index of call-contract event on Toronto chain
export DESTINATION_CHAIN="avalanche-fuji" # Name of dest chain as indicated in devnet-deployments.json file
export DESTINATION_ADDRESS="0x8f8dedd09E23E22E1555e9D2C25D7c7332291919" # Receiving dapp address on Avalanche
export SOURCE_ADDRESS="0x4501dc2E5Da9C3c8Eb6A68ecdE7a351229AA3134" # Sending dapp address on Toronto
export PAYLOAD_HASH="510904852D8E69EBFD1F84DC0DD0BF7A75C97A8C7EF873BF5BEF26DC41AD1C0F" # Hash of GMP payload sent
export RPC="http://devnet-amplifier.axelar.dev:26657" # Devnet RPC
export GAS_PRICE="0.007uamplifier" # Gas price for devnet tx
export CHAIN_ID="devnet-amplifier" # Devnet chain id
Terminal window
export SOURCE_CHAIN_GATEWAY="axelar1vnfn0e4vnn58ydpm05wqcc9zp8t5ztwd5rw5f895lykqaezmuccqujmwl2"
export CHAIN_NAME="avalanche"
export TX_HASH="0x41d08bd627d7be301e0858312b63b3ede840d7fad145a9da333c26adf5d98614"
export TX_EVENT="0"
export DESTINATION_CHAIN="ethereum-sepolia"
export DESTINATION_ADDRESS="0x8f8dedd09E23E22E1555e9D2C25D7c7332291919"
export SOURCE_ADDRESS="0x0a3b8dc7706c47b6dd87d771df63875b1c5cd867"
export PAYLOAD_HASH="220f68445e3cec114bff50cd6b251e3deabc7684b10280c2116b20bcc6795a96"
export RPC="https://tm.axelar-testnet.lava.build:443"
export GAS_PRICE="0.007uverifiers"
export CHAIN_ID="axelar-testnet-lisbon-3"
Terminal window
axelard tx wasm execute $SOURCE_CHAIN_GATEWAY \
'{
"route_messages":[
{
"cc_id": {
"source_chain":"'"$CHAIN_NAME"'",
"message_id":"'"$TX_HASH-$TX_EVENT"'"
},
"destination_chain":"'"$DESTINATION_CHAIN"'",
"destination_address":"'"$DESTINATION_ADDRESS"'",
"source_address":"'"$SOURCE_ADDRESS"'",
"payload_hash":"'"$PAYLOAD_HASH"'"
}
]
}' \
--keyring-backend test \
--from wallet \
--gas auto --gas-adjustment 1.5 --gas-prices $GAS_PRICE \
--chain-id $CHAIN_ID \
--node $RPC

Now that the message has been routed, the Prover of the destination chain can construct the proof for it. construct_proof takes a vector of CrossChainIDs and builds a proof that includes the messages to be routed so that the message can be relayed to the destination gateway. It also passes the message from the gateway to the prover.

Terminal window
export DESTINATION_CHAIN_MULTISIG_PROVER="axelar14smpvv72hkaajh7rqzuhxum5vj3yh5smftaescucdmfsh9eednyqdme35j" # Chain integrations unique prover
export CHAIN_NAME="toronto" # Chain name
export TX_HASH="0x4f5b0ed0dc717b317f16c841a407c79226deba7bc15e82ba98b4d4aab1169baf" # Hash of tx on Toronto chain
export TX_EVENT="1" # Index of call-contract event on Toronto chain
export RPC="http://devnet-amplifier.axelar.dev:26657" # Devnet RPC
export GAS_PRICE="0.007uamplifier" # Gas price for devnet tx
export CHAIN_ID="devnet-amplifier" # Devnet chain id
Terminal window
export DESTINATION_CHAIN_MULTISIG_PROVER="axelar1xz4cya4qm2ws6nzperhvc40wdjcq4872fl6d3j2s4cytyx8j80eqenv87g"
export CHAIN_NAME="avalanche"
export TX_HASH="0x41d08bd627d7be301e0858312b63b3ede840d7fad145a9da333c26adf5d98614"
export TX_EVENT="0"
export RPC="https://tm.axelar-testnet.lava.build:443"
export GAS_PRICE="0.007uverifiers"
export CHAIN_ID="axelar-testnet-lisbon-3"
Terminal window
axelard tx wasm execute $DESTINATION_CHAIN_MULTISIG_PROVER \
'{
"construct_proof":
[
{
"source_chain":"'"$CHAIN_NAME"'",
"message_id":"'"$TX_HASH-$TX_EVENT"'"
}
]
}' \
--keyring-backend test \
--from wallet \
--gas auto --gas-adjustment 1.5 --gas-prices $GAS_PRICE \
--chain-id $CHAIN_ID \
--node $RPC

Now that the proof has been submitted you can run get_proof to return the fully signed proof from the multisig prover.

Terminal window
export DESTINATION_CHAIN_MULTISIG_PROVER="axelar14smpvv72hkaajh7rqzuhxum5vj3yh5smftaescucdmfsh9eednyqdme35j" # Chain integrations unique prover
export MULTISIG_SESSION_ID="1742" # Unique value for each proof generation
export RPC="http://devnet-amplifier.axelar.dev:26657" # Devnet RPC
Terminal window
export DESTINATION_CHAIN_MULTISIG_PROVER="axelar1xz4cya4qm2ws6nzperhvc40wdjcq4872fl6d3j2s4cytyx8j80eqenv87g"
export MULTISIG_SESSION_ID="3457"
export RPC="https://tm.axelar-testnet.lava.build:443"
Terminal window
axelard q wasm contract-state smart $DESTINATION_CHAIN_MULTISIG_PROVER \
'{
"get_proof":{
"multisig_session_id":"'"$MULTISIG_SESSION_ID"'"
}
}' \
--node $RPC

Use the Foundry cast command to send the output of get_proof to the destination chain:

Terminal window
export OUTPUT="0x2A8465a312ebBa54D774972f01D64574a5acFC63 0x64f1d85a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001600000000000000000000000008f8dedd09e23e22e1555e9d2c25d7c7332291919bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a00000000000000000000000000000000000000000000000000000000000000096176616c616e636865000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000443078343164303862643632376437626533303165303835383331326236336233656465383430643766616431343561396461333333633236616466356439383631342d3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a30783345373746643142346434313736434139643534644236306631333246624238384246413433434100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000005400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000000212fcc0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000036e007b2f03a6f01af8c99ca1cded439d57007a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009cc9a35433552080ed235722ff331bcfb533ba700000000000000000000000000000000000000000000000000000000000000010000000000000000000000003a188571605e41ea022304485551dfee11c436d50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000438b7642a57aa47d1a4d290a5e2a046f45c9a6a4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000044b994544df974b36fa75ed80dfb17332dc65fcc0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000467df763dfe6d7608c0d59dff527ad42fe58f29900000000000000000000000000000000000000000000000000000000000000010000000000000000000000004b67ea6cd2728b39e1cc94e48efa988a39cd391f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000005c8d652b996286429c5df2fe604bd5b1299bb26900000000000000000000000000000000000000000000000000000000000000010000000000000000000000006968b2ad7a6d04d57798aebba01ff8bcf5aef5b200000000000000000000000000000000000000000000000000000000000000010000000000000000000000009485acfded01e09aa89d4ae63ec0c4a9f90f42c800000000000000000000000000000000000000000000000000000000000000010000000000000000000000009cd2ebe044c1f87d97d92252507c4363cefdbcf00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a477a38bd7490fe6a54545dc95240492f89803000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a56d4a7c6bb8f83b642e057e97de3061020d06c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ac46ca659582a66fa9f12e47bb204eda021ddb220000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ea21d376c66903d4c99771650f9e55f7fd7a78c90000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f1e9b98362820cc9257635b1e86dbf00d0bd92090000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f4848a321f7297e1683d188c1383519cf003415c0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fb13d049c9e0f0d433a09e38c009b697b1039c7c0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000000056000000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000006600000000000000000000000000000000000000000000000000000000000000041eba2a31ea0eae246e2c0418b9ce6d4c2e5905fbd7e185df77b513c27f2fc4c477b91205725ec25208b7de0d8000116079629941291b0cbf8c0325e91cc64baf61c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041d1160889bb4c83702f294659a17b9a6191adff62efa9c4d677ce3a77c5367ffb78e238b1d2fc622304b9828ab55812499734b33d5dc6ad89fcbca84385dea1cc1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000411dcd0881bb334167dbe6358263d39b0efe1ab550c384cac620ab2a224e7acf84562eec81fc00ff062ba2077159dd5cc9e89ecfe8eb9356e19dde42a108f0b94f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000419dfa1468778d46d2c36d33398971f8c05a501c399984ae437640bb22df50c6070e2e73cc51910bf53527e268ca113156685bd0bbe3e020a0e35a5fbc2a17d4f51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041bef930be7baa161ad6c71274a7bfb992e6c525a5ae30ba241a9d6d24a287949a61dfb1f639950c0e9d94cb375ced32309ac60542e22f0b56a55020451bb5e5b81b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417f21fc01777a48f51c1ed19f2a11d148acecce644c0d3de4253c4e9d58df2cd4416336eec7e8787c93ec445d3d985cdb9ce3312974f716cd1acc762ed0bf8d3c1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a02d3135ff391b457bcbe39fb25568e80930e2e4e970cfc488d28cfd876dddd64fdda2def8f09e1fd0a2c4d1c225d27a897035491e763b222a2f16c5dddf4eb31c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041382e2e4cbd9eaff8ce466b17a64829c6fcea9af8cde13c1f75763326f3828a383710b82bbf83345c66bbeb7c6d19ccf85d469d9b7524075b2dc96729196ce7c51c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410c702c068f9a679825aadebb8a25932557550653a101a4832cb334e87a2a8a3818615d5754ebb7aaabaec6eb190a262bd0e06804f06f5a87e154797959505ac91b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cec4908951da8841f7b7b92ec17ed04e5b95c22e2dbd1101b85842631a1b89dd09dd99506848fd3010adc3daad7ad94d275e5ebf9f391abbfa7608d0051061651b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004148c875c4ee50edc3224de13ee4c2aa0f1ac2b2736a075fe59a8c2af810b3f0383c0fcb4373d4dcacc0aced17cb6ede9f2cb7b8cd56a41502dcccccbkbrukgebdtgekutnlvkgecbgelflgtbgfcgfdl
ef8a848c5a5adb71c00000000000000000000000000000000000000000000000000000000000000"
export RPC="https://rpc.ankr.com/eth_sepolia"
Terminal window
cast send $OUTPUT \
--rpc-url $RPC \
--mnemonic-path ./private.mneumonic

Use your MetaMask wallet to send the output of get_proof to the destination chain:

  1. Open your MetaMask wallet.
  2. Click Send.
  3. Get the destination chain’s gateway address from devnet-amplifier.json and paste it in the Send to field.
  4. Paste the output from get_proof in the Hex data field.
  5. Click Next.

The proof should then be sent to the destination chain.

Edit on GitHub