Skip to content

Commit

Permalink
Add message type to associate CW contract address (#1681)
Browse files Browse the repository at this point in the history
* Add message type to associate CW contract address

* fix test

* bump sei-wasmd to fix ignite

---------

Co-authored-by: Philip Su <[email protected]>
  • Loading branch information
codchen and philipsu522 authored May 17, 2024
1 parent 16a2b18 commit c725b57
Show file tree
Hide file tree
Showing 18 changed files with 592 additions and 72 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ func New(

app.EvmKeeper = *evmkeeper.NewKeeper(keys[evmtypes.StoreKey], memKeys[evmtypes.MemStoreKey],
app.GetSubspace(evmtypes.ModuleName), app.BankKeeper, &app.AccountKeeper, &app.StakingKeeper,
app.TransferKeeper, wasmkeeper.NewDefaultPermissionKeeper(app.WasmKeeper))
app.TransferKeeper, wasmkeeper.NewDefaultPermissionKeeper(app.WasmKeeper), &app.WasmKeeper)
app.evmRPCConfig, err = evmrpc.ReadConfig(appOpts)
if err != nil {
panic(fmt.Sprintf("error reading EVM config due to %s", err))
Expand Down
3 changes: 2 additions & 1 deletion contracts/test/CW20toERC20PointerTest.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const {getAdmin, queryWasm, executeWasm, deployEvmContract, setupSigners, deployErc20PointerForCw20, deployWasm, WASM,
const {getAdmin, queryWasm, executeWasm, associateWasm, deployEvmContract, setupSigners, deployErc20PointerForCw20, deployWasm, WASM,
registerPointerForCw20
} = require("./lib")
const { expect } = require("chai");
Expand Down Expand Up @@ -102,6 +102,7 @@ describe("CW20 to ERC20 Pointer", function () {
});

it("transfer to contract address should succeed", async function() {
await associateWasm(cw20Pointer);
const respBefore = await queryWasm(cw20Pointer, "balance", {address: admin.seiAddress})
const balanceBefore = respBefore.data.balance;

Expand Down
7 changes: 7 additions & 0 deletions contracts/test/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ async function executeWasm(contractAddress, msg, coins = "0usei") {
return JSON.parse(output);
}

async function associateWasm(contractAddress) {
const command = `seid tx evm associate-contract-address ${contractAddress} --from ${adminKeyName} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`;
const output = await execute(command);
return JSON.parse(output);
}

async function isDocker() {
return new Promise((resolve, reject) => {
exec("docker ps --filter 'name=sei-node-0' --format '{{.Names}}'", (error, stdout, stderr) => {
Expand Down Expand Up @@ -427,6 +433,7 @@ module.exports = {
isDocker,
testAPIEnabled,
incrementPointerVersion,
associateWasm,
WASM,
ABI,
};
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ require (
)

replace (
github.com/CosmWasm/wasmd => github.com/sei-protocol/sei-wasmd v0.1.4
github.com/CosmWasm/wasmd => github.com/sei-protocol/sei-wasmd v0.1.5
github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.3.13
github.com/cosmos/iavl => github.com/sei-protocol/sei-iavl v0.1.9
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1359,8 +1359,8 @@ github.com/sei-protocol/sei-tendermint v0.3.3 h1:zH6xxrSRwHERaj/AcCR76U9daVY3Ub8
github.com/sei-protocol/sei-tendermint v0.3.3/go.mod h1:4LSlJdhl3nf3OmohliwRNUFLOB1XWlrmSodrIP7fLh4=
github.com/sei-protocol/sei-tm-db v0.0.5 h1:3WONKdSXEqdZZeLuWYfK5hP37TJpfaUa13vAyAlvaQY=
github.com/sei-protocol/sei-tm-db v0.0.5/go.mod h1:Cpa6rGyczgthq7/0pI31jys2Fw0Nfrc+/jKdP1prVqY=
github.com/sei-protocol/sei-wasmd v0.1.4 h1:StDThZwOJvLebeK1zTot8pByPd4kdM3AsBaKxLpDQPQ=
github.com/sei-protocol/sei-wasmd v0.1.4/go.mod h1:tv+4SYNzpryEPU1dV44+JFB5dIqpDAEx32RngGFgDeI=
github.com/sei-protocol/sei-wasmd v0.1.5 h1:+yO7mihwVTG34DGr/bF1bRwszUfDeN1EnMBZEY67l3k=
github.com/sei-protocol/sei-wasmd v0.1.5/go.mod h1:vd2qO4GOslLnmlsoAk+zE4D8/OQC1AkTqDY8QBOV09U=
github.com/sei-protocol/tm-db v0.0.4 h1:7Y4EU62Xzzg6wKAHEotm7SXQR0aPLcGhKHkh3qd0tnk=
github.com/sei-protocol/tm-db v0.0.4/go.mod h1:PWsIWOTwdwC7Ow/GUvx8HgUJTO691pBuorIQD8JvwAs=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
Expand Down
8 changes: 8 additions & 0 deletions proto/evm/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ service Msg {
rpc EVMTransaction(MsgEVMTransaction) returns (MsgEVMTransactionResponse);
rpc Send(MsgSend) returns (MsgSendResponse);
rpc RegisterPointer(MsgRegisterPointer) returns (MsgRegisterPointerResponse);
rpc AssociateContractAddress(MsgAssociateContractAddress) returns (MsgAssociateContractAddressResponse);
}

message MsgEVMTransaction {
Expand Down Expand Up @@ -65,3 +66,10 @@ message MsgRegisterPointer {
message MsgRegisterPointerResponse {
string pointer_address = 1;
}

message MsgAssociateContractAddress {
string sender = 1;
string address = 2;
}

message MsgAssociateContractAddressResponse {}
29 changes: 29 additions & 0 deletions x/evm/client/cli/native_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,32 @@ func RegisterEvmPointerCmd() *cobra.Command {

return cmd
}

func AssociateContractAddressCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "associate-contract-address [cw-address]",
Short: `Set address association for a CosmWasm contract.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

addr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
msg := types.NewMsgAssociateContractAddress(clientCtx.GetFromAddress(), addr)
if err := msg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
1 change: 1 addition & 0 deletions x/evm/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(NewAddERCNativePointerProposalTxCmd())
cmd.AddCommand(NewAddERCCW20PointerProposalTxCmd())
cmd.AddCommand(NewAddERCCW721PointerProposalTxCmd())
cmd.AddCommand(AssociateContractAddressCmd())

return cmd
}
Expand Down
3 changes: 3 additions & 0 deletions x/evm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func NewHandler(k *keeper.Keeper) sdk.Handler {
case *types.MsgRegisterPointer:
res, err := msgServer.RegisterPointer(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgAssociateContractAddress:
res, err := msgServer.AssociateContractAddress(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
default:
errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg)
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
Expand Down
7 changes: 0 additions & 7 deletions x/evm/keeper/address.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package keeper

import (
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -29,12 +28,6 @@ func (k *Keeper) DeleteAddressMapping(ctx sdk.Context, seiAddress sdk.AccAddress
}

func (k *Keeper) GetEVMAddress(ctx sdk.Context, seiAddress sdk.AccAddress) (common.Address, bool) {
// CW address has a different length and should always be considered associated
// Note that this association is one-way since CW address is longer than EOA address
// and would need to be cropped.
if len(seiAddress) == wasmtypes.ContractAddrLen {
return common.BytesToAddress(seiAddress), true
}
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.SeiAddressToEVMAddressKey(seiAddress))
addr := common.Address{}
Expand Down
10 changes: 0 additions & 10 deletions x/evm/keeper/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"bytes"
"testing"

wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
"github.com/ethereum/go-ethereum/common"
"github.com/sei-protocol/sei-chain/testutil/keeper"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -52,11 +50,3 @@ func TestGetAddressOrDefault(t *testing.T) {
defaultSeiAddr := k.GetSeiAddressOrDefault(ctx, evmAddr)
require.True(t, bytes.Equal(defaultSeiAddr, evmAddr[:]))
}

func TestGetEVMAddressForCW(t *testing.T) {
k, ctx := keeper.MockEVMKeeper()
cwAddr := wasmkeeper.BuildContractAddress(123, 456)
cwEvmAddr, associated := k.GetEVMAddress(ctx, cwAddr)
require.True(t, associated)
require.Equal(t, common.BytesToAddress(cwAddr), cwEvmAddr)
}
4 changes: 3 additions & 1 deletion x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type Keeper struct {
stakingKeeper *stakingkeeper.Keeper
transferKeeper ibctransferkeeper.Keeper
wasmKeeper *wasmkeeper.PermissionedKeeper
wasmViewKeeper *wasmkeeper.Keeper

cachedFeeCollectorAddressMtx *sync.RWMutex
cachedFeeCollectorAddress *common.Address
Expand Down Expand Up @@ -115,7 +116,7 @@ func (ctx *ReplayChainContext) GetHeader(hash common.Hash, number uint64) *ethty
func NewKeeper(
storeKey sdk.StoreKey, memStoreKey sdk.StoreKey, paramstore paramtypes.Subspace,
bankKeeper bankkeeper.Keeper, accountKeeper *authkeeper.AccountKeeper, stakingKeeper *stakingkeeper.Keeper,
transferKeeper ibctransferkeeper.Keeper, wasmKeeper *wasmkeeper.PermissionedKeeper) *Keeper {
transferKeeper ibctransferkeeper.Keeper, wasmKeeper *wasmkeeper.PermissionedKeeper, wasmViewKeeper *wasmkeeper.Keeper) *Keeper {
if !paramstore.HasKeyTable() {
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
}
Expand All @@ -128,6 +129,7 @@ func NewKeeper(
stakingKeeper: stakingKeeper,
transferKeeper: transferKeeper,
wasmKeeper: wasmKeeper,
wasmViewKeeper: wasmViewKeeper,
pendingTxs: make(map[string][]*PendingTx),
nonceMx: &sync.RWMutex{},
cachedFeeCollectorAddressMtx: &sync.RWMutex{},
Expand Down
20 changes: 20 additions & 0 deletions x/evm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"context"
"encoding/json"
"errors"
"fmt"
"math"
"math/big"
Expand Down Expand Up @@ -323,3 +324,22 @@ func (server msgServer) RegisterPointer(goCtx context.Context, msg *types.MsgReg
}
return &types.MsgRegisterPointerResponse{PointerAddress: pointerAddr.String()}, err
}

func (server msgServer) AssociateContractAddress(goCtx context.Context, msg *types.MsgAssociateContractAddress) (*types.MsgAssociateContractAddressResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
addr := sdk.MustAccAddressFromBech32(msg.Address) // already validated
// check if address is for a contract
if server.wasmViewKeeper.GetContractInfo(ctx, addr) == nil {
return nil, errors.New("no wasm contract found at the given address")
}
evmAddr := common.BytesToAddress(addr)
existingEvmAddr, ok := server.GetEVMAddress(ctx, addr)
if ok {
if existingEvmAddr.Cmp(evmAddr) != 0 {
ctx.Logger().Error(fmt.Sprintf("unexpected associated EVM address %s exists for contract %s: expecting %s", existingEvmAddr.Hex(), addr.String(), evmAddr.Hex()))
}
return nil, errors.New("contract already has an associated address")
}
server.SetAddressMapping(ctx, addr, evmAddr)
return &types.MsgAssociateContractAddressResponse{}, nil
}
37 changes: 37 additions & 0 deletions x/evm/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,40 @@ func TestEvmError(t *testing.T) {
require.Nil(t, err)
require.Equal(t, receipt.VmError, res.EvmTxInfo.VmError)
}

func TestAssociateContractAddress(t *testing.T) {
k, ctx := testkeeper.MockEVMKeeper()
msgServer := keeper.NewMsgServerImpl(k)
dummySeiAddr, dummyEvmAddr := testkeeper.MockAddressPair()
res, err := msgServer.RegisterPointer(sdk.WrapSDKContext(ctx), &types.MsgRegisterPointer{
Sender: dummySeiAddr.String(),
PointerType: types.PointerType_ERC20,
ErcAddress: dummyEvmAddr.Hex(),
})
require.Nil(t, err)
_, err = msgServer.AssociateContractAddress(sdk.WrapSDKContext(ctx), &types.MsgAssociateContractAddress{
Sender: dummySeiAddr.String(),
Address: res.PointerAddress,
})
require.Nil(t, err)
associatedEvmAddr, found := k.GetEVMAddress(ctx, sdk.MustAccAddressFromBech32(res.PointerAddress))
require.True(t, found)
require.Equal(t, common.BytesToAddress(sdk.MustAccAddressFromBech32(res.PointerAddress)), associatedEvmAddr)
associatedSeiAddr, found := k.GetSeiAddress(ctx, associatedEvmAddr)
require.True(t, found)
require.Equal(t, res.PointerAddress, associatedSeiAddr.String())
// setting for an associated address would fail
_, err = msgServer.AssociateContractAddress(sdk.WrapSDKContext(ctx), &types.MsgAssociateContractAddress{
Sender: dummySeiAddr.String(),
Address: res.PointerAddress,
})
require.NotNil(t, err)
require.Contains(t, err.Error(), "contract already has an associated address")
// setting for a non-contract would fail
_, err = msgServer.AssociateContractAddress(sdk.WrapSDKContext(ctx), &types.MsgAssociateContractAddress{
Sender: dummySeiAddr.String(),
Address: dummySeiAddr.String(),
})
require.NotNil(t, err)
require.Contains(t, err.Error(), "no wasm contract found at the given address")
}
1 change: 1 addition & 0 deletions x/evm/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
&MsgEVMTransaction{},
&MsgSend{},
&MsgRegisterPointer{},
&MsgAssociateContractAddress{},
)
registry.RegisterInterface(
"seiprotocol.seichain.evm.TxData",
Expand Down
49 changes: 49 additions & 0 deletions x/evm/types/message_associate_contract_address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package types

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

const TypeMsgAssociateContractAddress = "evm_associate_contract_address"

var (
_ sdk.Msg = &MsgAssociateContractAddress{}
)

func NewMsgAssociateContractAddress(sender sdk.AccAddress, addr sdk.AccAddress) *MsgAssociateContractAddress {
return &MsgAssociateContractAddress{Sender: sender.String(), Address: addr.String()}
}

func (msg *MsgAssociateContractAddress) Route() string {
return RouterKey
}

func (msg *MsgAssociateContractAddress) Type() string {
return TypeMsgAssociateContractAddress
}

func (msg *MsgAssociateContractAddress) GetSigners() []sdk.AccAddress {
from, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
panic(err)
}
return []sdk.AccAddress{from}
}

func (msg *MsgAssociateContractAddress) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
}

func (msg *MsgAssociateContractAddress) ValidateBasic() error {
_, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
}

if _, err := sdk.AccAddressFromBech32(msg.Address); err != nil {
return sdkerrors.ErrInvalidAddress
}

return nil
}
2 changes: 1 addition & 1 deletion x/evm/types/message_register_pointer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
const TypeMsgRegisterPointer = "evm_register_pointer"

var (
_ sdk.Msg = &MsgSend{}
_ sdk.Msg = &MsgRegisterPointer{}
)

func NewMsgRegisterERC20Pointer(sender sdk.AccAddress, ercAddress common.Address) *MsgRegisterPointer {
Expand Down
Loading

0 comments on commit c725b57

Please sign in to comment.