Skip to content

Commit 04dac0d

Browse files
authored
feat!: remove boilerplate code from stateful precompiles (#577)
* Problem: stateful precompiles have many boilerplate code Solution: - refactor the precompiles code with (hopefully) equivalent transformations. changelog cleanup handle balance handler error return cleanup avoid naked return fix lint fix werc20 fix staking precompile fix panic recovery return error fix staking test case NewPrecompile don't return error * don't assume input format in base * fix test * Update precompiles/common/precompile.go * add api-breaking changelog * add comments * cleanup * re-structure * fix build * fix test
1 parent 84d98a6 commit 04dac0d

File tree

31 files changed

+413
-669
lines changed

31 files changed

+413
-669
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
- [\#609](https://github.com/cosmos/evm/pull/609) Make `erc20Keeper` optional in the EVM keeper
3535
- [\#624](https://github.com/cosmos/evm/pull/624) Cleanup unnecessary `fix-revert-gas-refund-height`.
3636
- [\#635](https://github.com/cosmos/evm/pull/635) Move DefaultStaticPrecompiles to /evm and allow projects to set it by default alongside the keeper.
37-
37+
- [\#577](https://github.com/cosmos/evm/pull/577) Cleanup precompiles boilerplate code.
3838

3939
### FEATURES
4040

@@ -48,6 +48,7 @@
4848

4949
- [\#477](https://github.com/cosmos/evm/pull/477) Refactor precompile constructors to accept keeper interfaces instead of concrete implementations, breaking the existing `NewPrecompile` function signatures.
5050
- [\#594](https://github.com/cosmos/evm/pull/594) Remove all usage of x/params
51+
- [\#577](https://github.com/cosmos/evm/pull/577) Changed the way to create a stateful precompile based on the cmn.Precompile, change `NewPrecompile` to not return error.
5152

5253
## v0.4.1
5354

evmd/tests/ibc/ics20_precompile_transfer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ func (suite *ICS20TransferTestSuite) SetupTest() {
4646
suite.chainB = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(2))
4747

4848
evmAppA := suite.chainA.App.(*evmd.EVMD)
49-
suite.chainAPrecompile, _ = ics20.NewPrecompile(
49+
suite.chainAPrecompile = ics20.NewPrecompile(
5050
evmAppA.BankKeeper,
5151
*evmAppA.StakingKeeper,
5252
evmAppA.TransferKeeper,
5353
evmAppA.IBCKeeper.ChannelKeeper,
5454
)
5555
evmAppB := suite.chainB.App.(*evmd.EVMD)
56-
suite.chainBPrecompile, _ = ics20.NewPrecompile(
56+
suite.chainBPrecompile = ics20.NewPrecompile(
5757
evmAppB.BankKeeper,
5858
*evmAppB.StakingKeeper,
5959
evmAppB.TransferKeeper,

evmd/tests/ibc/v2_ics20_precompile_transfer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ func (suite *ICS20TransferV2TestSuite) SetupTest() {
4747
suite.chainB = suite.coordinator.GetChain(evmibctesting.GetEvmChainID(2))
4848

4949
evmAppA := suite.chainA.App.(*evmd.EVMD)
50-
suite.chainAPrecompile, _ = ics20.NewPrecompile(
50+
suite.chainAPrecompile = ics20.NewPrecompile(
5151
evmAppA.BankKeeper,
5252
*evmAppA.StakingKeeper,
5353
evmAppA.TransferKeeper,
5454
evmAppA.IBCKeeper.ChannelKeeper,
5555
)
5656
evmAppB := suite.chainB.App.(*evmd.EVMD)
57-
suite.chainBPrecompile, _ = ics20.NewPrecompile(
57+
suite.chainBPrecompile = ics20.NewPrecompile(
5858
evmAppB.BankKeeper,
5959
*evmAppB.StakingKeeper,
6060
evmAppB.TransferKeeper,

precompiles/bank/bank.go

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import (
1111

1212
"github.com/ethereum/go-ethereum/accounts/abi"
1313
"github.com/ethereum/go-ethereum/common"
14-
"github.com/ethereum/go-ethereum/core/tracing"
1514
"github.com/ethereum/go-ethereum/core/vm"
1615

1716
cmn "github.com/cosmos/evm/precompiles/common"
1817
evmtypes "github.com/cosmos/evm/x/vm/types"
1918

2019
storetypes "cosmossdk.io/store/types"
20+
21+
sdk "github.com/cosmos/cosmos-sdk/types"
2122
)
2223

2324
const (
@@ -33,14 +34,27 @@ const (
3334

3435
var _ vm.PrecompiledContract = &Precompile{}
3536

36-
// Embed abi json file to the executable binary. Needed when importing as dependency.
37-
//
38-
//go:embed abi.json
39-
var f embed.FS
37+
var (
38+
// Embed abi json file to the executable binary. Needed when importing as dependency.
39+
//
40+
//go:embed abi.json
41+
f embed.FS
42+
ABI abi.ABI
43+
)
44+
45+
func init() {
46+
var err error
47+
ABI, err = cmn.LoadABI(f, "abi.json")
48+
if err != nil {
49+
panic(err)
50+
}
51+
}
4052

4153
// Precompile defines the bank precompile
4254
type Precompile struct {
4355
cmn.Precompile
56+
57+
abi.ABI
4458
bankKeeper cmn.BankKeeper
4559
erc20Keeper cmn.ERC20Keeper
4660
}
@@ -50,28 +64,19 @@ type Precompile struct {
5064
func NewPrecompile(
5165
bankKeeper cmn.BankKeeper,
5266
erc20Keeper cmn.ERC20Keeper,
53-
) (*Precompile, error) {
54-
newABI, err := cmn.LoadABI(f, "abi.json")
55-
if err != nil {
56-
return nil, err
57-
}
58-
67+
) *Precompile {
5968
// NOTE: we set an empty gas configuration to avoid extra gas costs
6069
// during the run execution
61-
p := &Precompile{
70+
return &Precompile{
6271
Precompile: cmn.Precompile{
63-
ABI: newABI,
6472
KvGasConfig: storetypes.GasConfig{},
6573
TransientKVGasConfig: storetypes.GasConfig{},
74+
ContractAddress: common.HexToAddress(evmtypes.BankPrecompileAddress),
6675
},
76+
ABI: ABI,
6777
bankKeeper: bankKeeper,
6878
erc20Keeper: erc20Keeper,
6979
}
70-
71-
// SetAddress defines the address of the bank compile contract.
72-
p.SetAddress(common.HexToAddress(evmtypes.BankPrecompileAddress))
73-
74-
return p, nil
7580
}
7681

7782
// RequiredGas calculates the precompiled contract's base gas rate.
@@ -101,40 +106,33 @@ func (p Precompile) RequiredGas(input []byte) uint64 {
101106
return 0
102107
}
103108

104-
// Run executes the precompiled contract bank query methods defined in the ABI.
105-
func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz []byte, err error) {
106-
ctx, _, method, initialGas, args, err := p.RunSetup(evm, contract, readOnly, p.IsTransaction)
109+
func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]byte, error) {
110+
return p.RunNativeAction(evm, contract, func(ctx sdk.Context) ([]byte, error) {
111+
return p.Execute(ctx, contract, readonly)
112+
})
113+
}
114+
115+
// Execute executes the precompiled contract bank query methods defined in the ABI.
116+
func (p Precompile) Execute(ctx sdk.Context, contract *vm.Contract, readOnly bool) ([]byte, error) {
117+
method, args, err := cmn.SetupABI(p.ABI, contract, readOnly, p.IsTransaction)
107118
if err != nil {
108119
return nil, err
109120
}
110121

111-
// This handles any out of gas errors that may occur during the execution of a precompile query.
112-
// It avoids panics and returns the out of gas error so the EVM can continue gracefully.
113-
defer cmn.HandleGasError(ctx, contract, initialGas, &err)()
114-
122+
var bz []byte
115123
switch method.Name {
116124
// Bank queries
117125
case BalancesMethod:
118-
bz, err = p.Balances(ctx, contract, method, args)
126+
bz, err = p.Balances(ctx, method, args)
119127
case TotalSupplyMethod:
120-
bz, err = p.TotalSupply(ctx, contract, method, args)
128+
bz, err = p.TotalSupply(ctx, method, args)
121129
case SupplyOfMethod:
122-
bz, err = p.SupplyOf(ctx, contract, method, args)
130+
bz, err = p.SupplyOf(ctx, method, args)
123131
default:
124132
return nil, fmt.Errorf(cmn.ErrUnknownMethod, method.Name)
125133
}
126134

127-
if err != nil {
128-
return nil, err
129-
}
130-
131-
cost := ctx.GasMeter().GasConsumed() - initialGas
132-
133-
if !contract.UseGas(cost, nil, tracing.GasChangeCallPrecompiledContract) {
134-
return nil, vm.ErrOutOfGas
135-
}
136-
137-
return bz, nil
135+
return bz, err
138136
}
139137

140138
// IsTransaction checks if the given method name corresponds to a transaction or query.

precompiles/bank/query.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"math/big"
66

77
"github.com/ethereum/go-ethereum/accounts/abi"
8-
"github.com/ethereum/go-ethereum/core/vm"
98

109
sdk "github.com/cosmos/cosmos-sdk/types"
1110
)
@@ -29,7 +28,6 @@ const (
2928
// balanceOf call for each token returned.
3029
func (p Precompile) Balances(
3130
ctx sdk.Context,
32-
_ *vm.Contract,
3331
method *abi.Method,
3432
args []interface{},
3533
) ([]byte, error) {
@@ -73,7 +71,6 @@ func (p Precompile) Balances(
7371
// call for each token returned.
7472
func (p Precompile) TotalSupply(
7573
ctx sdk.Context,
76-
_ *vm.Contract,
7774
method *abi.Method,
7875
_ []interface{},
7976
) ([]byte, error) {
@@ -112,7 +109,6 @@ func (p Precompile) TotalSupply(
112109
// stored in the x/bank.
113110
func (p Precompile) SupplyOf(
114111
ctx sdk.Context,
115-
_ *vm.Contract,
116112
method *abi.Method,
117113
args []interface{},
118114
) ([]byte, error) {

precompiles/bech32/bech32.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,21 @@ import (
1414

1515
var _ vm.PrecompiledContract = &Precompile{}
1616

17-
// Embed abi json file to the executable binary. Needed when importing as dependency.
18-
//
19-
//go:embed abi.json
20-
var f embed.FS
17+
var (
18+
// Embed abi json file to the executable binary. Needed when importing as dependency.
19+
//
20+
//go:embed abi.json
21+
f embed.FS
22+
ABI abi.ABI
23+
)
24+
25+
func init() {
26+
var err error
27+
ABI, err = cmn.LoadABI(f, "abi.json")
28+
if err != nil {
29+
panic(err)
30+
}
31+
}
2132

2233
// Precompile defines the precompiled contract for Bech32 encoding.
2334
type Precompile struct {
@@ -28,17 +39,12 @@ type Precompile struct {
2839
// NewPrecompile creates a new bech32 Precompile instance as a
2940
// PrecompiledContract interface.
3041
func NewPrecompile(baseGas uint64) (*Precompile, error) {
31-
newABI, err := cmn.LoadABI(f, "abi.json")
32-
if err != nil {
33-
return nil, err
34-
}
35-
3642
if baseGas == 0 {
3743
return nil, fmt.Errorf("baseGas cannot be zero")
3844
}
3945

4046
return &Precompile{
41-
ABI: newABI,
47+
ABI: ABI,
4248
baseGas: baseGas,
4349
}, nil
4450
}

0 commit comments

Comments
 (0)