Cosmos General Message Passing

Quantum Portal has expanded General Message Passing (GMP) to support Cosmos blockchains. With Quantum Portal, 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.

Cosmos GMP works by sending and receiving through IBC’s memo field. Cosmos chains looking to support GMP should integrate the appropriate middleware and verify message source.

đź’ˇ

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:

  1. string - The method name of the CosmWasm contract to call
  2. string[] - The names of all arguments
  3. string[] - the types of all arguments
  4. bytes - the values of all arguments

Multi-send 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 Native Cosmos Go code, you must send a message to the Quantum Portal GMP Account (QP1dv4u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5) 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,
    QPGMPAcc,
    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 Quantum Portal Gateway (QP1dv4u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5s) 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: QP_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.

If you have CosmWasm on your Cosmos chain and want to be able to receieve and deliver messages, install Osmosis IBC-hooks.

If you don’t support CosmWasm on your Cosmos chain, you’ll need to implement and install your own Quantum Portal GMP module to receive and execute messages. See our sample reference implementation.

Establishing a Path of Trust

Because IBC messages are permissionless, you’ll want to ensure you verify the source of all messages.

When you receive a packet on Cosmos chain, you’ll need to verify:

  1. That the packet came from Quantum Portal (verify IBC channel; Quantum Portal GMP sender account).

    Specify the Quantum Portal IBC channel by allowlisting the channel hash.

  2. The EVM source chain and address.

    When a message has an EVM source, the chain and address are passed in differently for CosmWasm and Native GMP.

    • CosmWasm: When you build your CosmWasm contract, you can define source_chain and/or source_address as arguments for their wasm method. And pass in the correct source_chain and/or source_address as an argument from the EVM contract. The Quantum Portal network will validate that these values are accurate and not being spoofed by the sender.
    • Native GMP: The GMP call will be sent by the Quantum Portal GMP account to the cosmos chain. Make sure that it is formatted correctly in the IBC memo that contains validated source chain/address values.
Edit this page