Skip to main content
Version: 5.0

Messages

MsgRegisterInterchainAccount

Attempts to register an interchain account by sending an IBC packet over an IBC connection.

message MsgRegisterInterchainAccount {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

string from_address = 1;
string connection_id = 2 [ (gogoproto.moretags) = "yaml:\"connection_id\"" ];
string interchain_account_id = 3 [ (gogoproto.moretags) = "yaml:\"interchain_account_id\"" ];
repeated cosmos.base.v1beta1.Coin register_fee = 4;
}
  • from_address must be a smart contract address, otherwise the message will fail;
  • connection_id must be the identifier of a valid IBC connection, otherwise the message will fail;
  • interchain_account_id is used to generate the owner parameter for ICA's RegisterInterchainAccount() call, which is later used for port identifier generation (see below). Maximum allowed length of interchain_account_id is 47 characters.
  • register_fee fee is required to be paid in favor of the community (payee address is the treasury) to register an interchain account. Minimal amount of fee is contolled by the module's param RegisterFee
IBC ports naming / Interchain Account address derivation

If a contract with the address neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq sends an MsgRegisterInterchainAccount with interchain_account_id set to hub/1, the generated ICA owner will look like neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq.hub/1, and the IBC port generated by the ICA app will be equal to icacontroller-neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq.hub/1.

Neutron V2 update: Fee Implementation for ICA Registration

As of Neutron V2, we have introduced a new fee structure for the registration of Interchain Accounts (ICAs). Please be aware of the following updates:

  • Minimum Fee (minFee): A minimum fee is now required for all new ICA registrations. This fee goes directly to the FeeCollector with purpose of preventing spam.

  • FeeCollector Beneficiary: The FeeCollector is the designated recipient of the new registration fees, ensuring the economic sustainability of the network. This is the Neutron DAO at the moment.

  • Backwards Compatibility Assurance:

    • Contracts and ICAs established with contracts, stored on Neutron before V2 will not incur the new registration fee.
    • This update is fully compatible with previous Neutron V1 version, guaranteeing a smooth transition and no disruption to existing contracts and services.

ICA's remote address generation concatenates connection identifier and port identifier to use them as the derivation key for the new account:

// GenerateAddress returns an sdk.AccAddress derived using the provided module account address and connection and port identifiers.
// The sdk.AccAddress returned is a sub-address of the module account, using the host chain connection ID and controller chain's port ID as the derivation key
func GenerateAddress(moduleAccAddr sdk.AccAddress, connectionID, portID string) sdk.AccAddress {
return sdk.AccAddress(sdkaddress.Derive(moduleAccAddr, []byte(connectionID+portID)))
}

Note: your contract needs to implement the sudo() entrypoint on order to successfully process the IBC events associated with this message. You can find an example in the neutron-sdk repository.

Response

message MsgRegisterInterchainAccountResponse {}

IBC Events

type MessageOnChanOpenAck struct {
OpenAck OpenAckDetails `json:"open_ack"`
}

type OpenAckDetails struct {
PortID string `json:"port_id"`
ChannelID string `json:"channel_id"`
CounterpartyChannelId string `json:"counterparty_channel_id"`
CounterpartyVersion string `json:"counterparty_version"`
}

The data from an OnChanOpenAck event is passed to the contract using a Sudo() call. You can have a look at an example handler implementation in the neutron-sdk repository.

Note: you can find the interchain account address in the stored in the CounterpartyVersion field as part of metadata.

State modifications

None.

MsgSubmitTx

Attempts to execute a transaction on a remote chain.

message MsgSubmitTx {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

string from_address = 1;
string interchain_account_id = 2;
string connection_id = 3;
repeated google.protobuf.Any msgs = 4;
string memo = 5;
uint64 timeout = 6;

neutron.feerefunder.Fee fee = 7 [ (gogoproto.nullable) = false ];
}
  • from_address must be a smart contract address, otherwise the message will fail;
  • interchain_account_id is identical to MsgRegisterInterchainAccount.interchain_account_id;
  • connection_id must be the identifier of a valid IBC connection, otherwise the message will fail;
  • msgs must contain not more than it is defined in the module params;
  • memo is the transaction memo;
  • timeout is a timeout in seconds after which the packet times out;
  • fee is a fee amount to refund relayer for ack and timeout messages submission.

Note: your smart-contract must have fee.ack_fee + fee.timeout_fee + fee.recv_fee coins on its balance, otherwise the message fails. See more info about fee refunding mechanism here.

Note: most networks reject memos longer than 256 bytes.

Note: your contract needs to implement the sudo() entrypoint on order to successfully process the IBC events associated with this message. You can find an example in the neutron-sdk repository.

Note: to see the currently available messages amount in a single MsgSubmitTx, query the module parameters:

neutrond query interchaintxs params

params:
msg_submit_tx_max_messages: "16"

Response

message MsgSubmitTxResponse {
uint64 sequence_id = 1;
string channel = 2;
}
  • sequence_id is a channel's sequence_id for outgoing ibc packet. Unique per a channel;
  • channel is the src channel name on neutron's side trasaction was submitted from;

IBC Events

// MessageSudoCallback is passed to a contract's sudo() entrypoint when an interchain
// transaction failed.
type MessageSudoCallback struct {
Response *ResponseSudoPayload `json:"response,omitempty"`
Error *ErrorSudoPayload `json:"error,omitempty"`
Timeout *TimeoutPayload `json:"timeout,omitempty"`
}

type ResponseSudoPayload struct {
Request channeltypes.Packet `json:"request"`
Data []byte `json:"data"` // Message data
}

type ErrorSudoPayload struct {
Request channeltypes.Packet `json:"request"`
Details string `json:"details"`
}

type TimeoutPayload struct {
Request channeltypes.Packet `json:"request"`
}

While trying to execute an interchain transaction, you can receive an IBC Timeout or an IBC Acknowledgement, and the latter can contain either a valid response or an error. These three types of transaction results are passed to the contract as distinct messages using a Sudo() call. You can have a look at an example handler implementation in the neutron-sdk repository.

Note: there is no dedicated event for a closed channel (ICA disables all messages related to closing the channels) . Your channel, however, can still be closed if a packet timeout occurs; thus, if you are notified about a packet timeout, you can be sure that the affected channel was closed. Please note that it is generally a good practice to set the packet timeout for your interchain transactions to a really large value.

If the timeout occurs anyway, you can just execute RegisterInterchainAccount message again to recover access to your interchain account.

Note Keep in mind, new channel is created

You can more find info, recommendations and examples about how process acknowledgements here.

State modifications

None.