Skip to content

Commit 4208de3

Browse files
fix(erc20): Register code hash also for native precompiles (evmos#2962)
* add code hash also for native precompiles not only dynamic precompiles * address review comments * add changelog entry * add required build inputs for nix build on mac os
1 parent 4ad93fe commit 4208de3

File tree

9 files changed

+65
-48
lines changed

9 files changed

+65
-48
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
4444
- (app) [#2878](https://github.com/evmos/evmos/pull/2878) Changed `x/feemarket` BaseFee to Decimal. `x/evm` uses `FeeMarketWrapper` to manage EVM coin with different decimals.
4545
- (precompiles) [#2929](https://github.com/evmos/evmos/pull/2929) Distribution: scale balance change entries to the statedb journal to support different EVM denom precision.
4646
- (precompiles) [#2927](https://github.com/evmos/evmos/pull/2927) Erc20: scale balance change entries to the statedb journal to support different EVM denom precision.
47+
- (erc20) [#2962](https://github.com/evmos/evmos/pull/2962) Register ERC-20 code hash also for native ERC-20 extensions.
4748

4849
### Improvements
4950

app/upgrades/v19_2/upgrades_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,6 @@ func TestAddCodeToERC20Extensions(t *testing.T) {
144144

145145
ctx := network.GetContext()
146146

147-
// check code does not exist
148-
code := network.App.EvmKeeper.GetCode(ctx, expCodeHash)
149-
require.Len(t, code, 0)
150-
151147
// seed the token pairs
152148
for _, p := range tokenPairsSeed {
153149
network.App.Erc20Keeper.SetToken(ctx, p)
@@ -159,7 +155,7 @@ func TestAddCodeToERC20Extensions(t *testing.T) {
159155
err := v192.AddCodeToERC20Extensions(ctx, logger, network.App.Erc20Keeper)
160156
require.NoError(t, err)
161157

162-
code = network.App.EvmKeeper.GetCode(ctx, expCodeHash)
158+
code := network.App.EvmKeeper.GetCode(ctx, expCodeHash)
163159
require.True(t, len(code) > 0)
164160

165161
pairs := network.App.Erc20Keeper.GetTokenPairs(ctx)

default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
, buildPackages
44
, darwin
55
, fetchFromGitHub
6+
, pkgs
67
, stdenv
78
, rev ? "dirty"
89
, rocksdb

x/erc20/keeper/dynamic_precompiles.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func (k Keeper) RegisterERC20Extension(ctx sdk.Context, denom string) (*types.To
3737

3838
// RegisterERC20CodeHash sets the codehash for the erc20 precompile account
3939
// if the bytecode for the erc20 codehash does not exists, it stores it.
40-
func (k Keeper) RegisterERC20CodeHash(ctx sdk.Context, address common.Address) error {
40+
func (k Keeper) RegisterERC20CodeHash(ctx sdk.Context, erc20Addr common.Address) error {
4141
var (
4242
// bytecode and codeHash is the same for all IBC coins
4343
// cause they're all using the same contract
@@ -55,34 +55,33 @@ func (k Keeper) RegisterERC20CodeHash(ctx sdk.Context, address common.Address) e
5555
balance = common.Big0
5656
)
5757
// keep balance and nonce if account exists
58-
if acc := k.evmKeeper.GetAccount(ctx, address); acc != nil {
58+
if acc := k.evmKeeper.GetAccount(ctx, erc20Addr); acc != nil {
5959
nonce = acc.Nonce
6060
balance = acc.Balance
6161
}
6262

63-
return k.evmKeeper.SetAccount(ctx, address, statedb.Account{
63+
return k.evmKeeper.SetAccount(ctx, erc20Addr, statedb.Account{
6464
CodeHash: codeHash,
6565
Nonce: nonce,
6666
Balance: balance,
6767
})
6868
}
6969

7070
// UnRegisterERC20CodeHash sets the codehash for the account to an empty one
71-
func (k Keeper) UnRegisterERC20CodeHash(ctx sdk.Context, erc20Address string) error {
71+
func (k Keeper) UnRegisterERC20CodeHash(ctx sdk.Context, erc20Addr common.Address) error {
7272
emptyCodeHash := crypto.Keccak256(nil)
73-
contractAddr := common.HexToAddress(erc20Address)
7473

7574
var (
7675
nonce uint64
7776
balance = common.Big0
7877
)
7978
// keep balance and nonce if account exists
80-
if acc := k.evmKeeper.GetAccount(ctx, contractAddr); acc != nil {
79+
if acc := k.evmKeeper.GetAccount(ctx, erc20Addr); acc != nil {
8180
nonce = acc.Nonce
8281
balance = acc.Balance
8382
}
8483

85-
return k.evmKeeper.SetAccount(ctx, contractAddr, statedb.Account{
84+
return k.evmKeeper.SetAccount(ctx, erc20Addr, statedb.Account{
8685
CodeHash: emptyCodeHash,
8786
Nonce: nonce,
8887
Balance: balance,

x/erc20/keeper/dynamic_precompiles_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (suite *KeeperTestSuite) TestRegisterERC20CodeHash() {
7171
suite.Require().Equal(uint64(0), acc.Nonce)
7272
}
7373

74-
err = suite.network.App.Erc20Keeper.UnRegisterERC20CodeHash(ctx, account.Hex())
74+
err = suite.network.App.Erc20Keeper.UnRegisterERC20CodeHash(ctx, account)
7575
suite.Require().NoError(err)
7676

7777
acc = suite.network.App.EvmKeeper.GetAccount(ctx, account)

x/erc20/keeper/params.go

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,43 +23,71 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
2323
return types.NewParams(enableErc20, nativePrecompiles, dynamicPrecompiles)
2424
}
2525

26-
func (k Keeper) UpdateCodeHash(ctx sdk.Context, updatedDynamicPrecompiles []string) error {
27-
// if a precompile is disabled or deleted in the params, we should remove the codehash
26+
// UpdateCodeHash takes in the updated parameters and
27+
// compares the new set of native and dynamic precompiles to the current
28+
// parameter set.
29+
//
30+
// If there is a diff, the ERC-20 code hash for all precompiles that are removed from the list
31+
// will be removed from the store. Meanwhile, for all newly added precompiles the code hash will be
32+
// registered.
33+
func (k Keeper) UpdateCodeHash(ctx sdk.Context, newParams types.Params) error {
34+
oldNativePrecompiles := k.getNativePrecompiles(ctx)
2835
oldDynamicPrecompiles := k.getDynamicPrecompiles(ctx)
29-
disabledPrecompiles, enabledPrecompiles := types.GetDisabledAndEnabledPrecompiles(oldDynamicPrecompiles, updatedDynamicPrecompiles)
30-
for _, precompile := range disabledPrecompiles {
31-
if err := k.UnRegisterERC20CodeHash(ctx, precompile); err != nil {
36+
37+
if err := k.RegisterOrUnregisterERC20CodeHashes(ctx, oldDynamicPrecompiles, newParams.DynamicPrecompiles); err != nil {
38+
return err
39+
}
40+
41+
return k.RegisterOrUnregisterERC20CodeHashes(ctx, oldNativePrecompiles, newParams.NativePrecompiles)
42+
}
43+
44+
// RegisterOrUnregisterERC20CodeHashes takes two arrays of precompiles as its argument:
45+
// - previously registered precompiles
46+
// - new set of precompiles to be registered
47+
//
48+
// It then compares the two arrays and registers the code hash for all precompiles that are newly added
49+
// and unregisters the code hash for all precompiles that are removed from the list.
50+
func (k Keeper) RegisterOrUnregisterERC20CodeHashes(ctx sdk.Context, oldPrecompiles, newPrecompiles []string) error {
51+
for _, precompile := range oldPrecompiles {
52+
if slices.Contains(newPrecompiles, precompile) {
53+
continue
54+
}
55+
56+
if err := k.UnRegisterERC20CodeHash(ctx, common.HexToAddress(precompile)); err != nil {
3257
return err
3358
}
3459
}
3560

36-
// if a precompile is added we should register the account with the erc20 codehash
37-
for _, precompile := range enabledPrecompiles {
61+
for _, precompile := range newPrecompiles {
62+
if slices.Contains(oldPrecompiles, precompile) {
63+
continue
64+
}
65+
3866
if err := k.RegisterERC20CodeHash(ctx, common.HexToAddress(precompile)); err != nil {
3967
return err
4068
}
4169
}
70+
4271
return nil
4372
}
4473

4574
// SetParams sets the erc20 parameters to the param space.
46-
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error {
47-
// and keep params equal between different executions
48-
slices.Sort(params.DynamicPrecompiles)
49-
slices.Sort(params.NativePrecompiles)
75+
func (k Keeper) SetParams(ctx sdk.Context, newParams types.Params) error {
76+
// sort to keep params equal between different executions
77+
slices.Sort(newParams.DynamicPrecompiles)
78+
slices.Sort(newParams.NativePrecompiles)
5079

51-
if err := params.Validate(); err != nil {
80+
if err := newParams.Validate(); err != nil {
5281
return err
5382
}
5483

55-
// update the codehash for enabled or disabled dynamic precompiles
56-
if err := k.UpdateCodeHash(ctx, params.DynamicPrecompiles); err != nil {
84+
if err := k.UpdateCodeHash(ctx, newParams); err != nil {
5785
return err
5886
}
5987

60-
k.setERC20Enabled(ctx, params.EnableErc20)
61-
k.setDynamicPrecompiles(ctx, params.DynamicPrecompiles)
62-
k.setNativePrecompiles(ctx, params.NativePrecompiles)
88+
k.setERC20Enabled(ctx, newParams.EnableErc20)
89+
k.setDynamicPrecompiles(ctx, newParams.DynamicPrecompiles)
90+
k.setNativePrecompiles(ctx, newParams.NativePrecompiles)
6391
return nil
6492
}
6593

x/erc20/types/utils.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package types
66
import (
77
"fmt"
88
"regexp"
9-
"slices"
109
"strings"
1110

1211
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -92,19 +91,3 @@ func IsModuleAccount(acc sdk.AccountI) bool {
9291
_, isModuleAccount := acc.(sdk.ModuleAccountI)
9392
return isModuleAccount
9493
}
95-
96-
func GetDisabledAndEnabledPrecompiles(oldDynamicPrecompiles, newDynamicPrecompiles []string) (disabledPrecompiles, enabledPrecompiles []string) {
97-
for _, precompile := range oldDynamicPrecompiles {
98-
if !slices.Contains(newDynamicPrecompiles, precompile) {
99-
disabledPrecompiles = append(disabledPrecompiles, precompile)
100-
}
101-
}
102-
103-
for _, precompile := range newDynamicPrecompiles {
104-
if !slices.Contains(oldDynamicPrecompiles, precompile) {
105-
enabledPrecompiles = append(enabledPrecompiles, precompile)
106-
}
107-
}
108-
109-
return disabledPrecompiles, enabledPrecompiles
110-
}

x/evm/genesis_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/ethereum/go-ethereum/crypto"
1010
"github.com/evmos/evmos/v20/contracts"
1111
"github.com/evmos/evmos/v20/crypto/ethsecp256k1"
12+
"github.com/evmos/evmos/v20/precompiles/erc20"
1213
testfactory "github.com/evmos/evmos/v20/testutil/integration/evmos/factory"
1314
testhandler "github.com/evmos/evmos/v20/testutil/integration/evmos/grpc"
1415
testkeyring "github.com/evmos/evmos/v20/testutil/integration/evmos/keyring"
@@ -234,12 +235,13 @@ func TestExportGenesis(t *testing.T) {
234235
require.NoError(t, ts.network.NextBlock(), "failed to advance block")
235236

236237
genState := evm.ExportGenesis(ts.network.GetContext(), ts.network.App.EvmKeeper)
237-
require.Len(t, genState.Accounts, 2, "expected only one smart contract in the exported genesis")
238+
require.Len(t, genState.Accounts, 3, "expected 3 smart contracts in the exported genesis") // NOTE: 2 deployed above + 1 for the aevmos denomination ERC-20 pair
238239

239240
genAddresses := make([]string, 0, len(genState.Accounts))
240241
for _, acc := range genState.Accounts {
241242
genAddresses = append(genAddresses, acc.Address)
242243
}
243244
require.Contains(t, genAddresses, contractAddr.Hex(), "expected contract 1 address in exported genesis")
244245
require.Contains(t, genAddresses, contractAddr2.Hex(), "expected contract 2 address in exported genesis")
246+
require.Contains(t, genAddresses, erc20.WEVMOSContractMainnet, "expected mainnet aevmos contract address in exported genesis")
245247
}

x/evm/keeper/statedb_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package keeper_test
22

33
import (
4+
"bytes"
45
"fmt"
56
"math/big"
67
"testing"
@@ -18,6 +19,7 @@ import (
1819
ethtypes "github.com/ethereum/go-ethereum/core/types"
1920
"github.com/ethereum/go-ethereum/crypto"
2021
"github.com/evmos/evmos/v20/contracts"
22+
"github.com/evmos/evmos/v20/precompiles/erc20"
2123
testfactory "github.com/evmos/evmos/v20/testutil/integration/evmos/factory"
2224
testhandler "github.com/evmos/evmos/v20/testutil/integration/evmos/grpc"
2325
testkeyring "github.com/evmos/evmos/v20/testutil/integration/evmos/keyring"
@@ -402,6 +404,11 @@ func TestIterateContracts(t *testing.T) {
402404
)
403405

404406
network.App.EvmKeeper.IterateContracts(network.GetContext(), func(addr common.Address, codeHash common.Hash) bool {
407+
// NOTE: we only care about the 2 contracts deployed above, not the ERC20 native precompile for the aevmos denomination
408+
if bytes.Equal(addr.Bytes(), common.HexToAddress(erc20.WEVMOSContractMainnet).Bytes()) {
409+
return false
410+
}
411+
405412
foundAddrs = append(foundAddrs, addr)
406413
foundHashes = append(foundHashes, codeHash)
407414
return false

0 commit comments

Comments
 (0)