diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 02b74cd2..43283383 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -32,6 +32,9 @@ jobs: - TestIcaContractTimeoutPacket - TestOwnerCreateIcaContract - TestOwnerPredefinedAction + - TestSendCosmosMsgsProto3JsonEncoding + - TestSendCosmosMsgsProtobufEncoding + - TestSendWasmMsgsProtobufEncoding name: ${{ matrix.test }} runs-on: ubuntu-latest steps: diff --git a/e2e/interchaintest/contract_test.go b/e2e/interchaintest/contract_test.go index 19c41aeb..0e28f805 100644 --- a/e2e/interchaintest/contract_test.go +++ b/e2e/interchaintest/contract_test.go @@ -477,6 +477,221 @@ func (s *ContractTestSuite) IcaContractExecutionTestWithEncoding(encoding string }) } +func (s *ContractTestSuite) TestSendCosmosMsgsProto3JsonEncoding() { + s.SendCosmosMsgsTestWithEncoding(icatypes.EncodingProto3JSON) +} + +func (s *ContractTestSuite) TestSendCosmosMsgsProtobufEncoding() { + s.SendCosmosMsgsTestWithEncoding(icatypes.EncodingProtobuf) +} + +// SendCosmosMsgsTestWithEncoding tests some more CosmosMsgs that are not covered by the IcaContractExecutionTestWithEncoding. +// The following CosmosMsgs are tested here: +// +// - Bank::Send +// - Stargate +// - VoteWeighted +// - SetWithdrawAddress +func (s *ContractTestSuite) SendCosmosMsgsTestWithEncoding(encoding string) { + ctx := context.Background() + + // This starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, + // sets up the contract and does the channel handshake for the contract test suite. + s.SetupContractTestSuite(ctx, encoding) + wasmd, simd := s.ChainA, s.ChainB + wasmdUser := s.UserA + simdUser := s.UserB + + // Fund the ICA address: + s.FundAddressChainB(ctx, s.IcaAddress) + + s.Run(fmt.Sprintf("TestStargate-%s", encoding), func() { + // Send custom ICA messages through the contract: + // Let's create a governance proposal on simd and deposit some funds to it. + testProposal := govtypes.TextProposal{ + Title: "IBC Gov Proposal", + Description: "tokens for all!", + } + protoAny, err := codectypes.NewAnyWithValue(&testProposal) + s.Require().NoError(err) + proposalMsg := &govtypes.MsgSubmitProposal{ + Content: protoAny, + InitialDeposit: sdk.NewCoins(sdk.NewCoin(simd.Config().Denom, sdkmath.NewInt(5_000))), + Proposer: s.IcaAddress, + } + + // Create deposit message: + depositMsg := &govtypes.MsgDeposit{ + ProposalId: 1, + Depositor: s.IcaAddress, + Amount: sdk.NewCoins(sdk.NewCoin(simd.Config().Denom, sdkmath.NewInt(10_000_000))), + } + + initialBalance, err := simd.GetBalance(ctx, s.IcaAddress, simd.Config().Denom) + s.Require().NoError(err) + + if encoding == icatypes.EncodingProtobuf { + // Execute the contract: + err = s.Contract.ExecSendStargateMsgs(ctx, wasmdUser.KeyName(), []proto.Message{proposalMsg, depositMsg}, nil, nil) + s.Require().NoError(err) + } else if encoding == icatypes.EncodingProto3JSON { + err = s.Contract.ExecCustomIcaMessages(ctx, wasmdUser.KeyName(), []proto.Message{proposalMsg, depositMsg}, icatypes.EncodingProto3JSON, nil, nil) + s.Require().NoError(err) + } + + err = testutil.WaitForBlocks(ctx, 5, wasmd, simd) + s.Require().NoError(err) + + // Check if contract callbacks were executed: + callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) + s.Require().NoError(err) + + s.Require().Equal(uint64(1), callbackCounter.Success) + s.Require().Equal(uint64(0), callbackCounter.Error) + + // Check if the proposal was created: + proposal, err := simd.QueryProposal(ctx, "1") + s.Require().NoError(err) + s.Require().Equal(simd.Config().Denom, proposal.TotalDeposit[0].Denom) + s.Require().Equal(fmt.Sprint(10_000_000+5_000), proposal.TotalDeposit[0].Amount) + // We do not check title and description of the proposal because this is a legacy proposal. + + postBalance, err := simd.GetBalance(ctx, s.IcaAddress, simd.Config().Denom) + s.Require().NoError(err) + s.Require().Equal(initialBalance.Sub(sdkmath.NewInt(10_000_000+5_000)), postBalance) + }) + + s.Run(fmt.Sprintf("TestDelegateAndVoteWeighted-%s", encoding), func() { + intialBalance, err := simd.GetBalance(ctx, s.IcaAddress, simd.Config().Denom) + s.Require().NoError(err) + + validator, err := simd.Validators[0].KeyBech32(ctx, "validator", "val") + s.Require().NoError(err) + + // Stake some tokens through CosmosMsgs: + stakeCosmosMsg := types.ContractCosmosMsg{ + Staking: &types.StakingCosmosMsg{ + Delegate: &types.StakingDelegateCosmosMsg{ + Validator: validator, + Amount: types.Coin{ + Denom: simd.Config().Denom, + Amount: "10000000", + }, + }, + }, + } + // Vote on the proposal through CosmosMsgs: + voteCosmosMsg := types.ContractCosmosMsg{ + Gov: &types.GovCosmosMsg{ + VoteWeighted: &types.GovVoteWeightedCosmosMsg{ + ProposalID: 1, + Options: []types.GovVoteWeightedOption{ + { + Option: "yes", + Weight: "0.5", + }, + { + Option: "abstain", + Weight: "0.5", + }, + }, + }, + }, + } + + // Execute the contract: + err = s.Contract.ExecSendCosmosMsgs(ctx, wasmdUser.KeyName(), []types.ContractCosmosMsg{stakeCosmosMsg, voteCosmosMsg}, nil, nil) + s.Require().NoError(err) + + err = testutil.WaitForBlocks(ctx, 5, wasmd, simd) + s.Require().NoError(err) + + callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) + s.Require().NoError(err) + + s.Require().Equal(uint64(2), callbackCounter.Success) + s.Require().Equal(uint64(0), callbackCounter.Error) + + // Check if the delegation was successful: + postBalance, err := simd.GetBalance(ctx, s.IcaAddress, simd.Config().Denom) + s.Require().NoError(err) + s.Require().Equal(intialBalance.Sub(sdkmath.NewInt(10_000_000)), postBalance) + + delegationsQuerier := mysuite.NewGRPCQuerier[stakingtypes.QueryDelegationResponse](s.T(), simd, "/cosmos.staking.v1beta1.Query/Delegation") + + delRequest := stakingtypes.QueryDelegationRequest{ + DelegatorAddr: s.IcaAddress, + ValidatorAddr: validator, + } + delResp, err := delegationsQuerier.GRPCQuery(ctx, &delRequest) + s.Require().NoError(err) + s.Require().Equal(sdkmath.NewInt(10_000_000), delResp.DelegationResponse.Balance.Amount) + + // Check if the vote was successful: + votesQuerier := mysuite.NewGRPCQuerier[govtypes.QueryVoteResponse](s.T(), simd, "/cosmos.gov.v1beta1.Query/Vote") + + voteRequest := govtypes.QueryVoteRequest{ + ProposalId: 1, + Voter: s.IcaAddress, + } + voteResp, err := votesQuerier.GRPCQuery(ctx, &voteRequest) + s.Require().NoError(err) + s.Require().Len(voteResp.Vote.Options, 2) + s.Require().Equal(govtypes.OptionYes, voteResp.Vote.Options[0].Option) + expWeight, err := sdkmath.LegacyNewDecFromStr("0.5") + s.Require().NoError(err) + s.Require().Equal(expWeight, voteResp.Vote.Options[0].Weight) + s.Require().Equal(govtypes.OptionAbstain, voteResp.Vote.Options[1].Option) + s.Require().Equal(expWeight, voteResp.Vote.Options[1].Weight) + }) + + s.Run(fmt.Sprintf("TestSendAndSetWithdrawAddress-%s", encoding), func() { + initialBalance, err := simd.GetBalance(ctx, s.IcaAddress, simd.Config().Denom) + s.Require().NoError(err) + + // Send some tokens to the simdUser from the ICA address + sendMsg := types.ContractCosmosMsg{ + Bank: &types.BankCosmosMsg{ + Send: &types.BankSendCosmosMsg{ + ToAddress: simdUser.FormattedAddress(), + Amount: []types.Coin{ + { + Denom: simd.Config().Denom, + Amount: "1000000", + }, + }, + }, + }, + } + + // Set the withdraw address to the simdUser + setWithdrawAddressMsg := types.ContractCosmosMsg{ + Distribution: &types.DistributionCosmosMsg{ + SetWithdrawAddress: &types.DistributionSetWithdrawAddressCosmosMsg{ + Address: simdUser.FormattedAddress(), + }, + }, + } + + // Execute the contract: + err = s.Contract.ExecSendCosmosMsgs(ctx, wasmdUser.KeyName(), []types.ContractCosmosMsg{sendMsg, setWithdrawAddressMsg}, nil, nil) + s.Require().NoError(err) + + err = testutil.WaitForBlocks(ctx, 5, wasmd, simd) + s.Require().NoError(err) + + callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) + s.Require().NoError(err) + s.Require().Equal(uint64(3), callbackCounter.Success) + s.Require().Equal(uint64(0), callbackCounter.Error) + + // Check if the send was successful: + postBalance, err := simd.GetBalance(ctx, s.IcaAddress, simd.Config().Denom) + s.Require().NoError(err) + s.Require().Equal(sdkmath.NewInt(1_000_000), initialBalance.Sub(postBalance)) + }) +} + func (s *ContractTestSuite) TestIcaContractTimeoutPacket() { ctx := context.Background() diff --git a/e2e/interchaintest/go.mod b/e2e/interchaintest/go.mod index 665c6ee0..f74a7b35 100644 --- a/e2e/interchaintest/go.mod +++ b/e2e/interchaintest/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( cosmossdk.io/math v1.1.2 + github.com/CosmWasm/wasmd v0.45.0 github.com/cosmos/cosmos-sdk v0.47.5 github.com/cosmos/gogoproto v1.4.10 github.com/cosmos/ibc-go/v7 v7.3.0 @@ -33,8 +34,7 @@ require ( github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect - github.com/CosmWasm/wasmd v0.41.0 // indirect - github.com/CosmWasm/wasmvm v1.3.0 // indirect + github.com/CosmWasm/wasmvm v1.5.0 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/Microsoft/go-winio v0.6.0 // indirect @@ -61,7 +61,7 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/iavl v0.20.0 // indirect + github.com/cosmos/iavl v0.20.1 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.0-rc1 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.0 // indirect diff --git a/e2e/interchaintest/go.sum b/e2e/interchaintest/go.sum index 82faf589..e411be47 100644 --- a/e2e/interchaintest/go.sum +++ b/e2e/interchaintest/go.sum @@ -219,10 +219,10 @@ github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRr github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 h1:oknQF/iIhf5lVjbwjsVDzDByupRhga8nhA3NAmwyHDA= github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420/go.mod h1:KYkiMX5AbOlXXYfxkrYPrRPV6EbVUALTQh5ptUOJzu8= -github.com/CosmWasm/wasmd v0.41.0 h1:fmwxSbwb50zZDcBaayYFRLIaSFca+EFld1WOaQi49jg= -github.com/CosmWasm/wasmd v0.41.0/go.mod h1:0Sds1q2IsPaTN1gHa3BNOYcUFgtGvxH7CXEXPgoihns= -github.com/CosmWasm/wasmvm v1.3.0 h1:x12X4bKlUPS7TT9QQP45+fJo2sp30GEbiSSgb9jsec8= -github.com/CosmWasm/wasmvm v1.3.0/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8J7KFtkc= +github.com/CosmWasm/wasmd v0.45.0 h1:9zBqrturKJwC2kVsfHvbrA++EN0PS7UTXCffCGbg6JI= +github.com/CosmWasm/wasmd v0.45.0/go.mod h1:RnSAiqbNIZu4QhO+0pd7qGZgnYAMBPGmXpzTADag944= +github.com/CosmWasm/wasmvm v1.5.0 h1:3hKeT9SfwfLhxTGKH3vXaKFzBz1yuvP8SlfwfQXbQfw= +github.com/CosmWasm/wasmvm v1.5.0/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= @@ -357,8 +357,8 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI= github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= -github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= -github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= +github.com/cosmos/iavl v0.20.1 h1:rM1kqeG3/HBT85vsZdoSNsehciqUQPWrR4BYmqE2+zg= +github.com/cosmos/iavl v0.20.1/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ibc-go/modules/capability v1.0.0-rc1 h1:BvSKnPFKxL+TTSLxGKwJN4x0ndCZj0yfXhSvmsQztSA= github.com/cosmos/ibc-go/modules/capability v1.0.0-rc1/go.mod h1:A+CxAQdn2j6ihDTbClpEEBdHthWgAUAcHbRAQPY8sl4= github.com/cosmos/ibc-go/v7 v7.0.0-20230709121739-15b0fe103b52 h1:e6cYP9fum6kZPoXfQnyWWN7nO6wj21kllE/cDm3f1yo= diff --git a/e2e/interchaintest/test_data/migrate_counter.wasm b/e2e/interchaintest/test_data/migrate_counter.wasm new file mode 100644 index 00000000..068d7e1e Binary files /dev/null and b/e2e/interchaintest/test_data/migrate_counter.wasm differ diff --git a/e2e/interchaintest/test_data/simple_counter.wasm b/e2e/interchaintest/test_data/simple_counter.wasm new file mode 100644 index 00000000..723a0129 Binary files /dev/null and b/e2e/interchaintest/test_data/simple_counter.wasm differ diff --git a/e2e/interchaintest/types/contract.go b/e2e/interchaintest/types/contract.go index 48298cb2..14e2882a 100644 --- a/e2e/interchaintest/types/contract.go +++ b/e2e/interchaintest/types/contract.go @@ -29,3 +29,18 @@ func (c *Contract) Execute(ctx context.Context, callerKeyName string, execMsg st _, err := c.chain.ExecuteContract(ctx, callerKeyName, c.Address, execMsg, extraExecTxArgs...) return err } + +func QueryContract[T any](ctx context.Context, chain *cosmos.CosmosChain, contractAddr string, queryMsg string) (*T, error) { + queryResp := QueryResponse[T]{} + err := chain.QueryContract(ctx, contractAddr, queryMsg, &queryResp) + if err != nil { + return nil, err + } + + resp, err := queryResp.GetResp() + if err != nil { + return nil, err + } + + return &resp, nil +} diff --git a/e2e/interchaintest/types/cosmos_msg.go b/e2e/interchaintest/types/cosmos_msg.go index 97712f72..b701ce62 100644 --- a/e2e/interchaintest/types/cosmos_msg.go +++ b/e2e/interchaintest/types/cosmos_msg.go @@ -3,11 +3,13 @@ package types import "encoding/base64" type ContractCosmosMsg struct { - Stargate *StargateCosmosMsg `json:"stargate,omitempty"` - Bank *BankCosmosMsg `json:"bank,omitempty"` - IBC *IbcCosmosMsg `json:"ibc,omitempty"` - Staking *StakingCosmosMsg `json:"staking,omitempty"` - Gov *GovCosmosMsg `json:"gov,omitempty"` + Stargate *StargateCosmosMsg `json:"stargate,omitempty"` + Bank *BankCosmosMsg `json:"bank,omitempty"` + IBC *IbcCosmosMsg `json:"ibc,omitempty"` + Staking *StakingCosmosMsg `json:"staking,omitempty"` + Distribution *DistributionCosmosMsg `json:"distribution,omitempty"` + Gov *GovCosmosMsg `json:"gov,omitempty"` + Wasm *WasmCosmosMsg `json:"wasm,omitempty"` } type StargateCosmosMsg struct { @@ -36,6 +38,76 @@ type StakingCosmosMsg struct { Redelegate *StakingRedelegateCosmosMsg `json:"redelegate,omitempty"` } +type DistributionCosmosMsg struct { + SetWithdrawAddress *DistributionSetWithdrawAddressCosmosMsg `json:"set_withdraw_address,omitempty"` + WithdrawDelegatorReward *DistributionWithdrawDelegatorRewardCosmosMsg `json:"withdraw_delegator_reward,omitempty"` + FundCommunityPool *DistributionFundCommunityPoolCosmosMsg `json:"fund_community_pool,omitempty"` +} + +type WasmCosmosMsg struct { + Execute *WasmExecuteCosmosMsg `json:"execute,omitempty"` + Instantiate *WasmInstantiateCosmosMsg `json:"instantiate,omitempty"` + Instantiate2 *WasmInstantiate2CosmosMsg `json:"instantiate2,omitempty"` + Migrate *WasmMigrateCosmosMsg `json:"migrate,omitempty"` + UpdateAdmin *WasmUpdateAdminCosmosMsg `json:"update_admin,omitempty"` + ClearAdmin *WasmClearAdminCosmosMsg `json:"clear_admin,omitempty"` +} + +type WasmExecuteCosmosMsg struct { + ContractAddr string `json:"contract_addr"` + // base64 encoded bytes + Msg string `json:"msg"` + Funds []Coin `json:"funds"` +} + +type WasmInstantiateCosmosMsg struct { + Admin string `json:"admin"` + CodeID uint64 `json:"code_id"` + // base64 encoded bytes + Msg string `json:"msg"` + Funds []Coin `json:"funds"` + Label string `json:"label"` +} + +type WasmInstantiate2CosmosMsg struct { + Admin string `json:"admin"` + CodeID uint64 `json:"code_id"` + // base64 encoded bytes + Msg string `json:"msg"` + Funds []Coin `json:"funds"` + Label string `json:"label"` + // base64 encoded bytes + Salt string `json:"salt"` +} + +type WasmMigrateCosmosMsg struct { + ContractAddr string `json:"contract_addr"` + NewCodeID uint64 `json:"new_code_id"` + // base64 encoded bytes + Msg string `json:"msg"` +} + +type WasmUpdateAdminCosmosMsg struct { + ContractAddr string `json:"contract_addr"` + Admin string `json:"admin"` +} + +type WasmClearAdminCosmosMsg struct { + ContractAddr string `json:"contract_addr"` +} + +type DistributionSetWithdrawAddressCosmosMsg struct { + Address string `json:"address"` +} + +type DistributionWithdrawDelegatorRewardCosmosMsg struct { + Validator string `json:"validator"` +} + +type DistributionFundCommunityPoolCosmosMsg struct { + Amount []Coin `json:"amount"` +} + type StakingDelegateCosmosMsg struct { Validator string `json:"validator"` Amount Coin `json:"amount"` diff --git a/e2e/interchaintest/types/counter_msg.go b/e2e/interchaintest/types/counter_msg.go new file mode 100644 index 00000000..3b12f8ed --- /dev/null +++ b/e2e/interchaintest/types/counter_msg.go @@ -0,0 +1,20 @@ +package types + +type CounterExecuteMsg struct { + Increment *CounterIncrementMsg `json:"increment,omitempty"` + Reset *CounterResetMsg `json:"reset,omitempty"` +} + +type CounterQueryMsg struct { + GetCount *struct{} `json:"get_count,omitempty"` +} + +type GetCountResponse struct { + Count int64 `json:"count"` +} + +type CounterIncrementMsg struct{} + +type CounterResetMsg struct { + Count int64 `json:"count"` +} diff --git a/e2e/interchaintest/types/ica_contract.go b/e2e/interchaintest/types/ica_contract.go index 1687ea19..2900f4b5 100644 --- a/e2e/interchaintest/types/ica_contract.go +++ b/e2e/interchaintest/types/ica_contract.go @@ -80,11 +80,11 @@ func (c *IcaContract) ExecCustomIcaMessages( // ExecSendCosmosMsgs invokes the contract's `SendCosmosMsgsAsIcaTx` message as the caller func (c *IcaContract) ExecSendCosmosMsgs( - ctx context.Context, callerKeyName string, - cosmosMsgs []ContractCosmosMsg, memo *string, timeout *uint64, + ctx context.Context, callerKeyName string, cosmosMsgs []ContractCosmosMsg, + memo *string, timeout *uint64, extraExecTxArgs ...string, ) error { cosmosMsg := newSendCosmosMsgsMsg(cosmosMsgs, memo, timeout) - err := c.Execute(ctx, callerKeyName, cosmosMsg) + err := c.Execute(ctx, callerKeyName, cosmosMsg, extraExecTxArgs...) return err } diff --git a/e2e/interchaintest/types/ica_msg.go b/e2e/interchaintest/types/ica_msg.go index 26c36b13..f444ecb2 100644 --- a/e2e/interchaintest/types/ica_msg.go +++ b/e2e/interchaintest/types/ica_msg.go @@ -176,7 +176,7 @@ func newSendCosmosMsgsMsgFromProto(msgs []proto.Message, memo *string, timeout * } type SendCosmosMsgsAsIcaTxMsgWrapper struct { - SendCosmosMsgsAsIcaTxMsg SendCosmosMsgsAsIcaTxMsg `json:"send_cosmos_msgs_as_ica_tx"` + SendCosmosMsgsMsg SendCosmosMsgsAsIcaTxMsg `json:"send_cosmos_msgs"` } cosmosMsgs := make([]ContractCosmosMsg, len(msgs)) @@ -200,7 +200,7 @@ func newSendCosmosMsgsMsgFromProto(msgs []proto.Message, memo *string, timeout * } msg := SendCosmosMsgsAsIcaTxMsgWrapper{ - SendCosmosMsgsAsIcaTxMsg: SendCosmosMsgsAsIcaTxMsg{ + SendCosmosMsgsMsg: SendCosmosMsgsAsIcaTxMsg{ Messages: cosmosMsgs, PacketMemo: memo, TimeoutSeconds: timeout, diff --git a/e2e/interchaintest/types/types_test.go b/e2e/interchaintest/types/types_test.go index 526ac548..3ade1a50 100644 --- a/e2e/interchaintest/types/types_test.go +++ b/e2e/interchaintest/types/types_test.go @@ -1,7 +1,6 @@ package types_test import ( - "encoding/base64" "encoding/json" "testing" @@ -10,10 +9,11 @@ import ( sdkmath "cosmossdk.io/math" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos/wasm" "github.com/srdtrk/cw-ica-controller/interchaintest/v2/types" @@ -35,40 +35,6 @@ func TestExecuteMsgs(t *testing.T) { t.Parallel() - // Basic tests: - sendCustomIcaMessagesMsg := types.NewSendCustomIcaMessagesMsg(nil, nil, "", nil, nil) - require.Equal(t, `{"send_custom_ica_messages":{"messages":[]}}`, sendCustomIcaMessagesMsg) - memo := "test" - sendCustomIcaMessagesMsg = types.NewSendCustomIcaMessagesMsg(nil, nil, "", &memo, nil) - require.Equal(t, `{"send_custom_ica_messages":{"messages":[],"packet_memo":"test"}}`, sendCustomIcaMessagesMsg) - timeout := uint64(150) - sendCustomIcaMessagesMsg = types.NewSendCustomIcaMessagesMsg(nil, nil, "", nil, &timeout) - require.Equal(t, `{"send_custom_ica_messages":{"messages":[],"timeout_seconds":150}}`, sendCustomIcaMessagesMsg) - - // Test with custom messages: - - type SendCustomIcaMessagesMsg struct { - Messages []string `json:"messages"` - PacketMemo *string `json:"packet_memo,omitempty"` - TimeoutSeconds *uint64 `json:"timeout_seconds,omitempty"` - } - - type SendCustomIcaMessagesMsgWrapper struct { - SendCustomIcaMessagesMsg SendCustomIcaMessagesMsg `json:"send_custom_ica_messages"` - } - - testProposal := &govtypes.TextProposal{ - Title: "IBC Gov Proposal", - Description: "tokens for all!", - } - protoAny, err := codectypes.NewAnyWithValue(testProposal) - require.NoError(t, err) - proposalMsg := &govtypes.MsgSubmitProposal{ - Content: protoAny, - InitialDeposit: sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(5000))), - Proposer: testAddress, - } - // Create deposit message: depositMsg := &govtypes.MsgDeposit{ ProposalId: 1, @@ -76,24 +42,8 @@ func TestExecuteMsgs(t *testing.T) { Amount: sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10000000))), } - customMsg := types.NewSendCustomIcaMessagesMsg(wasm.WasmEncoding().Codec, []proto.Message{proposalMsg, depositMsg}, "proto3json", nil, nil) - unmarshaledCustomMsg := SendCustomIcaMessagesMsgWrapper{} - err = json.Unmarshal([]byte(customMsg), &unmarshaledCustomMsg) + _, err := icatypes.SerializeCosmosTxWithEncoding(wasm.WasmEncoding().Codec, []proto.Message{depositMsg}, icatypes.EncodingProto3JSON) require.NoError(t, err) - base64Msgs := unmarshaledCustomMsg.SendCustomIcaMessagesMsg.Messages - require.Len(t, base64Msgs, 2) - stringMsgs := make([]string, len(base64Msgs)) - for i, base64Msg := range base64Msgs { - msg, err := base64.StdEncoding.DecodeString(base64Msg) - require.NoError(t, err) - stringMsgs[i] = string(msg) - } - - expectedMsg1 := `{"@type":"/cosmos.gov.v1beta1.MsgSubmitProposal","content":{"@type":"/cosmos.gov.v1beta1.TextProposal","title":"IBC Gov Proposal","description":"tokens for all!"},"initial_deposit":[{"denom":"stake","amount":"5000"}],"proposer":"srdtrk"}` - expectedMsg2 := `{"@type":"/cosmos.gov.v1beta1.MsgDeposit","proposal_id":"1","depositor":"srdtrk","amount":[{"denom":"stake","amount":"10000000"}]}` - expectedMsgs := []string{expectedMsg1, expectedMsg2} - - require.Equal(t, expectedMsgs, stringMsgs) } func TestQueries(t *testing.T) { diff --git a/e2e/interchaintest/wasm_msg_test.go b/e2e/interchaintest/wasm_msg_test.go new file mode 100644 index 00000000..3bfedc8f --- /dev/null +++ b/e2e/interchaintest/wasm_msg_test.go @@ -0,0 +1,283 @@ +package main + +import ( + "context" + "encoding/base64" + "fmt" + "strconv" + + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + + interchaintest "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos/wasm" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + + mysuite "github.com/srdtrk/cw-ica-controller/interchaintest/v2/testsuite" + "github.com/srdtrk/cw-ica-controller/interchaintest/v2/types" +) + +func (s *ContractTestSuite) SetupWasmTestSuite(ctx context.Context, encoding string) uint64 { + wasmChainSpecs := []*interchaintest.ChainSpec{ + chainSpecs[0], + { + ChainConfig: ibc.ChainConfig{ + Type: "cosmos", + Name: "wasmd2", + ChainID: "wasmd-2", + Images: []ibc.DockerImage{ + { + Repository: "cosmwasm/wasmd", // FOR LOCAL IMAGE USE: Docker Image Name + Version: "v0.45.0", // FOR LOCAL IMAGE USE: Docker Image Tag + }, + }, + Bin: "wasmd", + Bech32Prefix: "wasm", + Denom: "stake", + GasPrices: "0.00stake", + GasAdjustment: 1.3, + // cannot run wasmd commands without wasm encoding + EncodingConfig: wasm.WasmEncoding(), + TrustingPeriod: "508h", + NoHostMount: false, + }, + }, + } + s.SetupSuite(ctx, wasmChainSpecs) + + codeId, err := s.ChainA.StoreContract(ctx, s.UserA.KeyName(), "../../artifacts/cw_ica_controller.wasm") + s.Require().NoError(err) + + // Instantiate the contract with channel: + instantiateMsg := types.NewInstantiateMsgWithChannelInitOptions(nil, s.ChainAConnID, s.ChainBConnID, nil, &encoding) + + contractAddr, err := s.ChainA.InstantiateContract(ctx, s.UserA.KeyName(), codeId, instantiateMsg, true, "--gas", "500000") + s.Require().NoError(err) + + s.Contract = types.NewIcaContract(types.NewContract(contractAddr, codeId, s.ChainA)) + + // Wait for the channel to get set up + err = testutil.WaitForBlocks(ctx, 5, s.ChainA, s.ChainB) + s.Require().NoError(err) + + contractState, err := s.Contract.QueryContractState(ctx) + s.Require().NoError(err) + + ownershipResponse, err := s.Contract.QueryOwnership(ctx) + s.Require().NoError(err) + + s.IcaAddress = contractState.IcaInfo.IcaAddress + s.Contract.SetIcaAddress(s.IcaAddress) + + s.Require().Equal(s.UserA.FormattedAddress(), ownershipResponse.Owner) + s.Require().Nil(ownershipResponse.PendingOwner) + s.Require().Nil(ownershipResponse.PendingExpiry) + + counterCodeId, err := s.ChainB.StoreContract(ctx, s.UserB.KeyName(), "./test_data/simple_counter.wasm") + s.Require().NoError(err) + + _, err = s.ChainB.StoreContract(ctx, s.UserB.KeyName(), "./test_data/migrate_counter.wasm") + s.Require().NoError(err) + + counterCodeID, err := strconv.ParseUint(counterCodeId, 10, 64) + s.Require().NoError(err) + + return counterCodeID +} + +func (s *ContractTestSuite) TestSendWasmMsgsProtobufEncoding() { + s.SendWasmMsgsTestWithEncoding(icatypes.EncodingProtobuf) +} + +// currently, Wasm is only supported with protobuf encoding +func (s *ContractTestSuite) SendWasmMsgsTestWithEncoding(encoding string) { + ctx := context.Background() + + // This starts the chains, relayer, creates the user accounts, creates the ibc clients and connections, + // sets up the contract and does the channel handshake for the contract test suite. + counterCodeID := s.SetupWasmTestSuite(ctx, encoding) + wasmd, wasmd2 := s.ChainA, s.ChainB + wasmdUser, wasmd2User := s.UserA, s.UserB + + // Fund the ICA address: + s.FundAddressChainB(ctx, s.IcaAddress) + + var counterAddress string + s.Run(fmt.Sprintf("TestInstantiate-%s", encoding), func() { + // Instantiate the contract: + instantiateMsg := types.ContractCosmosMsg{ + Wasm: &types.WasmCosmosMsg{ + Instantiate: &types.WasmInstantiateCosmosMsg{ + Admin: s.IcaAddress, + CodeID: counterCodeID, + Label: "counter", + Msg: toBase64(`{"count": 0}`), + Funds: []types.Coin{}, + }, + }, + } + + // Execute the contract: + err := s.Contract.ExecSendCosmosMsgs(ctx, wasmdUser.KeyName(), []types.ContractCosmosMsg{instantiateMsg}, nil, nil) + s.Require().NoError(err) + + err = testutil.WaitForBlocks(ctx, 5, wasmd, wasmd2) + s.Require().NoError(err) + + // Check if contract callbacks were executed: + callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) + s.Require().NoError(err) + + s.Require().Equal(uint64(1), callbackCounter.Success) + s.Require().Equal(uint64(0), callbackCounter.Error) + + contractByCodeQuerier := mysuite.NewGRPCQuerier[wasmtypes.QueryContractsByCodeResponse](s.T(), wasmd2, "/cosmwasm.wasm.v1.Query/ContractsByCode") + + contractByCodeRequest := wasmtypes.QueryContractsByCodeRequest{ + CodeId: counterCodeID, + } + contractByCodeResp, err := contractByCodeQuerier.GRPCQuery(ctx, &contractByCodeRequest) + s.Require().NoError(err) + s.Require().Len(contractByCodeResp.Contracts, 1) + + counterAddress = contractByCodeResp.Contracts[0] + + counterState, err := types.QueryContract[types.GetCountResponse](ctx, wasmd2, counterAddress, `{"get_count": {}}`) + s.Require().NoError(err) + + s.Require().Equal(int64(0), counterState.Count) + }) + + var counter2Address string + s.Run(fmt.Sprintf("TestExecuteAndInstantiate2AndClearAdminMsg-%s", encoding), func() { + // Execute the contract: + executeMsg := types.ContractCosmosMsg{ + Wasm: &types.WasmCosmosMsg{ + Execute: &types.WasmExecuteCosmosMsg{ + ContractAddr: counterAddress, + Msg: toBase64(`{"increment": {}}`), + Funds: []types.Coin{}, + }, + }, + } + + clearAdminMsg := types.ContractCosmosMsg{ + Wasm: &types.WasmCosmosMsg{ + ClearAdmin: &types.WasmClearAdminCosmosMsg{ + ContractAddr: counterAddress, + }, + }, + } + + instantiate2Msg := types.ContractCosmosMsg{ + Wasm: &types.WasmCosmosMsg{ + Instantiate2: &types.WasmInstantiate2CosmosMsg{ + Admin: s.IcaAddress, + CodeID: counterCodeID, + Label: "counter2", + Msg: toBase64(`{"count": 0}`), + Funds: []types.Coin{}, + Salt: toBase64("salt"), + }, + }, + } + + // Execute the contract: + err := s.Contract.ExecSendCosmosMsgs(ctx, wasmdUser.KeyName(), []types.ContractCosmosMsg{executeMsg, clearAdminMsg, instantiate2Msg}, nil, nil) + s.Require().NoError(err) + + err = testutil.WaitForBlocks(ctx, 5, wasmd, wasmd2) + s.Require().NoError(err) + + // Check if contract callbacks were executed: + callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) + s.Require().NoError(err) + + s.Require().Equal(uint64(2), callbackCounter.Success) + s.Require().Equal(uint64(0), callbackCounter.Error) + + counterState, err := types.QueryContract[types.GetCountResponse](ctx, wasmd2, counterAddress, `{"get_count": {}}`) + s.Require().NoError(err) + + s.Require().Equal(int64(1), counterState.Count) + + contractInfoQuerier := mysuite.NewGRPCQuerier[wasmtypes.QueryContractInfoResponse](s.T(), wasmd2, "/cosmwasm.wasm.v1.Query/ContractInfo") + + contractInfoRequest := wasmtypes.QueryContractInfoRequest{ + Address: counterAddress, + } + contractInfoResp, err := contractInfoQuerier.GRPCQuery(ctx, &contractInfoRequest) + s.Require().NoError(err) + + s.Require().Equal("", contractInfoResp.ContractInfo.Admin) + + contractByCodeQuerier := mysuite.NewGRPCQuerier[wasmtypes.QueryContractsByCodeResponse](s.T(), wasmd2, "/cosmwasm.wasm.v1.Query/ContractsByCode") + + contractByCodeRequest := wasmtypes.QueryContractsByCodeRequest{ + CodeId: counterCodeID, + } + contractByCodeResp, err := contractByCodeQuerier.GRPCQuery(ctx, &contractByCodeRequest) + s.Require().NoError(err) + s.Require().Len(contractByCodeResp.Contracts, 2) + + counter2Address = contractByCodeResp.Contracts[1] + }) + + s.Run(fmt.Sprintf("TestMigrateAndUpdateAdmin-%s", encoding), func() { + migrateMsg := types.ContractCosmosMsg{ + Wasm: &types.WasmCosmosMsg{ + Migrate: &types.WasmMigrateCosmosMsg{ + ContractAddr: counter2Address, + NewCodeID: counterCodeID + 1, + Msg: toBase64(`{}`), + }, + }, + } + + updateAdminMsg := types.ContractCosmosMsg{ + Wasm: &types.WasmCosmosMsg{ + UpdateAdmin: &types.WasmUpdateAdminCosmosMsg{ + ContractAddr: counter2Address, + Admin: wasmd2User.FormattedAddress(), + }, + }, + } + + // Execute the contract: + err := s.Contract.ExecSendCosmosMsgs(ctx, wasmdUser.KeyName(), []types.ContractCosmosMsg{migrateMsg, updateAdminMsg}, nil, nil) + s.Require().NoError(err) + + err = testutil.WaitForBlocks(ctx, 5, wasmd, wasmd2) + s.Require().NoError(err) + + // Check if contract callbacks were executed: + callbackCounter, err := s.Contract.QueryCallbackCounter(ctx) + s.Require().NoError(err) + + // s.Require().Equal(uint64(1), callbackCounter.Error) + s.Require().Equal(uint64(3), callbackCounter.Success) + s.Require().Equal(uint64(0), callbackCounter.Error) + + counterState, err := types.QueryContract[types.GetCountResponse](ctx, wasmd2, counter2Address, `{"get_count": {}}`) + s.Require().NoError(err) + + s.Require().Equal(int64(0), counterState.Count) + + contractInfoQuerier := mysuite.NewGRPCQuerier[wasmtypes.QueryContractInfoResponse](s.T(), wasmd2, "/cosmwasm.wasm.v1.Query/ContractInfo") + + contractInfoRequest := wasmtypes.QueryContractInfoRequest{ + Address: counter2Address, + } + contractInfoResp, err := contractInfoQuerier.GRPCQuery(ctx, &contractInfoRequest) + s.Require().NoError(err) + + s.Require().Equal(counterCodeID+1, contractInfoResp.ContractInfo.CodeID) + s.Require().Equal(wasmd2User.FormattedAddress(), contractInfoResp.ContractInfo.Admin) + }) +} + +func toBase64(msg string) string { + return base64.StdEncoding.EncodeToString([]byte(msg)) +} diff --git a/src/types/cosmos_msg.rs b/src/types/cosmos_msg.rs index 07c4b816..374a7e04 100644 --- a/src/types/cosmos_msg.rs +++ b/src/types/cosmos_msg.rs @@ -58,10 +58,8 @@ mod convert_to_any { cosmos::{ bank::v1beta1::MsgSend, base::v1beta1::Coin as ProtoCoin, - gov::v1beta1::{ - MsgVote, MsgVoteWeighted, VoteOption as ProtoVoteOption, - WeightedVoteOption as ProtoWeightedVoteOption, - }, + gov::v1::{MsgVoteWeighted, WeightedVoteOption as ProtoWeightedVoteOption}, + gov::v1beta1::{MsgVote, VoteOption as ProtoVoteOption}, }, cosmwasm::wasm::v1::{ MsgClearAdmin, MsgExecuteContract, MsgInstantiateContract, MsgInstantiateContract2, @@ -243,7 +241,7 @@ mod convert_to_any { proposal_id, options, } => { - let options = options + let options: Vec = options .into_iter() .map(|weighted_option| -> ProtoWeightedVoteOption { ProtoWeightedVoteOption { @@ -257,10 +255,11 @@ mod convert_to_any { proposal_id, voter, options, + metadata: String::new(), }; Any { - type_url: "/cosmos.gov.v1beta1.MsgVoteWeighted".to_string(), + type_url: "/cosmos.gov.v1.MsgVoteWeighted".to_string(), value: value.encode_to_vec(), } } @@ -268,14 +267,14 @@ mod convert_to_any { } #[cfg(feature = "staking")] - pub fn staking(msg: StakingMsg, from_address: String) -> Result { + pub fn staking(msg: StakingMsg, delegator_address: String) -> Result { use cosmos_sdk_proto::cosmos::staking::v1beta1::{ MsgBeginRedelegate, MsgDelegate, MsgUndelegate, }; match msg { StakingMsg::Delegate { validator, amount } => Any::from_msg(&MsgDelegate { - delegator_address: from_address, + delegator_address, validator_address: validator, amount: Some(ProtoCoin { denom: amount.denom, @@ -283,7 +282,7 @@ mod convert_to_any { }), }), StakingMsg::Undelegate { validator, amount } => Any::from_msg(&MsgUndelegate { - delegator_address: from_address, + delegator_address, validator_address: validator, amount: Some(ProtoCoin { denom: amount.denom, @@ -295,7 +294,7 @@ mod convert_to_any { dst_validator, amount, } => Any::from_msg(&MsgBeginRedelegate { - delegator_address: from_address, + delegator_address, validator_src_address: src_validator, validator_dst_address: dst_validator, amount: Some(ProtoCoin { @@ -308,7 +307,10 @@ mod convert_to_any { } #[cfg(feature = "staking")] - pub fn distribution(msg: DistributionMsg, from_address: String) -> Result { + pub fn distribution( + msg: DistributionMsg, + delegator_address: String, + ) -> Result { use cosmos_sdk_proto::cosmos::distribution::v1beta1::{ MsgSetWithdrawAddress, MsgWithdrawDelegatorReward, }; @@ -316,13 +318,13 @@ mod convert_to_any { match msg { DistributionMsg::WithdrawDelegatorReward { validator } => { Any::from_msg(&MsgWithdrawDelegatorReward { - delegator_address: from_address, + delegator_address, validator_address: validator, }) } DistributionMsg::SetWithdrawAddress { address } => { Any::from_msg(&MsgSetWithdrawAddress { - delegator_address: from_address, + delegator_address, withdraw_address: address, }) } @@ -335,18 +337,13 @@ mod convert_to_any { /// [`proto3json`](crate::ibc::types::metadata::TxEncoding::Proto3Json) encoding format. /// /// # Panics -/// Panics if the [`CosmosMsg`] is not supported. Notably, [`CosmosMsg::Stargate`] is not supported. +/// Panics if the [`CosmosMsg`] is not supported. +/// Notably, [`CosmosMsg::Stargate`] and [`CosmosMsg::Wasm`] are not supported. /// /// ## List of supported [`CosmosMsg`] /// /// - [`CosmosMsg::Bank`] with [`BankMsg::Send`] /// - [`CosmosMsg::Ibc`] with [`IbcMsg::Transfer`] -/// - [`CosmosMsg::Wasm`] with [`cosmwasm_std::WasmMsg::Execute`] -/// - [`CosmosMsg::Wasm`] with [`cosmwasm_std::WasmMsg::Instantiate`] -/// - [`CosmosMsg::Wasm`] with [`cosmwasm_std::WasmMsg::Instantiate2`] -/// - [`CosmosMsg::Wasm`] with [`cosmwasm_std::WasmMsg::Migrate`] -/// - [`CosmosMsg::Wasm`] with [`cosmwasm_std::WasmMsg::UpdateAdmin`] -/// - [`CosmosMsg::Wasm`] with [`cosmwasm_std::WasmMsg::ClearAdmin`] /// - [`CosmosMsg::Gov`] with [`cosmwasm_std::GovMsg::Vote`] /// - [`CosmosMsg::Gov`] with [`cosmwasm_std::GovMsg::VoteWeighted`] /// - [`CosmosMsg::Staking`] with [`cosmwasm_std::StakingMsg::Delegate`] @@ -359,7 +356,6 @@ pub fn convert_to_proto3json(msg: CosmosMsg, from_address: String) -> String { match msg { CosmosMsg::Bank(msg) => convert_to_json::bank(msg, from_address), CosmosMsg::Ibc(msg) => convert_to_json::ibc(msg, from_address), - CosmosMsg::Wasm(msg) => convert_to_json::wasm(msg, from_address), CosmosMsg::Gov(msg) => convert_to_json::gov(msg, from_address), #[cfg(feature = "staking")] CosmosMsg::Staking(msg) => convert_to_json::staking(msg, from_address), @@ -372,7 +368,7 @@ pub fn convert_to_proto3json(msg: CosmosMsg, from_address: String) -> String { mod convert_to_json { #[cfg(feature = "staking")] use cosmwasm_std::{DistributionMsg, StakingMsg}; - use cosmwasm_std::{GovMsg, VoteOption, WasmMsg}; + use cosmwasm_std::{GovMsg, VoteOption}; use super::{BankMsg, Coin, IbcMsg}; @@ -419,76 +415,6 @@ mod convert_to_json { .to_string() } - pub fn wasm(msg: WasmMsg, sender: String) -> String { - match msg { - WasmMsg::Execute { - contract_addr, - msg, - funds, - } => CosmosMsgProto3JsonSerializer::ExecuteContract { - sender, - contract: contract_addr, - msg: msg.to_vec(), - funds, - }, - WasmMsg::Instantiate { - admin, - code_id, - msg, - funds, - label, - } => CosmosMsgProto3JsonSerializer::InstantiateContract { - admin: admin.unwrap_or_default(), - sender, - code_id, - msg: msg.to_vec(), - funds, - label, - }, - WasmMsg::Migrate { - contract_addr, - new_code_id, - msg, - } => CosmosMsgProto3JsonSerializer::MigrateContract { - sender, - contract: contract_addr, - code_id: new_code_id, - msg: msg.to_vec(), - }, - WasmMsg::UpdateAdmin { - contract_addr, - admin, - } => CosmosMsgProto3JsonSerializer::UpdateAdmin { - sender, - new_admin: admin, - contract: contract_addr, - }, - WasmMsg::ClearAdmin { contract_addr } => CosmosMsgProto3JsonSerializer::ClearAdmin { - sender, - contract: contract_addr, - }, - WasmMsg::Instantiate2 { - admin, - code_id, - label, - msg, - funds, - salt, - } => CosmosMsgProto3JsonSerializer::InstantiateContract2 { - admin: admin.unwrap_or_default(), - sender, - code_id, - label, - msg: msg.to_vec(), - funds, - salt: salt.to_vec(), - fix_msg: false, - }, - _ => panic!("Unsupported WasmMsg"), - } - .to_string() - } - pub fn gov(msg: GovMsg, voter: String) -> String { const fn convert_to_u64(option: &VoteOption) -> u64 { match option { @@ -620,54 +546,6 @@ mod convert_to_json { #[serde(skip_serializing_if = "Option::is_none")] memo: Option, }, - /// This is a Cosmos message to execute a smart contract. - #[serde(rename = "/cosmwasm.wasm.v1.MsgExecuteContract")] - ExecuteContract { - sender: String, - contract: String, - msg: Vec, - funds: Vec, - }, - /// This is a Cosmos message to instantiate a smart contract. - #[serde(rename = "/cosmwasm.wasm.v1.MsgInstantiateContract")] - InstantiateContract { - admin: String, - sender: String, - code_id: u64, - msg: Vec, - funds: Vec, - label: String, - }, - /// This is a Cosmos message to migrate a smart contract. - #[serde(rename = "/cosmwasm.wasm.v1.MsgMigrateContract")] - MigrateContract { - sender: String, - contract: String, - code_id: u64, - msg: Vec, - }, - /// This is a Cosmos message to update the admin of a smart contract. - #[serde(rename = "/cosmwasm.wasm.v1.MsgUpdateAdmin")] - UpdateAdmin { - sender: String, - new_admin: String, - contract: String, - }, - /// This is a Cosmos message to clear the admin of a smart contract. - #[serde(rename = "/cosmwasm.wasm.v1.MsgClearAdmin")] - ClearAdmin { sender: String, contract: String }, - /// This is a Cosmos message to instantiate2 a smart contract with a salt. - #[serde(rename = "/cosmwasm.wasm.v1.MsgInstantiateContract2")] - InstantiateContract2 { - admin: String, - sender: String, - code_id: u64, - label: String, - msg: Vec, - funds: Vec, - salt: Vec, - fix_msg: bool, - }, /// This is a Cosmos message to vote on a governance proposal. #[serde(rename = "/cosmos.gov.v1beta1.MsgVote")] Vote { @@ -746,7 +624,9 @@ mod convert_to_json { #[cfg(test)] mod tests { - use cosmwasm_std::{coins, from_json}; + use std::str::FromStr; + + use cosmwasm_std::{coins, from_json, Decimal, Uint128, VoteOption, WeightedVoteOption}; use crate::ibc::types::packet::IcaPacketData; @@ -773,4 +653,23 @@ mod tests { assert_eq!(expected, cosmos_tx.messages[0]); } + + #[test] + fn test_weighted_vote_option() { + let test_msg = r#"{"option":"yes","weight":"0.5"}"#; + + let vote_option = serde_json_wasm::from_str::(test_msg).unwrap(); + + assert_eq!( + vote_option, + WeightedVoteOption { + option: VoteOption::Yes, + weight: Decimal::from_ratio( + Uint128::from_str("1").unwrap(), + Uint128::from_str("2").unwrap() + ), + } + ); + assert_eq!("0.5".to_string(), vote_option.weight.to_string()); + } } diff --git a/src/types/msg.rs b/src/types/msg.rs index 93ab41cd..c1b4d0de 100644 --- a/src/types/msg.rs +++ b/src/types/msg.rs @@ -36,7 +36,24 @@ pub enum ExecuteMsg { #[serde(skip_serializing_if = "Option::is_none")] channel_open_init_options: Option, }, + /// `SendCosmosMsgs` converts the provided array of [`CosmosMsg`] to an ICA tx and sends them to the ICA host. + /// [`CosmosMsg::Stargate`] and [`CosmosMsg::Wasm`] are only supported if the [`TxEncoding`](crate::ibc::types::metadata::TxEncoding) is [`TxEncoding::Protobuf`](crate::ibc::types::metadata::TxEncoding). + /// + /// **This is the recommended way to send messages to the ICA host.** + SendCosmosMsgs { + /// The stargate messages to convert and send to the ICA host. + messages: Vec, + /// Optional memo to include in the ibc packet. + #[serde(skip_serializing_if = "Option::is_none")] + packet_memo: Option, + /// Optional timeout in seconds to include with the ibc packet. + /// If not specified, the [default timeout](crate::ibc::types::packet::DEFAULT_TIMEOUT_SECONDS) is used. + #[serde(skip_serializing_if = "Option::is_none")] + timeout_seconds: Option, + }, /// `SendCustomIcaMessages` sends custom messages from the ICA controller to the ICA host. + /// + /// **Use this only if you know what you are doing.** SendCustomIcaMessages { /// Base64-encoded json or proto messages to send to the ICA host. /// @@ -71,19 +88,6 @@ pub enum ExecuteMsg { #[serde(skip_serializing_if = "Option::is_none")] timeout_seconds: Option, }, - /// `SendCosmosMsgs` converts the provided array of [`CosmosMsg`] to an ICA tx and sends them to the ICA host. - /// [`CosmosMsg::Stargate`] is only supported if the [`TxEncoding`](crate::ibc::types::metadata::TxEncoding) is [`TxEncoding::Protobuf`](crate::ibc::types::metadata::TxEncoding). - SendCosmosMsgs { - /// The stargate messages to convert and send to the ICA host. - messages: Vec, - /// Optional memo to include in the ibc packet. - #[serde(skip_serializing_if = "Option::is_none")] - packet_memo: Option, - /// Optional timeout in seconds to include with the ibc packet. - /// If not specified, the [default timeout](crate::ibc::types::packet::DEFAULT_TIMEOUT_SECONDS) is used. - #[serde(skip_serializing_if = "Option::is_none")] - timeout_seconds: Option, - }, /// `UpdateCallbackAddress` updates the contract callback address. UpdateCallbackAddress { /// The new callback address.