Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add message type to associate CW contract address #1681

Merged
merged 4 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
};
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to handle the case where ok == false ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's the case where we set the association (because no existing association)

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
Loading