@@ -3,6 +3,8 @@ package bank2
33import (
44 "encoding/binary"
55 "encoding/hex"
6+ "errors"
7+ "fmt"
68 "math"
79 "math/big"
810
@@ -12,10 +14,11 @@ import (
1214
1315 _ "embed"
1416
15- "github.com/cosmos/evm/x/vm/statedb "
17+ cmn "github.com/cosmos/evm/precompiles/common "
1618 evmtypes "github.com/cosmos/evm/x/vm/types"
1719
1820 sdkmath "cosmossdk.io/math"
21+ storetypes "cosmossdk.io/store/types"
1922
2023 sdk "github.com/cosmos/cosmos-sdk/types"
2124 banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
3336 Create2FactoryAddress = common .HexToAddress ("0x4e59b44847b379578588920ca78fbf26c0b4956c" )
3437)
3538
39+ var (
40+ ErrInputTooShort = errors .New ("input too short" )
41+ ErrDenomNotFound = errors .New ("denom not found" )
42+ ErrUnauthorized = errors .New ("unauthorized" )
43+
44+ ErrUnknownMethod = "unknown method: %d"
45+ )
46+
3647func init () {
3748 var err error
3849 ERC20Bin , err = hex .DecodeString (ERC20BinHex )
@@ -52,24 +63,38 @@ const (
5263 MethodTransferFrom
5364)
5465
55- var _ vm.PrecompiledContract = & Precompile {}
66+ var (
67+ _ vm.PrecompiledContract = & Precompile {}
68+ _ cmn.NativeExecutor = & Precompile {}
69+ )
5670
5771type Precompile struct {
72+ cmn.Precompile
73+
5874 msgServer BankMsgServer
5975 bankKeeper BankKeeper
6076}
6177
6278func NewPrecompile (msgServer BankMsgServer , bankKeeper BankKeeper ) * Precompile {
63- return & Precompile {msgServer , bankKeeper }
79+ return & Precompile {
80+ Precompile : cmn.Precompile {
81+ KvGasConfig : storetypes.GasConfig {},
82+ TransientKVGasConfig : storetypes.GasConfig {},
83+ ContractAddress : common .HexToAddress (evmtypes .Bank2PrecompileAddress ),
84+ },
85+ msgServer : msgServer ,
86+ bankKeeper : bankKeeper ,
87+ }
6488}
6589
66- func (p Precompile ) Address () common. Address {
67- return common . HexToAddress ( evmtypes . Bank2PrecompileAddress )
90+ func (p Precompile ) RequiredGas ( input [] byte ) uint64 {
91+ return p . Precompile . RequiredGas ( input , p . IsTransaction ( BankMethod ( input [ 0 ])) )
6892}
6993
70- func (p Precompile ) RequiredGas (input []byte ) uint64 {
71- // FIXME
72- return 21000
94+ // IsTransaction checks if the given method name corresponds to a transaction or query.
95+ // It returns false since all bank methods are queries.
96+ func (Precompile ) IsTransaction (method BankMethod ) bool {
97+ return BankMethod (method ) == MethodTransferFrom
7398}
7499
75100// Name
@@ -78,7 +103,7 @@ func (p Precompile) RequiredGas(input []byte) uint64 {
78103func (p Precompile ) Name (ctx sdk.Context , input []byte ) ([]byte , error ) {
79104 metadata , found := p .bankKeeper .GetDenomMetaData (ctx , string (input ))
80105 if ! found {
81- return nil , vm . ErrExecutionReverted
106+ return nil , ErrDenomNotFound
82107 }
83108
84109 return []byte (metadata .Name ), nil
@@ -91,7 +116,7 @@ func (p Precompile) Name(ctx sdk.Context, input []byte) ([]byte, error) {
91116func (p Precompile ) Symbol (ctx sdk.Context , input []byte ) ([]byte , error ) {
92117 metadata , found := p .bankKeeper .GetDenomMetaData (ctx , string (input ))
93118 if ! found {
94- return nil , vm . ErrExecutionReverted
119+ return nil , ErrDenomNotFound
95120 }
96121
97122 return []byte (metadata .Symbol ), nil
@@ -104,11 +129,11 @@ func (p Precompile) Symbol(ctx sdk.Context, input []byte) ([]byte, error) {
104129func (p Precompile ) Decimals (ctx sdk.Context , input []byte ) ([]byte , error ) {
105130 m , found := p .bankKeeper .GetDenomMetaData (ctx , string (input ))
106131 if ! found {
107- return nil , vm . ErrExecutionReverted
132+ return nil , ErrDenomNotFound
108133 }
109134
110135 if len (m .DenomUnits ) == 0 {
111- return []byte {0 }, nil
136+ return []byte {0 }, errors . New ( "denom metadata has no denom units" )
112137 }
113138
114139 // look up Display denom unit
@@ -128,7 +153,7 @@ func (p Precompile) Decimals(ctx sdk.Context, input []byte) ([]byte, error) {
128153 }
129154
130155 if exponent > math .MaxUint8 {
131- return nil , vm . ErrExecutionReverted
156+ return nil , errors . New ( "exponent too large" )
132157 }
133158
134159 return []byte {uint8 (exponent )}, nil
@@ -146,7 +171,7 @@ func (p Precompile) TotalSupply(ctx sdk.Context, input []byte) ([]byte, error) {
146171// input format: abi.encodePacked(address account, string denom)
147172func (p Precompile ) BalanceOf (ctx sdk.Context , input []byte ) ([]byte , error ) {
148173 if len (input ) < 20 {
149- return nil , vm . ErrExecutionReverted
174+ return nil , ErrInputTooShort
150175 }
151176 account := common .BytesToAddress (input [:20 ])
152177 denom := string (input [20 :])
@@ -158,7 +183,7 @@ func (p Precompile) BalanceOf(ctx sdk.Context, input []byte) ([]byte, error) {
158183// input format: abi.encodePacked(address from, address to, uint256 amount, string denom)
159184func (p Precompile ) TransferFrom (ctx sdk.Context , caller common.Address , input []byte ) ([]byte , error ) {
160185 if len (input ) < 20 * 2 + 32 {
161- return nil , vm . ErrExecutionReverted
186+ return nil , ErrInputTooShort
162187 }
163188
164189 from := common .BytesToAddress (input [:20 ])
@@ -168,63 +193,41 @@ func (p Precompile) TransferFrom(ctx sdk.Context, caller common.Address, input [
168193
169194 // don't handle gas token here
170195 if denom == evmtypes .GetEVMCoinDenom () {
171- return nil , vm . ErrExecutionReverted
196+ return nil , errors . New ( "cannot transfer gas token with bank precompile" )
172197 }
173198
174199 // authorization: only from address or deterministic erc20 contract address can call this method
175200 if caller != from && caller != ERC20ContractAddress (p .Address (), denom ) {
176- return nil , vm . ErrExecutionReverted
201+ return nil , ErrUnauthorized
177202 }
178203
179204 coins := sdk.Coins {{Denom : denom , Amount : sdkmath .NewIntFromBigInt (amount )}}
180205 if err := coins .Validate (); err != nil {
181- return nil , vm . ErrExecutionReverted
206+ return nil , fmt . Errorf ( "invalid coins: %w" , err )
182207 }
183208
184209 // execute the transfer with bank keeper
185210 msg := banktypes .NewMsgSend (from .Bytes (), to .Bytes (), coins )
186211 if _ , err := p .msgServer .Send (ctx , msg ); err != nil {
187- return nil , vm . ErrExecutionReverted
212+ return nil , fmt . Errorf ( "failed to send coins: %w" , err )
188213 }
189214
190215 return []byte {1 }, nil
191216}
192217
193- func (p Precompile ) Run (evm * vm.EVM , contract * vm.Contract , readonly bool ) ([]byte , error ) {
194- stateDB , ok := evm .StateDB .(* statedb.StateDB )
195- if ! ok {
196- return nil , vm .ErrExecutionReverted
197- }
198-
199- ctx , err := stateDB .GetCacheContext ()
200- if err != nil {
201- return nil , vm .ErrExecutionReverted
202- }
203-
204- // take a snapshot of the current state before any changes
205- // to be able to revert the changes
206- snapshot := stateDB .MultiStoreSnapshot ()
207- events := ctx .EventManager ().Events ()
208-
209- // add precompileCall entry on the stateDB journal
210- // this allows to revert the changes within an evm tx
211- err = stateDB .AddPrecompileFn (p .Address (), snapshot , events )
212- if err != nil {
213- return nil , vm .ErrExecutionReverted
214- }
215-
218+ func (p Precompile ) Execute (ctx sdk.Context , evm * vm.EVM , contract * vm.Contract , readonly bool ) ([]byte , error ) {
216219 // 1 byte method selector
217220 if len (contract .Input ) == 0 {
218- return nil , vm . ErrExecutionReverted
221+ return nil , ErrInputTooShort
219222 }
220223
221- action := BankMethod (contract .Input [0 ])
222- if readonly && action == MethodTransferFrom {
224+ method := BankMethod (contract .Input [0 ])
225+ if readonly && p . IsTransaction ( method ) {
223226 return nil , vm .ErrWriteProtection
224227 }
225228
226229 input := contract .Input [1 :]
227- switch action {
230+ switch method {
228231 case MethodName :
229232 return p .Name (ctx , input )
230233 case MethodSymbol :
@@ -239,7 +242,7 @@ func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]by
239242 return p .TransferFrom (ctx , contract .Caller (), input )
240243 }
241244
242- return nil , vm . ErrExecutionReverted
245+ return nil , fmt . Errorf ( ErrUnknownMethod , method )
243246}
244247
245248// ERC20ContractAddress computes the contract address deployed with create2 factory contract.
0 commit comments