Skip to content

Commit

Permalink
Fix event attribute names in CW pointer contracts (#1659)
Browse files Browse the repository at this point in the history
* Fix event attribute names in CW pointer contracts

* use reverse registry to validate delegatecall
  • Loading branch information
codchen authored May 14, 2024
1 parent f985944 commit 8c348d5
Show file tree
Hide file tree
Showing 24 changed files with 687 additions and 129 deletions.
2 changes: 1 addition & 1 deletion example/cosmwasm/cw721/artifacts/checksums.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
94cdd9c3e85c26f7cec43c23bfb4b3b2b2d71a0a8d85c58df12ffec0741febc8 cwerc721.wasm
d34f50997bd0ad45badcf8174b67900806021abca011cbba658517d5af5e5eca cwerc721.wasm
Binary file modified example/cosmwasm/cw721/artifacts/cwerc721.wasm
Binary file not shown.
38 changes: 27 additions & 11 deletions example/cosmwasm/cw721/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ pub fn execute(
execute_send_nft(deps, info, contract, token_id, msg)
},
ExecuteMsg::Approve { spender, token_id, expires: _ } => {
execute_approve(deps, spender, token_id)
execute_approve(deps, info, spender, token_id, true)
},
ExecuteMsg::Revoke { spender: _, token_id } => {
execute_approve(deps, "".to_string(), token_id)
ExecuteMsg::Revoke { spender, token_id } => {
execute_approve(deps, info, spender, token_id, false)
},
ExecuteMsg::ApproveAll { operator, expires: _ } => {
execute_approve_all(deps, operator, true)
execute_approve_all(deps, info, operator, true)
},
ExecuteMsg::RevokeAll { operator } => {
execute_approve_all(deps, operator, false)
execute_approve_all(deps, info, operator, false)
},
ExecuteMsg::Burn { token_id: _ } => { execute_burn() }
}
Expand Down Expand Up @@ -82,24 +82,35 @@ pub fn execute_send_nft(

pub fn execute_approve(
deps: DepsMut<EvmQueryWrapper>,
info: MessageInfo,
spender: String,
token_id: String,
approved: bool,
) -> Result<Response<EvmMsg>, ContractError> {
let erc_addr = ERC721_ADDRESS.load(deps.storage)?;

let querier = EvmQuerier::new(&deps.querier);
let payload = querier.erc721_approve_payload(spender.clone(), token_id.clone())?;
let mut payload_spender = spender.clone();
let mut action = "approve";
if !approved {
payload_spender = "".to_string();
action = "revoke";
}
let payload = querier.erc721_approve_payload(payload_spender, token_id.clone())?;
let msg = EvmMsg::DelegateCallEvm { to: erc_addr, data: payload.encoded_payload };
let res = Response::new()
.add_attribute("action", "approve")
.add_attribute("action", action)
.add_attribute("token_id", token_id)
.add_attribute("sender", info.sender)
.add_attribute("spender", spender.clone())
.add_message(msg);

Ok(res)
}

pub fn execute_approve_all(
deps: DepsMut<EvmQueryWrapper>,
info: MessageInfo,
to: String,
approved: bool,
) -> Result<Response<EvmMsg>, ContractError> {
Expand All @@ -108,9 +119,14 @@ pub fn execute_approve_all(
let querier = EvmQuerier::new(&deps.querier);
let payload = querier.erc721_set_approval_all_payload(to.clone(), approved)?;
let msg = EvmMsg::DelegateCallEvm { to: erc_addr, data: payload.encoded_payload };
let mut action = "approve_all";
if !approved {
action = "revoke_all";
}
let res = Response::new()
.add_attribute("action", "approve_all")
.add_attribute("to", to)
.add_attribute("action", action)
.add_attribute("operator", to)
.add_attribute("sender", info.sender)
.add_attribute("approved", format!("{}", approved))
.add_message(msg);

Expand All @@ -136,8 +152,8 @@ fn transfer_nft(
let payload = querier.erc721_transfer_payload(owner, recipient.clone(), token_id.clone())?;
let msg = EvmMsg::DelegateCallEvm { to: erc_addr, data: payload.encoded_payload };
let res = Response::new()
.add_attribute("from", info.sender)
.add_attribute("to", recipient)
.add_attribute("sender", info.sender)
.add_attribute("recipient", recipient)
.add_attribute("token_id", token_id)
.add_message(msg);

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ require (
github.com/fzipp/gocyclo v0.5.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-critic/go-critic v0.6.3 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
Expand Down Expand Up @@ -172,6 +173,7 @@ require (
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2Gihuqh
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
Expand Down Expand Up @@ -765,6 +766,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag=
Expand Down
5 changes: 3 additions & 2 deletions precompiles/ibc/ibc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package ibc_test

import (
"errors"
"github.com/cosmos/ibc-go/v3/modules/core/exported"
"math/big"
"reflect"
"testing"
"time"

"github.com/cosmos/ibc-go/v3/modules/core/exported"

sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -95,7 +96,7 @@ func TestPrecompile_Run(t *testing.T) {
fields: fields{transferKeeper: &MockTransferKeeper{}},
args: commonArgs,
wantBz: packedTrue,
wantRemainingGas: 994040,
wantRemainingGas: 994319,
wantErr: false,
},
{
Expand Down
14 changes: 7 additions & 7 deletions precompiles/wasmd/wasmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestInstantiate(t *testing.T) {
require.Equal(t, 2, len(outputs))
require.Equal(t, "sei1hrpna9v7vs3stzyd4z3xf00676kf78zpe2u5ksvljswn2vnjp3yslucc3n", outputs[0].(string))
require.Empty(t, outputs[1].([]byte))
require.Equal(t, uint64(880848), g)
require.Equal(t, uint64(881127), g)

amtsbz, err = sdk.NewCoins().MarshalJSON()
require.Nil(t, err)
Expand All @@ -102,7 +102,7 @@ func TestInstantiate(t *testing.T) {
require.Equal(t, 2, len(outputs))
require.Equal(t, "sei1hrpna9v7vs3stzyd4z3xf00676kf78zpe2u5ksvljswn2vnjp3yslucc3n", outputs[0].(string))
require.Empty(t, outputs[1].([]byte))
require.Equal(t, uint64(903904), g)
require.Equal(t, uint64(904183), g)

// non-existent code ID
args, _ = instantiateMethod.Inputs.Pack(
Expand Down Expand Up @@ -174,7 +174,7 @@ func TestExecute(t *testing.T) {
require.Nil(t, err)
require.Equal(t, 1, len(outputs))
require.Equal(t, fmt.Sprintf("received test msg from %s with 1000usei", mockAddr.String()), string(outputs[0].([]byte)))
require.Equal(t, uint64(907107), g)
require.Equal(t, uint64(907386), g)
require.Equal(t, int64(1000), testApp.BankKeeper.GetBalance(statedb.Ctx(), contractAddr, "usei").Amount.Int64())

amtsbz, err = sdk.NewCoins().MarshalJSON()
Expand Down Expand Up @@ -263,7 +263,7 @@ func TestQuery(t *testing.T) {
require.Nil(t, err)
require.Equal(t, 1, len(outputs))
require.Equal(t, "{\"message\":\"query test\"}", string(outputs[0].([]byte)))
require.Equal(t, uint64(931433), g)
require.Equal(t, uint64(931712), g)

// bad contract address
args, _ = queryMethod.Inputs.Pack(mockAddr.String(), []byte("{\"info\":{}}"))
Expand Down Expand Up @@ -326,7 +326,7 @@ func TestExecuteBatchOneMessage(t *testing.T) {
require.Nil(t, err)
require.Equal(t, 1, len(outputs))
require.Equal(t, fmt.Sprintf("received test msg from %s with 1000usei", mockAddr.String()), string((outputs[0].([][]byte))[0]))
require.Equal(t, uint64(907107), g)
require.Equal(t, uint64(907386), g)
require.Equal(t, int64(1000), testApp.BankKeeper.GetBalance(statedb.Ctx(), contractAddr, "usei").Amount.Int64())

amtsbz, err = sdk.NewCoins().MarshalJSON()
Expand Down Expand Up @@ -432,7 +432,7 @@ func TestExecuteBatchMultipleMessages(t *testing.T) {
require.Equal(t, fmt.Sprintf("received test msg from %s with 1000usei", mockAddr.String()), string(parsedOutputs[0]))
require.Equal(t, fmt.Sprintf("received test msg from %s with 1000usei", mockAddr.String()), string(parsedOutputs[1]))
require.Equal(t, fmt.Sprintf("received test msg from %s with 1000usei", mockAddr.String()), string(parsedOutputs[2]))
require.Equal(t, uint64(726445), g)
require.Equal(t, uint64(726724), g)
require.Equal(t, int64(3000), testApp.BankKeeper.GetBalance(statedb.Ctx(), contractAddr, "usei").Amount.Int64())

amtsbz2, err := sdk.NewCoins().MarshalJSON()
Expand All @@ -459,7 +459,7 @@ func TestExecuteBatchMultipleMessages(t *testing.T) {
require.Equal(t, fmt.Sprintf("received test msg from %s with", mockAddr.String()), string(parsedOutputs[0]))
require.Equal(t, fmt.Sprintf("received test msg from %s with 1000usei", mockAddr.String()), string(parsedOutputs[1]))
require.Equal(t, fmt.Sprintf("received test msg from %s with", mockAddr.String()), string(parsedOutputs[2]))
require.Equal(t, uint64(774966), g)
require.Equal(t, uint64(775245), g)
require.Equal(t, int64(1000), testApp.BankKeeper.GetBalance(statedb.Ctx(), contractAddr, "usei").Amount.Int64())

// allowed delegatecall
Expand Down
13 changes: 13 additions & 0 deletions proto/evm/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ service Query {
rpc Pointer(QueryPointerRequest) returns (QueryPointerResponse) {
option (google.api.http).get = "/sei-protocol/seichain/evm/pointer";
}

rpc PointerVersion(QueryPointerVersionRequest) returns (QueryPointerVersionResponse) {
option (google.api.http).get = "/sei-protocol/seichain/evm/pointer_version";
}
}

message QuerySeiAddressByEVMAddressRequest {
Expand Down Expand Up @@ -62,3 +66,12 @@ message QueryPointerResponse {
uint32 version = 2;
bool exists = 3;
}

message QueryPointerVersionRequest {
PointerType pointer_type = 1;
}

message QueryPointerVersionResponse {
uint32 version = 1;
uint64 cw_code_id = 2;
}
2 changes: 1 addition & 1 deletion x/evm/artifacts/erc721/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package erc721

import "embed"

const CurrentVersion uint16 = 1
const CurrentVersion uint16 = 2

//go:embed cwerc721.wasm
var f embed.FS
Expand Down
Binary file modified x/evm/artifacts/erc721/cwerc721.wasm
Binary file not shown.
33 changes: 32 additions & 1 deletion x/evm/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func GetQueryCmd(_ string) *cobra.Command {
cmd.AddCommand(CmdQueryERC20())
cmd.AddCommand(CmdQueryPayload())
cmd.AddCommand(CmdQueryPointer())
cmd.AddCommand(CmdQueryPointerVersion())

return cmd
}
Expand Down Expand Up @@ -301,8 +302,9 @@ func CmdQueryPointer() *cobra.Command {
return err
}
queryClient := types.NewQueryClient(clientCtx)
ctx := cmd.Context()

res, err := queryClient.Pointer(context.Background(), &types.QueryPointerRequest{
res, err := queryClient.Pointer(ctx, &types.QueryPointerRequest{
PointerType: types.PointerType(types.PointerType_value[args[0]]), Pointee: args[1],
})
if err != nil {
Expand All @@ -317,3 +319,32 @@ func CmdQueryPointer() *cobra.Command {

return cmd
}

func CmdQueryPointerVersion() *cobra.Command {
cmd := &cobra.Command{
Use: "pointer-version [type]",
Short: "Query for the current pointer version and stored code ID (if applicable)",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)
ctx := cmd.Context()

req := types.QueryPointerVersionRequest{PointerType: types.PointerType(types.PointerType_value[args[0]])}
res, err := queryClient.PointerVersion(ctx, &req)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
9 changes: 6 additions & 3 deletions x/evm/keeper/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@ func (k *Keeper) HandleInternalEVMCall(ctx sdk.Context, req *types.MsgInternalEV
}

func (k *Keeper) HandleInternalEVMDelegateCall(ctx sdk.Context, req *types.MsgInternalEVMDelegateCall) (*sdk.Result, error) {
if !k.IsCWCodeHashWhitelistedForEVMDelegateCall(ctx, req.CodeHash) {
return nil, errors.New("code hash not authorized to make EVM delegate call")
}
var to *common.Address
if req.To != "" {
addr := common.HexToAddress(req.To)
to = &addr
} else {
return nil, errors.New("cannot use a CosmWasm contract to delegate-create an EVM contract")
}
addr, _, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(common.BytesToAddress([]byte(req.FromContract))))
if !exists || common.BytesToAddress(addr).Cmp(*to) != 0 {
return nil, errors.New("only pointer contract can make delegatecalls")
}
zeroInt := sdk.ZeroInt()
senderAddr, err := sdk.AccAddressFromBech32(req.Sender)
Expand Down
35 changes: 35 additions & 0 deletions x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/ethereum/go-ethereum/common"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/cw20"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/cw721"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/erc20"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/erc721"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/native"
"github.com/sei-protocol/sei-chain/x/evm/types"
)

Expand Down Expand Up @@ -111,3 +116,33 @@ func (q Querier) Pointer(c context.Context, req *types.QueryPointerRequest) (*ty
return nil, errors.ErrUnsupported
}
}

func (q Querier) PointerVersion(c context.Context, req *types.QueryPointerVersionRequest) (*types.QueryPointerVersionResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
switch req.PointerType {
case types.PointerType_NATIVE:
return &types.QueryPointerVersionResponse{
Version: uint32(native.CurrentVersion),
}, nil
case types.PointerType_CW20:
return &types.QueryPointerVersionResponse{
Version: uint32(cw20.CurrentVersion),
}, nil
case types.PointerType_CW721:
return &types.QueryPointerVersionResponse{
Version: uint32(cw721.CurrentVersion),
}, nil
case types.PointerType_ERC20:
return &types.QueryPointerVersionResponse{
Version: uint32(erc20.CurrentVersion),
CwCodeId: q.GetStoredPointerCodeID(ctx, types.PointerType_ERC20),
}, nil
case types.PointerType_ERC721:
return &types.QueryPointerVersionResponse{
Version: uint32(erc721.CurrentVersion),
CwCodeId: q.GetStoredPointerCodeID(ctx, types.PointerType_ERC721),
}, nil
default:
return nil, errors.ErrUnsupported
}
}
Loading

0 comments on commit 8c348d5

Please sign in to comment.