Skip to content

feat!: permissionless erc20 registration #84

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

### FEATURES

- permissionless erc20 registration to cosmos coin conversion

### STATE BREAKING

- Refactored evmos/os into cosmos/evm
Expand Down
253 changes: 128 additions & 125 deletions api/cosmos/evm/erc20/v1/tx.pulsar.go

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions proto/cosmos/evm/erc20/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

import "amino/amino.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/evm/erc20/v1/genesis.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "cosmos/evm/erc20/v1/genesis.proto";

option go_package = "github.com/cosmos/evm/x/erc20/types";

Expand Down Expand Up @@ -50,7 +50,7 @@
(amino.dont_omitempty) = true
];
// receiver is the bech32 address to receive native Cosmos coins
string receiver = 3;
string receiver = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// sender is the hex address from the owner of the given ERC20 tokens
string sender = 4;
}
Expand All @@ -67,7 +67,7 @@
string receiver = 2;
// sender is the cosmos bech32 address from the owner of the given Cosmos
// coins
string sender = 3;
string sender = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}

// MsgConvertCoinResponse returns no fields
Expand Down Expand Up @@ -97,10 +97,10 @@
// an Erc20 contract token pair.
message MsgRegisterERC20 {
option (amino.name) = "cosmos/evm/x/erc20/MsgRegisterERC20";
option (cosmos.msg.v1.signer) = "authority";
option (cosmos.msg.v1.signer) = "signer";

// authority is the address of the governance account.
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// signer is the address registering the erc20 pairs
string signer = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

Check failure on line 103 in proto/cosmos/evm/erc20/v1/tx.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "1" with name "signer" on message "MsgRegisterERC20" changed option "json_name" from "authority" to "signer".

Check failure on line 103 in proto/cosmos/evm/erc20/v1/tx.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "1" on message "MsgRegisterERC20" changed name from "authority" to "signer".

// erc20addresses is a slice of ERC20 token contract hex addresses
repeated string erc20addresses = 2;
Expand Down
2 changes: 1 addition & 1 deletion testutil/integration/os/utils/erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func RegisterERC20(tf factory.TxFactory, network network.Network, data ERC20Regi
}

proposal := erc20types.MsgRegisterERC20{
Authority: authtypes.NewModuleAddress("gov").String(),
Signer: authtypes.NewModuleAddress("gov").String(),
Erc20Addresses: data.Addresses,
}

Expand Down
31 changes: 31 additions & 0 deletions x/erc20/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func NewTxCmd() *cobra.Command {

txCmd.AddCommand(
NewConvertERC20Cmd(),
NewMsgRegisterERC20Cmd(),
)
return txCmd
}
Expand Down Expand Up @@ -79,3 +80,33 @@ func NewConvertERC20Cmd() *cobra.Command {
flags.AddTxFlagsToCmd(cmd)
return cmd
}

func NewMsgRegisterERC20Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "register-erc20 [CONTRACT_ADDRESS...]",
Short: "Register a native ERC20 token",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

for _, contract := range args {
if err := cosmosevmtypes.ValidateAddress(contract); err != nil {
return fmt.Errorf("invalid ERC20 contract address %w", err)
}
}

msg := &types.MsgRegisterERC20{
Signer: cliCtx.GetFromAddress().String(),
Erc20Addresses: args,
}

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

flags.AddTxFlagsToCmd(cmd)
return cmd
}
9 changes: 2 additions & 7 deletions x/erc20/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,20 +275,15 @@ func (k *Keeper) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams)
return &types.MsgUpdateParamsResponse{}, nil
}

// RegisterERC20 implements the gRPC MsgServer interface. After a successful governance vote
// it updates creates the token pair for an ERC20 contract if the requested authority
// is the Cosmos SDK governance module account
// RegisterERC20 implements the gRPC MsgServer interface. Any account can permissionlessly
// register a native ERC20 contract to map to a Cosmos Coin.
func (k *Keeper) RegisterERC20(goCtx context.Context, req *types.MsgRegisterERC20) (*types.MsgRegisterERC20Response, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
// Check if the conversion is globally enabled
if !k.IsERC20Enabled(ctx) {
return nil, types.ErrERC20Disabled.Wrap("registration is currently disabled by governance")
}

if err := k.validateAuthority(req.Authority); err != nil {
return nil, err
}

for _, addr := range req.Erc20Addresses {
if !common.IsHexAddress(addr) {
return nil, errortypes.ErrInvalidAddress.Wrapf("invalid ERC20 contract address: %s", addr)
Expand Down
2 changes: 1 addition & 1 deletion x/erc20/keeper/proposals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (suite *KeeperTestSuite) TestRegisterERC20() {
tc.malleate()

_, err = suite.network.App.Erc20Keeper.RegisterERC20(ctx, &types.MsgRegisterERC20{
Authority: authtypes.NewModuleAddress("gov").String(),
Signer: authtypes.NewModuleAddress("gov").String(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we test this when the governance module isn't the signer so we actually validate the permissionless case?

Erc20Addresses: []string{contractAddr.Hex()},
})
metadata, found := suite.network.App.BankKeeper.GetDenomMetaData(ctx, coinName)
Expand Down
5 changes: 5 additions & 0 deletions x/erc20/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ func (m MsgUpdateParams) GetSignBytes() []byte {

// ValidateBasic does a sanity check of the provided data
func (m *MsgRegisterERC20) ValidateBasic() error {
_, err := sdk.AccAddressFromBech32(m.Signer)
if err != nil {
return errorsmod.Wrap(err, "invalid signer address")
}

for _, addr := range m.Erc20Addresses {
if !common.IsHexAddress(addr) {
return errortypes.ErrInvalidAddress.Wrapf("invalid ERC20 contract address: %s", addr)
Expand Down
118 changes: 60 additions & 58 deletions x/erc20/types/tx.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading