Cosmos General Message Passing
Axelar has expanded General Message Passing (GMP) to support Cosmos blockchains. With Axelar, you can now send and receive messages on EVM chains and Cosmos chains. Messages sent to Cosmos chains can be received by CosmWasm smart contracts (on blockchains with CosmWasm support), or those messages can be received natively at the consensus layer as part Go code.
To see full examples of sending and receiving messages in all cases, check out our full Cosmos GMP Example Project.
Messages from EVM to CosmWasm
To send a message from an EVM chain to a Cosmos CosmWasm contract, the payload must be encoded before calling the standard payNativeGasForContractCall
and callContract
methods (or token-including equivalents). You must also specify the destination method you are calling.
The payload must specify:
- string - The method name of the CosmWasm contract to call
- string[] - The names of all arguments
- string[] - the types of all arguments
- bytes - the values of all arguments
MultiSend Example encoding
This example encodes a string array of recipients, as you can see in our example of sending tokens and recipients.
bytes memory argValue = abi.encode(recipients); // A standard EVM payload
bytes memory payload = abi.encode(
"multi_send", // CosmWasm method name
StringArray.fromArray1(["recipients"]), // argument name
StringArray.fromArray1(["string[]"]), // argument type
argValue // argument value
);
bytes memory payloadToCW = abi.encodePacked(
bytes4(uint32(1)), // version number
payload
);
Messages from EVM to Native Cosmos
When sending a message to a Cosmos chain with native (consensus layer) support for GMP, you must encode a version number with the intended payload. This allows the consensus layer to know what to do with the payload.
bytes memory payload = abi.encodePacked(
bytes4(uint32(0)), // version number
abi.encode(receiverAddresses)
);
Messages from Native Cosmos
To send a message from Naive Cosmos Go code, you must send a message to the Axelar GMP Account (axelar1dv4u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5s
) via IBC.
When sending messages from Cosmos, you should perform standard GMP ABI encoding via the github.com/ethereum/go-ethereum/accounts/abi
package.
// build payload that can be decoded by solidity
addressesType, err := abi.NewType("address[]", "address[]", nil)
if err != nil {
return nil, err
}
var addresses []common.Address
for _, receiver := range msg.ReceiverAddresses {
addresses = append(addresses, common.HexToAddress(receiver))
}
payload, err := abi.Arguments{{Type: addressesType}}.Pack(addresses)
if err != nil {
return nil, err
}
message := Message{
DestinationChain: msg.DestinationChain,
DestinationAddress: msg.DestinationAddress,
Payload: payload,
Type: TypeGeneralMessageWithToken,
}
bz, err := message.Marshal()
if err != nil {
return nil, err
}
msg := ibctransfertypes.NewMsgTransfer(
ibctransfertypes.PortID,
"channel-17", // hard-coded channel id for demo
msg.Amount,
msg.Sender,
AxelarGMPAcc,
clienttypes.ZeroHeight(),
uint64(ctx.BlockTime().Add(6*time.Hour).UnixNano()),
)
msg.Memo = string(payload)
res, err := k.ibcTransferK.Transfer(goCtx, msg)
if err != nil {
return err
}
Messages from CosmWasm
To send a message from CosmWasm Rust code, you must send a message to the Axelar Gateway (axelar1dv4u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5s
) via IBC.
You should encode your payload using ethabi.
pub fn multi_send_to_evm(
deps: DepsMut,
env: Env,
info: MessageInfo,
destination_chain: String,
destination_contract: String,
recipients: Vec<String>,
fee: Option<Fee>
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
let addresses = recipients
.into_iter()
.map(|s| {
match s.parse::<H160>() {
Ok(address) => Ok(Token::Address(Address::from(address))),
Err(_) => Err(ContractError::InvalidRecipientAddress { address: s }),
}
})
.collect::<Result<Vec<Token>, ContractError>>()?;
let payload = encode(&[Token::Array(addresses)]);
let msg = GeneralMessage {
destination_chain,
destination_address: destination_contract.clone(),
payload,
type_: 2,
fee
};
let coin = cw_utils::one_coin(&info).unwrap();
let ibc_transfer = MsgTransfer {
source_port: "transfer".to_string(),
source_channel: config.channel.to_string(),
token: Some(coin.into()),
sender: env.contract.address.to_string(),
receiver: AXELAR_GATEWAY.to_string(),
timeout_height: None,
timeout_timestamp: Some(env.block.time.plus_seconds(604_800u64).nanos()),
memo: to_string(&msg).unwrap(),
};
// Base response
let response = Response::new()
.add_attribute("status", "ibc_message_created")
.add_attribute("ibc_message", format!("{:?}", ibc_transfer));
return Ok(response.add_message(ibc_transfer));
}
Chain Support
For a Cosmos blockchain to support receiving GMP messages (naively or via CosmWasm), it should implement the appropriate middleware in Go as part of the consensus layer.
See our Sample GMP Middleware.