diff --git a/go.mod b/go.mod index d0161e9288..b9f0c22e72 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/CosmWasm/wasmd go 1.23.1 require ( - github.com/CosmWasm/wasmvm/v2 v2.2.1 + github.com/CosmWasm/wasmvm/v2 v2.2.2-0.20250207160143-733babc9015a github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.11 github.com/cosmos/gogogateway v1.2.0 // indirect diff --git a/go.sum b/go.sum index e7ec8cd41d..6968e117d3 100644 --- a/go.sum +++ b/go.sum @@ -227,8 +227,12 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CosmWasm/wasmvm/v2 v2.2.1 h1:cmOnM+TDfUl2VRugeo1eJBw4U/Lw0WLviuQHKSo9DVQ= -github.com/CosmWasm/wasmvm/v2 v2.2.1/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= +github.com/CosmWasm/wasmvm/v2 v2.2.2-0.20250203131529-d31a37c53d81 h1:G8P/Fah2W0Cf/FDBM3IczkVg9Ym6GsTpbIUbegPi50w= +github.com/CosmWasm/wasmvm/v2 v2.2.2-0.20250203131529-d31a37c53d81/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= +github.com/CosmWasm/wasmvm/v2 v2.2.2-0.20250207154024-eaca88056c1b h1:oP4ereVjXqsMhoYXj5MW9y/5YT6YiW7RIXTbxgn+els= +github.com/CosmWasm/wasmvm/v2 v2.2.2-0.20250207154024-eaca88056c1b/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= +github.com/CosmWasm/wasmvm/v2 v2.2.2-0.20250207160143-733babc9015a h1:UF88iJZK+/MsImDRFrjma+yXPOfDl3mcCzxiTr0dmSk= +github.com/CosmWasm/wasmvm/v2 v2.2.2-0.20250207160143-733babc9015a/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= diff --git a/tests/e2e/ibc_callbacks_test.go b/tests/e2e/ibc_callbacks_test.go index 30c194f527..f17937427a 100644 --- a/tests/e2e/ibc_callbacks_test.go +++ b/tests/e2e/ibc_callbacks_test.go @@ -64,6 +64,7 @@ func TestIBCCallbacks(t *testing.T) { ToAddress string `json:"to_address"` ChannelID string `json:"channel_id"` TimeoutSeconds uint32 `json:"timeout_seconds"` + ChannelVersion string `json:"channel_version"` } // ExecuteMsg is the ibc-callbacks contract's execute msg type ExecuteMsg struct { @@ -88,6 +89,7 @@ func TestIBCCallbacks(t *testing.T) { Transfer: &TransferExecMsg{ ChannelID: path.EndpointA.ChannelID, TimeoutSeconds: 100, + ChannelVersion: "v2", }, }, expAck: true, @@ -97,6 +99,7 @@ func TestIBCCallbacks(t *testing.T) { Transfer: &TransferExecMsg{ ChannelID: path.EndpointA.ChannelID, TimeoutSeconds: 1, + ChannelVersion: "v2", }, }, expAck: false, diff --git a/tests/e2e/testdata/ibc_callbacks.wasm b/tests/e2e/testdata/ibc_callbacks.wasm index 63519505aa..5a34e66726 100644 Binary files a/tests/e2e/testdata/ibc_callbacks.wasm and b/tests/e2e/testdata/ibc_callbacks.wasm differ diff --git a/tests/integration/module_test.go b/tests/integration/module_test.go index 8edd94fa67..5a9a3735ea 100644 --- a/tests/integration/module_test.go +++ b/tests/integration/module_test.go @@ -66,7 +66,7 @@ func setupTest(t *testing.T) testData { ctx, keepers := keeper.CreateTestInput(t, false, []string{ "iterator", "staking", "stargate", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3", - "cosmwasm_1_4", "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2", + "cosmwasm_1_4", "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2", "cosmwasm_3_0", }) encConf := keeper.MakeEncodingConfig(t) queryRouter := baseapp.NewGRPCQueryRouter() diff --git a/x/wasm/keeper/capabilities.go b/x/wasm/keeper/capabilities.go index 47fbf2f7db..8aca980bab 100644 --- a/x/wasm/keeper/capabilities.go +++ b/x/wasm/keeper/capabilities.go @@ -18,5 +18,6 @@ func BuiltInCapabilities() []string { "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2", + "cosmwasm_3_0", } } diff --git a/x/wasm/keeper/handler_plugin_encoders.go b/x/wasm/keeper/handler_plugin_encoders.go index e5c17a949c..88c8426899 100644 --- a/x/wasm/keeper/handler_plugin_encoders.go +++ b/x/wasm/keeper/handler_plugin_encoders.go @@ -321,6 +321,57 @@ func EncodeIBCMsg(portSource types.ICS20TransferPortSource) func(ctx sdk.Context Memo: msg.Transfer.Memo, } return []sdk.Msg{msg}, nil + case msg.TransferV2 != nil: + tokens, err := ConvertWasmCoinsToSdkCoins(msg.TransferV2.Tokens) + if err != nil { + return nil, errorsmod.Wrap(err, "amount") + } + msgTransferV2 := msg.TransferV2.TransferType + switch { + case msgTransferV2.Direct != nil: + msg := &ibctransfertypes.MsgTransfer{ + SourcePort: portSource.GetPort(ctx), + SourceChannel: msgTransferV2.Direct.ChannelID, + Tokens: tokens, + Sender: sender.String(), + Receiver: msg.TransferV2.ToAddress, + TimeoutHeight: ConvertWasmIBCTimeoutHeightToCosmosHeight(msgTransferV2.Direct.IBCTimeout.Block), + TimeoutTimestamp: msgTransferV2.Direct.IBCTimeout.Timestamp, + Memo: msg.TransferV2.Memo, + } + return []sdk.Msg{msg}, nil + case msgTransferV2.MultiHop != nil: + msg := &ibctransfertypes.MsgTransfer{ + SourcePort: portSource.GetPort(ctx), + SourceChannel: msgTransferV2.MultiHop.ChannelID, + Tokens: tokens, + Sender: sender.String(), + Receiver: msg.TransferV2.ToAddress, + TimeoutTimestamp: uint64(msgTransferV2.MultiHop.Timeout), + Memo: msg.TransferV2.Memo, + Forwarding: &ibctransfertypes.Forwarding{ + Hops: ConvertWasmHopsToIbcHops(msgTransferV2.MultiHop.Hops), + Unwind: false, + }, + } + return []sdk.Msg{msg}, nil + case msgTransferV2.Unwinding != nil: + msg := &ibctransfertypes.MsgTransfer{ + SourcePort: portSource.GetPort(ctx), + Tokens: tokens, + Sender: sender.String(), + Receiver: msg.TransferV2.ToAddress, + TimeoutTimestamp: uint64(msgTransferV2.Unwinding.Timeout), + Memo: msg.TransferV2.Memo, + Forwarding: &ibctransfertypes.Forwarding{ + Hops: ConvertWasmHopsToIbcHops(msgTransferV2.Unwinding.Hops), + Unwind: true, + }, + } + return []sdk.Msg{msg}, nil + default: + return nil, errorsmod.Wrap(types.ErrUnknownMsg, "unknown variant of IBC TransferV2 message") + } case msg.PayPacketFee != nil: fee, err := ConvertIBCFee(&msg.PayPacketFee.Fee) if err != nil { @@ -422,6 +473,16 @@ func ConvertWasmCoinsToSdkCoins(coins []wasmvmtypes.Coin) (sdk.Coins, error) { return toSend.Sort(), nil } +// ConvertWasmHopsToIbcHops converts the wasm []hop type to IBC type []hop +func ConvertWasmHopsToIbcHops(hops []wasmvmtypes.Hop) []ibctransfertypes.Hop { + forwardingHops := []ibctransfertypes.Hop{} + for _, hop := range hops { + newHop := ibctransfertypes.NewHop(hop.PortID, hop.ChannelID) + forwardingHops = append(forwardingHops, newHop) + } + return forwardingHops +} + // ConvertWasmCoinToSdkCoin converts a wasm vm type coin to sdk type coin func ConvertWasmCoinToSdkCoin(coin wasmvmtypes.Coin) (sdk.Coin, error) { amount, ok := sdkmath.NewIntFromString(coin.Amount) diff --git a/x/wasm/keeper/handler_plugin_encoders_test.go b/x/wasm/keeper/handler_plugin_encoders_test.go index c353948ab1..d8c4d0c2e2 100644 --- a/x/wasm/keeper/handler_plugin_encoders_test.go +++ b/x/wasm/keeper/handler_plugin_encoders_test.go @@ -694,6 +694,161 @@ func TestEncodeIbcMsg(t *testing.T) { }, }, }, + "TransferV2 direct": { + sender: addr1, + srcContractIBCPort: "myIBCPort", + srcMsg: wasmvmtypes.CosmosMsg{ + IBC: &wasmvmtypes.IBCMsg{ + TransferV2: &wasmvmtypes.TransferV2Msg{ + TransferType: wasmvmtypes.TransferV2Type{ + Direct: &wasmvmtypes.DirectType{ + ChannelID: "myChanID", + IBCTimeout: wasmvmtypes.IBCTimeout{Timestamp: 100}, + }, + }, + ToAddress: addr2.String(), + Tokens: []wasmvmtypes.Coin{ + { + Denom: "TK1", + Amount: "1", + }, + { + Denom: "TK2", + Amount: "12", + }, + }, + Memo: "myMemo", + }, + }, + }, + transferPortSource: wasmtesting.MockIBCTransferKeeper{GetPortFn: func(ctx sdk.Context) string { + return "myTransferPort" + }}, + output: []sdk.Msg{ + &ibctransfertypes.MsgTransfer{ + SourcePort: "myTransferPort", + SourceChannel: "myChanID", + Tokens: []sdk.Coin{ + { + Denom: "TK1", + Amount: sdkmath.NewInt(1), + }, + { + Denom: "TK2", + Amount: sdkmath.NewInt(12), + }, + }, + Sender: addr1.String(), + Receiver: addr2.String(), + TimeoutTimestamp: 100, + Memo: "myMemo", + }, + }, + }, + "TransferV2 with forwarding": { + sender: addr1, + srcContractIBCPort: "myIBCPort", + srcMsg: wasmvmtypes.CosmosMsg{ + IBC: &wasmvmtypes.IBCMsg{ + TransferV2: &wasmvmtypes.TransferV2Msg{ + TransferType: wasmvmtypes.TransferV2Type{ + MultiHop: &wasmvmtypes.MultiHopType{ + ChannelID: "myChanID", + Timeout: 100, + Hops: []wasmvmtypes.Hop{{ChannelID: "chnl1", PortID: "port1"}}, + }, + }, + ToAddress: addr2.String(), + Tokens: []wasmvmtypes.Coin{ + { + Denom: "TK1", + Amount: "1", + }, + { + Denom: "TK2", + Amount: "12", + }, + }, + Memo: "myMemo", + }, + }, + }, + transferPortSource: wasmtesting.MockIBCTransferKeeper{GetPortFn: func(ctx sdk.Context) string { + return "myTransferPort" + }}, + output: []sdk.Msg{ + &ibctransfertypes.MsgTransfer{ + SourcePort: "myTransferPort", + SourceChannel: "myChanID", + Tokens: []sdk.Coin{ + { + Denom: "TK1", + Amount: sdkmath.NewInt(1), + }, + { + Denom: "TK2", + Amount: sdkmath.NewInt(12), + }, + }, + Sender: addr1.String(), + Receiver: addr2.String(), + TimeoutTimestamp: 100, + Memo: "myMemo", + Forwarding: &ibctransfertypes.Forwarding{Hops: []ibctransfertypes.Hop{{ChannelId: "chnl1", PortId: "port1"}}, Unwind: false}, + }, + }, + }, + "TransferV2 with unwinding": { + sender: addr1, + srcContractIBCPort: "myIBCPort", + srcMsg: wasmvmtypes.CosmosMsg{ + IBC: &wasmvmtypes.IBCMsg{ + TransferV2: &wasmvmtypes.TransferV2Msg{ + TransferType: wasmvmtypes.TransferV2Type{ + Unwinding: &wasmvmtypes.UnwindingType{ + Timeout: 100, + Hops: []wasmvmtypes.Hop{{ChannelID: "chnl1", PortID: "port1"}}, + }, + }, + ToAddress: addr2.String(), + Tokens: []wasmvmtypes.Coin{ + { + Denom: "TK1", + Amount: "1", + }, + { + Denom: "TK2", + Amount: "12", + }, + }, + Memo: "myMemo", + }, + }, + }, + transferPortSource: wasmtesting.MockIBCTransferKeeper{GetPortFn: func(ctx sdk.Context) string { + return "myTransferPort" + }}, + output: []sdk.Msg{ + &ibctransfertypes.MsgTransfer{ + SourcePort: "myTransferPort", + Tokens: []sdk.Coin{ + { + Denom: "TK1", + Amount: sdkmath.NewInt(1), + }, + { + Denom: "TK2", + Amount: sdkmath.NewInt(12), + }, + }, + Sender: addr1.String(), + Receiver: addr2.String(), + TimeoutTimestamp: 100, + Memo: "myMemo", + Forwarding: &ibctransfertypes.Forwarding{Hops: []ibctransfertypes.Hop{{ChannelId: "chnl1", PortId: "port1"}}, Unwind: true}, + }, + }, + }, } encodingConfig := MakeEncodingConfig(t) for name, tc := range cases { diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index 5cc9c35193..bce7835b54 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -53,7 +53,7 @@ var replierWasm []byte var AvailableCapabilities = []string{ "iterator", "staking", "stargate", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3", - "cosmwasm_1_4", "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2", + "cosmwasm_1_4", "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2", "cosmwasm_3_0", } func TestNewKeeper(t *testing.T) {