Skip to content

Commit

Permalink
Add ibc fee support
Browse files Browse the repository at this point in the history
  • Loading branch information
chipshort committed Aug 5, 2024
1 parent e3aefc2 commit ac66f4d
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
121 changes: 121 additions & 0 deletions tests/e2e/ibc_fees_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/CosmWasm/wasmd/app"
wasmibctesting "github.com/CosmWasm/wasmd/x/wasm/ibctesting"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
)

func TestIBCFeesTransfer(t *testing.T) {
Expand Down Expand Up @@ -219,3 +220,123 @@ func TestIBCFeesWasm(t *testing.T) {
payeeBalance = chainB.AllBalances(payee)
assert.Equal(t, sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(2)).String(), payeeBalance.String())
}

func TestIBCFeesReflect(t *testing.T) {
// scenario:
// given 2 chains with reflect on chain A
// and an ibc channel established
// when ibc-reflect sends a PayPacketFee and a PayPacketFeeAsync msg
// then the relayer's payee is receiving the fee(s) on success

marshaler := app.MakeEncodingConfig(t).Codec
coord := wasmibctesting.NewCoordinator(t, 2)
chainA := coord.GetChain(wasmibctesting.GetChainID(1))
chainB := coord.GetChain(ibctesting.GetChainID(2))
actorChainA := sdk.AccAddress(chainA.SenderPrivKey.PubKey().Address())
actorChainB := sdk.AccAddress(chainB.SenderPrivKey.PubKey().Address())

// setup chain A
codeID := chainA.StoreCodeFile("./testdata/reflect_2_2.wasm").CodeID

initMsg := []byte("{}")
reflectContractAddr := chainA.InstantiateContract(codeID, initMsg)

payee := sdk.AccAddress(bytes.Repeat([]byte{2}, address.Len))
oneToken := []wasmvmtypes.Coin{wasmvmtypes.NewCoin(1, sdk.DefaultBondDenom)}

path := wasmibctesting.NewPath(chainA, chainB)
path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{
PortID: ibctransfertypes.PortID,
Version: string(marshaler.MustMarshalJSON(&ibcfee.Metadata{FeeVersion: ibcfee.Version, AppVersion: ibctransfertypes.Version})),
Order: channeltypes.UNORDERED,
}
path.EndpointB.ChannelConfig = &ibctesting.ChannelConfig{
PortID: ibctransfertypes.PortID,
Version: string(marshaler.MustMarshalJSON(&ibcfee.Metadata{FeeVersion: ibcfee.Version, AppVersion: ibctransfertypes.Version})),
Order: channeltypes.UNORDERED,
}
// with an ics-29 fee enabled channel setup between both chains
coord.Setup(path)
appA := chainA.App.(*app.WasmApp)
appB := chainB.App.(*app.WasmApp)
require.True(t, appA.IBCFeeKeeper.IsFeeEnabled(chainA.GetContext(), ibctransfertypes.PortID, path.EndpointA.ChannelID))
require.True(t, appB.IBCFeeKeeper.IsFeeEnabled(chainB.GetContext(), ibctransfertypes.PortID, path.EndpointB.ChannelID))
// and with a payee registered for A -> B
_, err := chainA.SendMsgs(ibcfee.NewMsgRegisterPayee(ibctransfertypes.PortID, path.EndpointA.ChannelID, actorChainA.String(), payee.String()))
require.NoError(t, err)
_, err = chainB.SendMsgs(ibcfee.NewMsgRegisterCounterpartyPayee(ibctransfertypes.PortID, path.EndpointB.ChannelID, actorChainB.String(), payee.String()))
require.NoError(t, err)

// when reflect contract on A sends a PayPacketFee msg, followed by a transfer
_, err = ExecViaReflectContract(t, chainA, reflectContractAddr, []wasmvmtypes.CosmosMsg{
{
IBC: &wasmvmtypes.IBCMsg{
PayPacketFee: &wasmvmtypes.PayPacketFeeMsg{
Fee: wasmvmtypes.IBCFee{
AckFee: oneToken,
RecvFee: oneToken,
TimeoutFee: []wasmvmtypes.Coin{},
},
Relayers: []string{},
Src: wasmvmtypes.IBCEndpoint{
PortID: ibctransfertypes.PortID,
ChannelID: path.EndpointA.ChannelID,
},
},
},
},
{
IBC: &wasmvmtypes.IBCMsg{
Transfer: &wasmvmtypes.TransferMsg{
ChannelID: path.EndpointA.ChannelID,
ToAddress: actorChainB.String(),
Amount: wasmvmtypes.NewCoin(10, sdk.DefaultBondDenom),
Timeout: wasmvmtypes.IBCTimeout{
Timestamp: 9999999999999999999,
},
},
},
},
})
require.NoError(t, err)

pendingIncentivisedPackages := appA.IBCFeeKeeper.GetIdentifiedPacketFeesForChannel(chainA.GetContext(), ibctransfertypes.PortID, path.EndpointA.ChannelID)
assert.Len(t, pendingIncentivisedPackages, 1)

// and sends an PayPacketFeeAsync msg
_, err = ExecViaReflectContract(t, chainA, reflectContractAddr, []wasmvmtypes.CosmosMsg{
{
IBC: &wasmvmtypes.IBCMsg{
PayPacketFeeAsync: &wasmvmtypes.PayPacketFeeAsyncMsg{
Fee: wasmvmtypes.IBCFee{
AckFee: []wasmvmtypes.Coin{},
RecvFee: oneToken,
TimeoutFee: oneToken,
},
Relayers: []string{},
Sequence: pendingIncentivisedPackages[0].PacketId.Sequence,
Src: wasmvmtypes.IBCEndpoint{
PortID: ibctransfertypes.PortID,
ChannelID: path.EndpointA.ChannelID,
},
},
},
},
})
require.NoError(t, err)

// and packages relayed
require.NoError(t, coord.RelayAndAckPendingPackets(path))

// then
// on chain A
payeeBalance := chainA.AllBalances(payee)
// 2 tokens from the PayPacketFee and 1 token from the PayPacketFeeAsync
assert.Equal(t, sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(3)).String(), payeeBalance.String())
// and on chain B
pendingIncentivisedPackages = appA.IBCFeeKeeper.GetIdentifiedPacketFeesForChannel(chainA.GetContext(), ibctransfertypes.PortID, path.EndpointA.ChannelID)
assert.Len(t, pendingIncentivisedPackages, 0)
expBalance := ibctransfertypes.GetTransferCoin(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.DefaultBondDenom, sdkmath.NewInt(10))
gotBalance := chainB.Balance(actorChainB, expBalance.Denom)
assert.Equal(t, expBalance.String(), gotBalance.String(), chainB.AllBalances(actorChainB))
}
Binary file added tests/e2e/testdata/reflect_2_2.wasm
Binary file not shown.
48 changes: 48 additions & 0 deletions x/wasm/keeper/handler_plugin_encoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
ibcfeetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types"
ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
Expand Down Expand Up @@ -320,6 +321,33 @@ func EncodeIBCMsg(portSource types.ICS20TransferPortSource) func(ctx sdk.Context
Memo: msg.Transfer.Memo,
}
return []sdk.Msg{msg}, nil
case msg.PayPacketFee != nil:
fee, err := ConvertIBCFee(&msg.PayPacketFee.Fee)
if err != nil {
return nil, errorsmod.Wrap(err, "fee")
}
msg := &ibcfeetypes.MsgPayPacketFee{
Fee: fee,
SourcePortId: msg.PayPacketFee.Src.PortID,
SourceChannelId: msg.PayPacketFee.Src.ChannelID,
Signer: sender.String(),
Relayers: msg.PayPacketFee.Relayers,
}
return []sdk.Msg{msg}, nil
case msg.PayPacketFeeAsync != nil:
fee, err := ConvertIBCFee(&msg.PayPacketFeeAsync.Fee)
if err != nil {
return nil, errorsmod.Wrap(err, "fee")
}
msg := &ibcfeetypes.MsgPayPacketFeeAsync{
PacketId: channeltypes.PacketId{
PortId: msg.PayPacketFeeAsync.Src.PortID,
ChannelId: msg.PayPacketFeeAsync.Src.ChannelID,
Sequence: msg.PayPacketFeeAsync.Sequence,
},
PacketFee: ibcfeetypes.NewPacketFee(fee, sender.String(), msg.PayPacketFeeAsync.Relayers),
}
return []sdk.Msg{msg}, nil
default:
return nil, errorsmod.Wrap(types.ErrUnknownMsg, "unknown variant of IBC")
}
Expand Down Expand Up @@ -406,3 +434,23 @@ func ConvertWasmCoinToSdkCoin(coin wasmvmtypes.Coin) (sdk.Coin, error) {
}
return r, r.Validate()
}

func ConvertIBCFee(fee *wasmvmtypes.IBCFee) (ibcfeetypes.Fee, error) {
ackFee, err := ConvertWasmCoinsToSdkCoins(fee.AckFee)
if err != nil {
return ibcfeetypes.Fee{}, err
}
recvFee, err := ConvertWasmCoinsToSdkCoins(fee.RecvFee)
if err != nil {
return ibcfeetypes.Fee{}, err
}
timeoutFee, err := ConvertWasmCoinsToSdkCoins(fee.TimeoutFee)
if err != nil {
return ibcfeetypes.Fee{}, err
}
return ibcfeetypes.Fee{
AckFee: ackFee,
RecvFee: recvFee,
TimeoutFee: timeoutFee,
}, nil
}

0 comments on commit ac66f4d

Please sign in to comment.