-
Notifications
You must be signed in to change notification settings - Fork 597
Fee enabled fungible token transfers
This document outlines the setup of an e2e local environment and the sample usage of the fee middleware module applied to ICS20 fungible token transfers. The fee middleware module exposes two different ways to pay fees for relaying IBC packets:
- using the
MsgPayPacketFeeAsync
message, which enables the payment of fees for a packet at a specified sequence; - and using the
MsgPayPacketFee
message, which enables the payment of fees for a packet at the next sequence send and should be combined with the message that will be paid for.
The use of both methods will be demonstrated in this guide.
The IBC packet flow has two different branches:
- one that involves 2 transactions: one for the submission of the
MsgRecvPacket
message on the destination chain, and another one for the submission of theMsgAcknowledgement
message on the source chain; - and another one that involves only the submission of the
MsgTimeout
message on the source chain.
All these three messages can be submitted by different relayer processes and ICS29 allows to specify separate fees that will be paid out to the relayer addresses for submitting each of them. Fees that are not paid out (either the fee for MsgTimeout
or the fees for MsgRecvPacket
and MsgAcknowledgement
) will be reimbursed to the paying account.
- Prerequisites
- Setup
- Asynchronous incentivization of a fungible token transfer
- Synchronous incentivization of a fungible token transfer
- Install the IBC
simd
binary:
git clone [email protected]:cosmos/ibc-go.git && cd ibc-go
git checkout main
make install
- Install Hermes
v0.15.0
(this is the latest version at the moment of writing) via cargo:
cargo install --version 0.15.0 ibc-relayer-cli --bin hermes --locked
Clone the following repository containing boilerplate scripts for creating the test environment. These scripts are intended for testing purposes and simplify the process of bootstrapping two chains, a relayer and creating an IBC connection between them.
git clone [email protected]:damiannolan/simd-scripts.git && cd simd-scripts
The repository contains a basic Makefile
defining simple targets to assist in manual testing.
# bootstrap two chains and create connection
make init
# start the relayer
make start-rly
For convenience, we are going to store a few account addresses as variables in the current shell environment. Execute the following commands to store the relayer addresses on chains test-1
(account rly1
) and test-2
(account rly2
), respectively:
export RLY_1=$(simd keys show rly1 -a \
--keyring-backend test \
--home ./data/test-1) && echo $RLY_1;
export RLY_2=$(simd keys show rly2 -a \
--keyring-backend test \
--home ./data/test-2) && echo $RLY_2;
And execute also the following commands to store the wallet account addresses on chains test-1
(accounts wallet1
and wallet2
) and test-2
(account wallet3
) that we will use throughout this guide:
export WALLET_1=$(simd keys show wallet1 -a \
--keyring-backend test \
--home ./data/test-1) && echo $WALLET_1;
export WALLET_2=$(simd keys show wallet2 -a \
--keyring-backend test \
--home ./data/test-1) && echo $WALLET_2;
export WALLET_3=$(simd keys show wallet3 -a \
--keyring-backend test \
--home ./data/test-2) && echo $WALLET_3;
We can now initiate a handshake for a new fee-enabled transfer channel using the existing connection:
hermes -c ./network/hermes/config.toml create channel test-1 connection-0 \
--port-a transfer \
--port-b transfer \
-v "{\"fee_version\":\"ics29-1\",\"app_version\":\"ics20-1\"}"
Once the handshake finishes, we can query the channels on both chains to ensure the handshake completed successfully:
# query the channels on chain test-1
> simd q ibc channel channels --home ./data/test-1 --node tcp://localhost:16657
channels:
- channel_id: channel-0
connection_hops:
- connection-0
counterparty:
channel_id: channel-0
port_id: transfer
ordering: ORDER_UNORDERED
port_id: transfer
state: STATE_OPEN
version: '{"fee_version":"ics29-1","app_version":"ics20-1"}'
height:
revision_height: "52"
revision_number: "1"
pagination:
next_key: null
total: "0"
# query the channels on chain test-2
> simd q ibc channel channels --home ./data/test-2 --node tcp://localhost:26657
channels:
- channel_id: channel-0
connection_hops:
- connection-0
counterparty:
channel_id: channel-0
port_id: transfer
ordering: ORDER_UNORDERED
port_id: transfer
state: STATE_OPEN
version: '{"fee_version":"ics29-1","app_version":"ics20-1"}'
height:
revision_height: "64"
revision_number: "2"
pagination:
next_key: null
total: "0"
There is only one channel between these two chains, and we can see that the version string in both ends of the channel is {"fee_version":"ics29-1","app_version":"ics20-1"}
, which is what we expected.
As part of the fee middleware feature we have added some new queries, availabe both as CLI commands and as gRPC endpoints. In this guide we will make use of the CLI commands. We will show usage of all these queries along this guide.
We can query which channels on both chains have fee enabled:
# query the fee-enabled channels on chain test-1
> simd q ibc-fee channels --node tcp://localhost:16657
fee_enabled_channels:
- channel_id: channel-0
port_id: transfer
# query the fee-enabled channels on chain test-2
> simd q ibc-fee channels --node tcp://localhost:26657
fee_enabled_channels:
- channel_id: channel-0
port_id: transfer
We can also query for the fee-enabled status of a particular channel (i.e. by channel ID):
# query the fee-enabled status of channel with ID channel-0 on chain test-1
> simd q ibc-fee channel transfer channel-0 --chain-id test-1 --node tcp://localhost:16657
fee_enabled: true
# query the fee-enabled status of channel with ID channel-0 on chain test-2
> simd q ibc-fee channel transfer channel-0 --chain-id test-2 --node tcp://localhost:26657
fee_enabled: true
Unsurprisingly, both ends of the channel have fee enabled.
All incentivization fees are paid to accounts on the chain from where the IBC packets originate. To ensure that the relayer that delivers the MsgRecvPacket
on the destination chain is correctly compensated, the counterparty payee address (i.e. the account address of the relayer on the source chain) needs to be registered on the destination chain. Throughout this guide the source chain is test-1
and the destination chain is test-2
, therefore we need to register the account address of the relayer on chain test-1
(RLY_1
) on chain test-2
(where the relayer has the account address RLY_2
):
simd tx ibc-fee register-counterparty-payee transfer channel-0 $RLY_2 $RLY_1 \
--from $RLY_2 \
--home ./data/test-2 \
--node tcp://localhost:26657 \
--keyring-backend test \
--chain-id test-2
Once the above command succeeds, then we can verify which counterparty payee is registered on chain test-2
for account RLY_2
:
> simd q ibc-fee counterparty-payee channel-0 $RLY_2 --chain-id test-2 --node tcp://localhost:26657
counterparty_address: cosmos1mjk79fjjgpplak5wq838w0yd982gzkyfrk07am
We see that the counterparty payee address matches what we expected (i.e. the RLY_1
address). In this guide we are going to send only packets from chain test-1
to chain test-2
, so we only need to register the counterparty payee on chain test-2
. In real life circumstances relayers relay packets on both directions (i.e. from chain test-1
to test-2
and also viceversa), and thus relayers should register as well on chain test-1
the counterparty payee address to be compensated for delivering the MsgRecvPacket
on chain test-1
.
In this section we are going to pay fees using the asynchronous method, i.e. using MsgPayPacketFeeAsync
to pay for a packet at a specified sequence. The asynchronous incentivization method is meant to incentivize packets that have already been submitted on the source chain and are still waiting to be relayed. Please note that this method is NOT meant to incentivize packets that will be submitted in the future (since it is not reliable to try to predict the packet sequence). In fact, ibc-go will not allow this and will reject the transaction.
Fees for a packet can be paid paid by one or more parties. We will see an example of both in the coming sections.
We first inspect the balance of the account WALLET_1
on chain test-1
:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "100000000000"
denom: stake
pagination:
next_key: null
total: "0"
We are going to send 1000stake
from WALLET_1
on chain test-1
to the account WALLET_3
on chain test-2
. We will use the account WALLET_1
to incentivize the packet as well. In the next section we will see that it is also possible that multiple accounts incentive the same packet.
Please make sure that the relayer is not running before submitting the IBC transfer transaction. Otherwise the packet would be immediately relayed and we would not have time to submit the
MsgPayPacketFeeAsync
to incentivize it.
We send the IBC transfer from WALLET_1
to WALLET_3
:
simd tx ibc-transfer transfer transfer channel-0 $WALLET_3 1000stake \
--from $WALLET_1 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657
And we verify that the amount of 1000stake
has been escrowed (i.e. the balance of WALLET_1
has decreased by 1000stake
):
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999999000"
denom: stake
pagination:
next_key: null
total: "0"
Since we have started chains test-1
and test-2
from a clean state and we have created a new channel, we know that this is the first IBC packet that is transfered between chains test-1
and test-2
on this channel, and thus we know that we are going to incentivise sequence number 1. Nevertheless, the sequence number of a packet can be obtained by querying the transaction of the IBC transfer and inspecting the events for the send_packet
attribute and checking the value for the key packet_sequence
. The following is an extract of the relevant output for the IBC transfer transaction above:
logs:
- events:
- attributes:
- key: packet_data
value: '{"amount":"1000","denom":"stake","receiver":"cosmos14zs2x38lmkw4eqvl3lpml5l8crzaxn6m7wuznx","sender":"cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud"}'
- key: packet_data_hex
value: 7b22616d6f756e74223a2231303030222c2264656e6f6d223a227374616b65222c227265636569766572223a22636f736d6f7331347a73327833386c6d6b77346571766c336c706d6c356c3863727a61786e366d3777757a6e78222c2273656e646572223a22636f736d6f73316d396c33353878756e6868776473303536387a6134396d7a6876757878397578726535747564227d
- key: packet_timeout_height
value: 2-1255
- key: packet_timeout_timestamp
value: "1651667579045137000"
- key: packet_sequence
value: "1"
- key: packet_src_port
value: transfer
- key: packet_src_channel
value: channel-0
- key: packet_dst_port
value: transfer
- key: packet_dst_channel
value: channel-0
- key: packet_channel_ordering
value: ORDER_UNORDERED
- key: packet_connection
value: connection-0
type: send_packet
The relayer should be stopped and thus the packet should not be relayed yet. We are now going to asynchronously incentivize the packet with sequence 1 on port transfer
and channel channel-0
, signed by account WALLET_1
on chain test-1
:
# incentivize packet with 50stake for MsgRecvPacket,
# 25stake for MsgAcknowledgement and 10stake for MsgTimeout
simd tx ibc-fee pay-packet-fee transfer channel-0 1 \
--recv-fee 50stake \
--ack-fee 25stake \
--timeout-fee 10stake \
--from $WALLET_1 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657
In the case of a successful relay of the packet, the receive and acknowledgement fees will be paid out to the relayer address which has submitted the MsgRecvPacket
and MsgAcknowledgement
messages, and the timeout fee will be reimbursed to the paying account (WALLET_1
).
We check again the balance of the account WALLET_1
on chain test-1
and verify that the fees have been successfully escrowed and a total of 85stake
is deducted from account balance (balance was 99999999000stake
after the token transfer of 1000stake
).
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999998915"
denom: stake
pagination:
next_key: null
total: "0"
We can query chain test-1
to retrieve all unrelayed incentivized packets in all channels:
> simd q ibc-fee packets --node tcp://localhost:16657
incentivized_packets:
- packet_fees:
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud
relayers: []
packet_id:
channel_id: channel-0
port_id: transfer
sequence: "1"
We can also query chain test-1
to retrieve all unrelayed incentivized packets on port transfer
and channel channel-0
:
> simd q ibc-fee packets-for-channel transfer channel-0 --node tcp://localhost:16657
incentivized_packets:
- packet_fees:
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud
relayers: []
packet_id:
channel_id: channel-0
port_id: transfer
sequence: "1"
And we can even query chain test-1
to retrieve the fees for the unrelayed packet with sequence number 1 on port transfer
and channel channel-0
:
> simd q ibc-fee packet transfer channel-0 1 --node tcp://localhost:16657
incentivized_packet:
packet_fees:
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud
relayers: []
packet_id:
channel_id: channel-0
port_id: transfer
sequence: "1"
Notice that in all three sample responses above the refund_address
(cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud) matches the address of WALLET_1
, which is the account we have used to pay fees for the packet.
We can now start the relayer:
# start relaying if not previously started
make start-rly
# wait for the packet to be relayed and then stop the relayer again.
We can query the balance of account WALLET_3
on chain test-2
and see that it has indeed received an equivalent amount of vouchers for the 1000stake
sent by WALLET_1
:
# query the balance of WALLET_3 on chain test-2
> simd q bank balances $WALLET_3 --node tcp://localhost:26657
balances:
- amount: "1000"
denom: ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878
- amount: "100000000000"
denom: stake
pagination:
next_key: null
total: "0"
We can now query as well the balance of the account WALLET_1
on chain test-1
:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999998925"
denom: stake
pagination:
next_key: null
total: "0"
And we can see that the balance of the account has been reduced by 1075stake
(1000stake
transfered to WALLET_3
+ 50stake
paid for the receive fee + 25stake
paid for the acknowledgment fee). Also, the timeout fee of 10stake
has been refunded and the relayer address should have therefore gained an additional 75stake
for the receive and acknowledgement fees.
We check first the balances of the two accounts (WALLET_1
and WALLET_2
) that we are going to use to incentivize the packet:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999998925"
denom: stake
pagination:
next_key: null
total: "0"
# query the balance of WALLET_2 on chain test-1
> simd q bank balances $WALLET_2 --node tcp://localhost:16657
balances:
- amount: "100000000000"
denom: stake
pagination:
next_key: null
total: "0"
We are going to send again 1000stake
from account WALLET_1
to account WALLET_3
on chain test-2
. The balance of WALLET_3
has the equivalent amount of vouchers for the 1000stake
that was sent in the previous section.
# query the balance of WALLET_3 on chain test-2
> simd q bank balances $WALLET_3 --node tcp://localhost:26657
balances:
- amount: "1000"
denom: ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878
- amount: "100000000000"
denom: stake
pagination:
next_key: null
total: "0"
Please make sure that the relayer is not running before submitting the IBC transfer transaction. Otherwise the packet would be immediately relayed and we would not have time to use the
MsgPayPacketFeeAsync
to incentivize it.
We send the IBC transfer from WALLET_1
to WALLET_3
:
simd tx ibc-transfer transfer transfer channel-0 $WALLET_3 1000stake \
--from $WALLET_1 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657
And we verify that the amount of 1000stake
has been escrowed (i.e. the balance of WALLET_1
has decreased by 1000stake
):
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999997925"
denom: stake
pagination:
next_key: null
total: "0"
We are going now to incentivize the packet (which has sequence number 2, since this is the second packet we send over channel-0
) using two different accounts:
# pay fees with account WALLET_1
> simd tx ibc-fee pay-packet-fee transfer channel-0 2 \
--recv-fee 50stake \
--ack-fee 25stake \
--timeout-fee 10stake \
--from $WALLET_1 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657
# pay fees with account WALLET_2
> simd tx ibc-fee pay-packet-fee transfer channel-0 2 \
--recv-fee 50stake \
--ack-fee 25stake \
--timeout-fee 10stake \
--from $WALLET_2 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657
We can now check the balances of both accounts to see that the incentivization funds (85stake
in total) have been escrowed successfully:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999997840"
denom: stake
pagination:
next_key: null
total: "0"
# query the balance of WALLET_2 on chain test-1
> simd q bank balances $WALLET_2 --node tcp://localhost:16657
balances:
- amount: "99999999915"
denom: stake
pagination:
next_key: null
total: "0"
We can query chain test-1
to retrieve all unrelayed incentivized packets on port transfer
and channel channel-0
:
> simd q ibc-fee packets-for-channel transfer channel-0 --node tcp://localhost:16657
incentivized_packets:
packet_fees:
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw
relayers: []
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud
relayers: []
packet_id:
channel_id: channel-0
port_id: transfer
sequence: "2"
We see that packet with sequence number 2 has two different fees attached to it, and the refund addresses are the addresses for WALLET_1
and WALLET_2
. We can also query chain test-1
to check if the specific packet with sequence number 2 on port transfer
and channel channel-0
has any fees:
> simd q ibc-fee packet transfer channel-0 2 --node tcp://localhost:16657
incentivized_packet:
packet_fees:
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw
relayers: []
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud
relayers: []
packet_id:
channel_id: channel-0
port_id: transfer
sequence: "2"
We can also use different queries to retrieve the total amount escrowed for the different messages. For MsgRecvPacket
:
> simd q ibc-fee total-recv-fees transfer channel-0 2 --node http://localhost:16657
recv_fees:
- amount: "100"
denom: stake
For MsgAcknowledgement
:
> simd q ibc-fee total-ack-fees transfer channel-0 2 --node http://localhost:16657
ack_fees:
- amount: "50"
denom: stake
For MsgTimeout
:
> simd q ibc-fee total-timeout-fees transfer channel-0 2 --node http://localhost:16657
timeout_fees:
- amount: "20"
denom: stake
The default timeout of the IBC packet is 10 minutes, so we can either wait that amount of time before starting the relayer:
# start relaying if not previously started
make start-rly
#wait for the packet to be timed out and then stop the relayer again.
or using the --packet-timeout-height
flag set a height which is guaranteed to timeout in the original transfer tx:
simd tx ibc-transfer transfer transfer channel-0 $WALLET_3 1000stake \
--from $WALLET_1 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657 \
--packet-timeout-height 0-3
Once the packet has timed out, we query the balances of accounts WALLET_1
and WALLET_2
:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999998915"
denom: stake
pagination:
next_key: null
total: "0"
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_2 --node tcp://localhost:16657
balances:
- amount: "99999999990"
denom: stake
pagination:
next_key: null
total: "0"
And we can verify that only an amout of 10stake
has been deducted from the balances of WALLET_1
and WALLET_2
comparing to the balances that they had before the IBC packet was submitted and the fees escrowed. And at the same time, the relayer account RLY_1
should have gained 20stake
from submitting MsgTimeout
on chain test-1
.
In this section we are going to pay fees using the synchronous method, i.e. using MsgPayPacketFee
to incentivize the IBC packet contained in the same transaction.
Similarly as with the asynchronous method, fees for a packet can be paid by one or more parties. We will see an example of both in the coming sections.
We begin by inspecting the balance of the account WALLET_1
, which is again the account we are going to use to incentivize tha packet:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999998915"
denom: stake
pagination:
next_key: null
total: "0"
Since we are going to broadcast a multi-message transaction containing the messages both for the incentivization of the packet and the IBC packet itself, there would be no need to stop the relayer to prevent it from relaying the IBC packet before we are able to incentivizate it, as in the asynchronous method. However, we stop the relayer now so that we are able to query the channel for the fee information for unrelayed incentivized packets.
We first generate (not execute) an IBC transfer transaction (again 1000stake
from WALLET_1
to WALLET_3
):
simd tx ibc-transfer transfer transfer channel-0 $WALLET_3 1000stake \
--from $WALLET_1 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657 \
--generate-only > transfer.json
Then we prepend a MsgPayPacketFee
, sign the transaction and broadcast it. Please note that jq
is used to manipulate the transaction JSON file, making it a multi-message transaction. In practice, this multi-message transaction would be built using a gRPC or web client, for example, a web-based wallet application could fulfill this role. Note also that the signer
field uses the address of WALLET_1
.
jq '.body.messages |= [{"@type":"/ibc.applications.fee.v1.MsgPayPacketFee","fee": {"recv_fee": [{"denom": "stake", "amount": "50"}], "ack_fee": [{"denom": "stake", "amount": "25"}], "timeout_fee": [{"denom": "stake", "amount": "10"}]}, "source_port_id": "transfer", "source_channel_id": "channel-0", "signer": "cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud" }] + .' transfer.json > incentivized_transfer.json
simd tx sign incentivized_transfer.json \
--from $WALLET_1 \
--keyring-backend test \
--chain-id test-1 \
--home ./data/test-1 \
--node tcp://localhost:16657 > signed.json
simd tx broadcast signed.json \
--home ./data/test-1 \
--chain-id test-1 \
--node tcp://localhost:16657
We now query chain test-1
to retrieve all unrelayed incentivized packets on port transfer
and channel channel-0
:
> simd q ibc-fee packets-for-channel transfer channel-0 --node tcp://localhost:16657
incentivized_packets:
- packet_fees:
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud
relayers: []
packet_id:
channel_id: channel-0
port_id: transfer
sequence: "3"
The information matches what we expect (i.e. fees paid by WALLET_1
for the next IBC packet, which has sequence number 3). We can now start the relayer:
# start relaying if not previously started
make start-rly
# wait for the packet to be relayed and then stop the relayer again.
We can query the balance of account WALLET_3
on chain test-2
and see that it has indeed received an equivalent amount of vouchers for the 1000stake
sent by WALLET_1
:
# query the balance of WALLET_3 on chain test-2
> simd q bank balances $WALLET_3 --node tcp://localhost:26657
balances:
- amount: "2000"
denom: ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878
- amount: "100000000000"
denom: stake
pagination:
next_key: null
total: "0"
We check as well the balance for account WALLET_1
on chain test-1
:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999997840"
denom: stake
pagination:
next_key: null
total: "0"
An amount of 1075stake
has been deducted, which is what we expected: 1000stake
have been transfered to WALLET_3
and 75stake
have been paid othe receive and acknowledgment fees. The timeout fee has been refunded to WALLET_1
and the relayer address RLY_1
should have gained 75stake
for submitting the MsgRecvPacket
and the MsgAcknowledgement
messages.
We check first the balances of the two accounts (WALLET_1
and WALLET_2
) that we are going to use to incentivize the packet:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999997840"
denom: stake
pagination:
next_key: null
total: "0"
# query the balance of WALLET_2 on chain test-1
> simd q bank balances $WALLET_2 --node tcp://localhost:16657
balances:
- amount: "99999999990"
denom: stake
pagination:
next_key: null
total: "0"
Please stop the relayer now so that we are able to query the channel for the fee information for unrelayed incentivized packets.
Similarly as in the previous section we first generate (not execute) an IBC transfer transaction (again 1000stake
from WALLET_1
to WALLET_3
):
simd tx ibc-transfer transfer transfer channel-0 $WALLET_3 1000stake \
--from $WALLET_1 \
--home ./data/test-1 \
--keyring-backend test \
--chain-id test-1 \
--node tcp://localhost:16657 \
--generate-only > transfer.json
Using again jq
we are going to prepend this time two MsgPayPacketFee
messages to the transaction JSON file, making it a multi-message transaction:
jq '.body.messages |= [{"@type":"/ibc.applications.fee.v1.MsgPayPacketFee","fee": {"recv_fee": [{"denom": "stake", "amount": "50"}], "ack_fee": [{"denom": "stake", "amount": "25"}], "timeout_fee": [{"denom": "stake", "amount": "10"}]}, "source_port_id": "transfer", "source_channel_id": "channel-0", "signer": "cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud" },{"@type":"/ibc.applications.fee.v1.MsgPayPacketFee","fee": {"recv_fee": [{"denom": "stake", "amount": "50"}], "ack_fee": [{"denom": "stake", "amount": "25"}], "timeout_fee": [{"denom": "stake", "amount": "10"}]}, "source_port_id": "transfer", "source_channel_id": "channel-0", "signer": "cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw" }] + .' transfer.json > incentivized_transfer.json
The signer of the first MsgPayPacketFee
message is the address of WALLET_1
and the signer of the second MsgPayPacketFee
message is the address of WALLET_2
.
Now we need to sign the transaction, and since we need to sign it with both WALLET_1
and WALLET_2
accounts, we need to use the flag --sign-mode amino-json
. The first signer (WALLET_1
) will sign the unsigned transaction file (incentivized_transfer.json
), but the second signer (WALLET_2
) will sign the file signed by previous signer (signed_wallet1.json
):
# sign transaction with `WALLET_1` account
simd tx sign incentivized_transfer.json \
--from $WALLET_1 \
--sign-mode amino-json \
--keyring-backend test \
--chain-id test-1 \
--home ./data/test-1 \
--node tcp://localhost:16657 > signed_wallet1.json
# sign transaction with `WALLET_2` account
simd tx sign signed_wallet1.json \
--from $WALLET_2 \
--sign-mode amino-json \
--keyring-backend test \
--chain-id test-1 \
--home ./data/test-1 \
--node tcp://localhost:16657 > signed_wallet1_and_wallet2.json
And finally we broadcast the signed transaction:
simd tx broadcast signed_wallet1_and_wallet2.json \
--home ./data/test-1 \
--chain-id test-1 \
--node tcp://localhost:16657
We now query chain test-1
to retrieve all unrelayed incentivized packets on port transfer
and channel channel-0
:
> simd q ibc-fee packets-for-channel transfer channel-0 --node tcp://localhost:16657
incentivized_packets:
- packet_fees:
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos10h9stc5v6ntgeygf5xf945njqq5h32r53uquvw
relayers: []
- fee:
ack_fee:
- amount: "25"
denom: stake
recv_fee:
- amount: "50"
denom: stake
timeout_fee:
- amount: "10"
denom: stake
refund_address: cosmos1m9l358xunhhwds0568za49mzhvuxx9uxre5tud
relayers: []
packet_id:
channel_id: channel-0
port_id: transfer
sequence: "4"
This is the information we expected, with two fees attached for the next packet (sequence number 4). We query the balances of the accounts used for paying fees:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999996755"
denom: stake
pagination:
next_key: null
total: "0"
# query the balance of WALLET_2 on chain test-1
> simd q bank balances $WALLET_2 --node tcp://localhost:16657
balances:
- amount: "99999999905"
denom: stake
pagination:
next_key: null
total: "0"
We see that an amount of 1085stake
has been deducted from the balance of WALLET_1
(1000stake
that shoud be transfered to WALLET_3
+ 85stake
for all fees) and an amount of 85stake
has been deducted from the balance of WALLET_3
also for the fees. These amounts are in escrow at the moment, since the relayer is stopped and the packet has not been relayed yet.
We wait enought time to make sure that the IBC packet will time out before starting the relayer:
# start relaying if not previously started
make start-rly
#wait for the packet to be timed out and then stop the relayer again.
Once the packet has timed out, we query again the balances of accounts WALLET_1
and WALLET_2
:
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_1 --node tcp://localhost:16657
balances:
- amount: "99999997830"
denom: stake
pagination:
next_key: null
total: "0"
# query the balance of WALLET_1 on chain test-1
> simd q bank balances $WALLET_2 --node tcp://localhost:16657
balances:
- amount: "99999999980"
denom: stake
pagination:
next_key: null
total: "0"
And we can verify that only an amout of 10stake
has been deducted from the balances of WALLET_1
and WALLET_2
comparing to the balances that they had before the IBC packet was submitted and the fees escrowed. The other fees have been refunded, as well as the 1000stake
that WALLET_1
intended to transfer to WALLET_3
. And at the same time, the relayer account RLY_1
should have gained 20stake
for submitting the MsgTimeout
on chain test-1
.