github.com/klaytn/klaytn@v1.10.2/api/tx_args.go (about)

     1  // Modifications Copyright 2019 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from internal/ethapi/api.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package api
    22  
    23  import (
    24  	"bytes"
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"math/big"
    29  	"reflect"
    30  
    31  	"github.com/klaytn/klaytn/accounts/abi"
    32  	"github.com/klaytn/klaytn/blockchain/types"
    33  	"github.com/klaytn/klaytn/blockchain/types/accountkey"
    34  	"github.com/klaytn/klaytn/blockchain/vm"
    35  	"github.com/klaytn/klaytn/common"
    36  	"github.com/klaytn/klaytn/common/hexutil"
    37  	"github.com/klaytn/klaytn/params"
    38  	"github.com/klaytn/klaytn/rlp"
    39  )
    40  
    41  var (
    42  	errTxArgInvalidInputData = errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`)
    43  	errTxArgInvalidFeePayer  = errors.New("invalid fee payer is set")
    44  	errTxArgNilTxType        = errors.New("tx should have a type value")
    45  	errTxArgNilContractData  = errors.New(`contract creation without any data provided`)
    46  	errTxArgNilSenderSig     = errors.New("sender signature is not set")
    47  	errTxArgNilNonce         = errors.New("nonce of the sender is not set")
    48  	errTxArgNilGas           = errors.New("gas limit is not set")
    49  	errTxArgNilGasPrice      = errors.New("gas price is not set")
    50  	errNotForFeeDelegationTx = errors.New("fee-delegation type transactions are not allowed to use this API")
    51  )
    52  
    53  // isTxField checks whether the string is a field name of the specific txType.
    54  // isTxField[txType][txFieldName] has true/false.
    55  var isTxField = func() map[types.TxType]map[string]bool {
    56  	mapOfFieldMap := map[types.TxType]map[string]bool{}
    57  	internalDataTypes := map[types.TxType]interface{}{
    58  		// since legacy tx has optional fields, some fields can be omitted
    59  		// types.TxTypeLegacyTransaction:                           types.TxInternalDataLegacy{},
    60  		types.TxTypeValueTransfer:                               types.TxInternalDataValueTransfer{},
    61  		types.TxTypeFeeDelegatedValueTransfer:                   types.TxInternalDataFeeDelegatedValueTransfer{},
    62  		types.TxTypeFeeDelegatedValueTransferWithRatio:          types.TxInternalDataFeeDelegatedValueTransferWithRatio{},
    63  		types.TxTypeValueTransferMemo:                           types.TxInternalDataValueTransferMemo{},
    64  		types.TxTypeFeeDelegatedValueTransferMemo:               types.TxInternalDataFeeDelegatedValueTransferMemo{},
    65  		types.TxTypeFeeDelegatedValueTransferMemoWithRatio:      types.TxInternalDataFeeDelegatedValueTransferMemoWithRatio{},
    66  		types.TxTypeAccountUpdate:                               types.TxInternalDataAccountUpdate{},
    67  		types.TxTypeFeeDelegatedAccountUpdate:                   types.TxInternalDataFeeDelegatedAccountUpdate{},
    68  		types.TxTypeFeeDelegatedAccountUpdateWithRatio:          types.TxInternalDataFeeDelegatedAccountUpdateWithRatio{},
    69  		types.TxTypeSmartContractDeploy:                         types.TxInternalDataSmartContractDeploy{},
    70  		types.TxTypeFeeDelegatedSmartContractDeploy:             types.TxInternalDataFeeDelegatedSmartContractDeploy{},
    71  		types.TxTypeFeeDelegatedSmartContractDeployWithRatio:    types.TxInternalDataFeeDelegatedSmartContractDeployWithRatio{},
    72  		types.TxTypeSmartContractExecution:                      types.TxInternalDataSmartContractExecution{},
    73  		types.TxTypeFeeDelegatedSmartContractExecution:          types.TxInternalDataFeeDelegatedSmartContractExecution{},
    74  		types.TxTypeFeeDelegatedSmartContractExecutionWithRatio: types.TxInternalDataFeeDelegatedSmartContractExecutionWithRatio{},
    75  		types.TxTypeCancel:                                      types.TxInternalDataCancel{},
    76  		types.TxTypeFeeDelegatedCancel:                          types.TxInternalDataFeeDelegatedCancel{},
    77  		types.TxTypeFeeDelegatedCancelWithRatio:                 types.TxInternalDataFeeDelegatedCancelWithRatio{},
    78  		types.TxTypeChainDataAnchoring:                          types.TxInternalDataChainDataAnchoring{},
    79  		types.TxTypeFeeDelegatedChainDataAnchoring:              types.TxInternalDataFeeDelegatedChainDataAnchoring{},
    80  		types.TxTypeFeeDelegatedChainDataAnchoringWithRatio:     types.TxInternalDataFeeDelegatedChainDataAnchoringWithRatio{},
    81  	}
    82  
    83  	// generate field maps for each tx type
    84  	for txType, internalData := range internalDataTypes {
    85  		fieldMap := map[string]bool{}
    86  		internalDataType := reflect.TypeOf(internalData)
    87  
    88  		// key of filedMap is tx field name and value of fieldMap means the existence of field name
    89  		for i := 0; i < internalDataType.NumField(); i++ {
    90  			fieldMap[internalDataType.Field(i).Name] = true
    91  		}
    92  
    93  		// additional field of SendTxArgs to support various tx types
    94  		fieldMap["TypeInt"] = true
    95  		// additional field of SendTxArgs to support a legacy tx field (skip checking)
    96  		fieldMap["Data"] = false
    97  
    98  		mapOfFieldMap[txType] = fieldMap
    99  	}
   100  	return mapOfFieldMap
   101  }()
   102  
   103  type NewTxArgs interface {
   104  	setDefaults(context.Context, Backend) error
   105  	toTransaction() (*types.Transaction, error)
   106  	from() common.Address
   107  }
   108  
   109  // SendTxArgs represents the arguments to submit a new transaction into the transaction pool.
   110  type SendTxArgs struct {
   111  	TypeInt              *types.TxType   `json:"typeInt"`
   112  	From                 common.Address  `json:"from"`
   113  	Recipient            *common.Address `json:"to"`
   114  	GasLimit             *hexutil.Uint64 `json:"gas"`
   115  	Price                *hexutil.Big    `json:"gasPrice"`
   116  	MaxPriorityFeePerGas *hexutil.Big    `json:"maxPriorityFeePerGas"`
   117  	MaxFeePerGas         *hexutil.Big    `json:"maxFeePerGas"`
   118  	Amount               *hexutil.Big    `json:"value"`
   119  	AccountNonce         *hexutil.Uint64 `json:"nonce"`
   120  	// We accept "data" and "input" for backwards-compatibility reasons. "input" is the
   121  	// newer name and should be preferred by clients.
   122  	Data    *hexutil.Bytes `json:"data"`
   123  	Payload *hexutil.Bytes `json:"input"`
   124  
   125  	CodeFormat    *params.CodeFormat `json:"codeFormat"`
   126  	HumanReadable *bool              `json:"humanReadable"`
   127  
   128  	Key *hexutil.Bytes `json:"key"`
   129  
   130  	AccessList *types.AccessList `json:"accessList,omitempty"`
   131  	ChainID    *hexutil.Big      `json:"chainId,omitempty"`
   132  
   133  	FeePayer *common.Address `json:"feePayer"`
   134  	FeeRatio *types.FeeRatio `json:"feeRatio"`
   135  
   136  	TxSignatures types.TxSignaturesJSON `json:"signatures"`
   137  }
   138  
   139  // setDefaults is a helper function that fills in default values for unspecified common tx fields.
   140  func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
   141  	isMagma := b.ChainConfig().IsMagmaForkEnabled(new(big.Int).Add(b.CurrentBlock().Number(), big.NewInt(1)))
   142  
   143  	if args.TypeInt == nil {
   144  		args.TypeInt = new(types.TxType)
   145  		*args.TypeInt = types.TxTypeLegacyTransaction
   146  	}
   147  	if args.GasLimit == nil {
   148  		args.GasLimit = new(hexutil.Uint64)
   149  		*args.GasLimit = hexutil.Uint64(90000)
   150  	}
   151  	// Eth typed transactions requires chainId.
   152  	if args.TypeInt.IsEthTypedTransaction() {
   153  		if args.ChainID == nil {
   154  			args.ChainID = (*hexutil.Big)(b.ChainConfig().ChainID)
   155  		}
   156  	}
   157  	// For the transaction that do not use the gasPrice field, the default value of gasPrice is not set.
   158  	if args.Price == nil && *args.TypeInt != types.TxTypeEthereumDynamicFee {
   159  		// b.SuggestPrice = unitPrice, for before Magma
   160  		//                = baseFee * 2,   for after Magma
   161  		price, err := b.SuggestPrice(ctx)
   162  		if err != nil {
   163  			return err
   164  		}
   165  		args.Price = (*hexutil.Big)(price)
   166  	}
   167  
   168  	if *args.TypeInt == types.TxTypeEthereumDynamicFee {
   169  		gasPrice, err := b.SuggestPrice(ctx)
   170  		if err != nil {
   171  			return err
   172  		}
   173  		if args.MaxPriorityFeePerGas == nil {
   174  			args.MaxPriorityFeePerGas = (*hexutil.Big)(gasPrice)
   175  		}
   176  		if args.MaxFeePerGas == nil {
   177  			// Before Magma hard fork, `gasFeeCap` was set to `baseFee*2 + maxPriorityFeePerGas` by default.
   178  			gasFeeCap := new(big.Int).Add(
   179  				(*big.Int)(args.MaxPriorityFeePerGas),
   180  				new(big.Int).Mul(new(big.Int).SetUint64(params.ZeroBaseFee), big.NewInt(2)),
   181  			)
   182  			if isMagma {
   183  				// After Magma hard fork, `gasFeeCap` was set to `baseFee*2` by default.
   184  				gasFeeCap = gasPrice
   185  			}
   186  			args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap)
   187  		}
   188  		if isMagma {
   189  			if args.MaxFeePerGas.ToInt().Cmp(new(big.Int).Div(gasPrice, common.Big2)) < 0 {
   190  				return fmt.Errorf("maxFeePerGas (%v) < BaseFee (%v)", args.MaxFeePerGas, gasPrice)
   191  			}
   192  		} else if args.MaxPriorityFeePerGas.ToInt().Cmp(gasPrice) != 0 || args.MaxFeePerGas.ToInt().Cmp(gasPrice) != 0 {
   193  			return fmt.Errorf("only %s is allowed to be used as maxFeePerGas and maxPriorityPerGas", gasPrice.Text(16))
   194  		}
   195  		if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
   196  			return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
   197  		}
   198  	}
   199  	if args.AccountNonce == nil {
   200  		nonce := b.GetPoolNonce(ctx, args.From)
   201  		args.AccountNonce = (*hexutil.Uint64)(&nonce)
   202  	}
   203  
   204  	return nil
   205  }
   206  
   207  // checkArgs checks the validity of SendTxArgs values.
   208  // The each tx types has its own validation logic to give detailed errors to users.
   209  func (args *SendTxArgs) checkArgs() error {
   210  	if args.TypeInt == nil {
   211  		return errTxArgNilTxType
   212  	}
   213  	// Skip ethereum transaction type since it has optional fields
   214  	if args.TypeInt.IsEthereumTransaction() {
   215  		return nil
   216  	}
   217  
   218  	argsType := reflect.TypeOf(*args)
   219  	argsValue := reflect.ValueOf(*args)
   220  
   221  	for i := 0; i < argsType.NumField(); i++ {
   222  		// Skip From since it is an essential field and a non-pointer value
   223  		// Skip TxSignatures since the value is not considered by all APIs
   224  		if argsType.Field(i).Name == "From" || argsType.Field(i).Name == "TxSignatures" {
   225  			continue
   226  		}
   227  
   228  		// An args field doesn't have a value but the field name exist on the tx type
   229  		if argsValue.Field(i).IsNil() && isTxField[*args.TypeInt][argsType.Field(i).Name] {
   230  			// Allow only contract deploying txs to set the recipient as nil
   231  			if (*args.TypeInt).IsContractDeploy() && argsType.Field(i).Name == "Recipient" {
   232  				continue
   233  			}
   234  			return errors.New((string)(argsType.Field(i).Tag) + " is required for " + (*args.TypeInt).String())
   235  		}
   236  
   237  		// An args field has a value but the field name doesn't exist on the tx type
   238  		if !argsValue.Field(i).IsNil() && !isTxField[*args.TypeInt][argsType.Field(i).Name] {
   239  			return errors.New((string)(argsType.Field(i).Tag) + " is not a field of " + (*args.TypeInt).String())
   240  		}
   241  	}
   242  
   243  	return nil
   244  }
   245  
   246  // genTxValuesMap generates a value map used used in "NewTransactionWithMap" function.
   247  // This function assigned all non-nil values regardless of the tx type.
   248  // Invalid values in the map will be validated in "NewTransactionWithMap" function.
   249  func (args *SendTxArgs) genTxValuesMap() map[types.TxValueKeyType]interface{} {
   250  	values := make(map[types.TxValueKeyType]interface{})
   251  
   252  	// common tx fields. They should have values after executing "setDefaults" function.
   253  	if args.TypeInt == nil || args.AccountNonce == nil || args.GasLimit == nil {
   254  		return values
   255  	}
   256  	// GasPrice can be an optional tx filed for TxTypeEthereumDynamicFee
   257  	if args.Price == nil && *args.TypeInt != types.TxTypeEthereumDynamicFee {
   258  		return values
   259  	}
   260  
   261  	if !args.TypeInt.IsEthereumTransaction() {
   262  		values[types.TxValueKeyFrom] = args.From
   263  	}
   264  	values[types.TxValueKeyNonce] = uint64(*args.AccountNonce)
   265  	values[types.TxValueKeyGasLimit] = uint64(*args.GasLimit)
   266  
   267  	// optional tx fields
   268  	if args.Price != nil {
   269  		values[types.TxValueKeyGasPrice] = (*big.Int)(args.Price)
   270  	}
   271  	if args.TypeInt.IsContractDeploy() || args.TypeInt.IsEthereumTransaction() {
   272  		// contract deploy type and ethereum tx types allow nil as TxValueKeyTo value
   273  		values[types.TxValueKeyTo] = (*common.Address)(args.Recipient)
   274  	} else if args.Recipient != nil {
   275  		values[types.TxValueKeyTo] = *args.Recipient
   276  	}
   277  	if args.FeePayer != nil {
   278  		values[types.TxValueKeyFeePayer] = *args.FeePayer
   279  	}
   280  	if args.FeeRatio != nil {
   281  		values[types.TxValueKeyFeeRatioOfFeePayer] = *args.FeeRatio
   282  	}
   283  	if args.Amount != nil {
   284  		values[types.TxValueKeyAmount] = (*big.Int)(args.Amount)
   285  	} else if args.TypeInt.IsEthereumTransaction() {
   286  		values[types.TxValueKeyAmount] = common.Big0
   287  	}
   288  	if args.Payload != nil {
   289  		// chain data anchoring type uses the TxValueKeyAnchoredData field
   290  		if args.TypeInt.IsChainDataAnchoring() {
   291  			values[types.TxValueKeyAnchoredData] = ([]byte)(*args.Payload)
   292  		} else {
   293  			values[types.TxValueKeyData] = ([]byte)(*args.Payload)
   294  		}
   295  	} else if args.TypeInt.IsEthereumTransaction() {
   296  		// For Ethereum transactions, Payload is an optional field.
   297  		values[types.TxValueKeyData] = []byte{}
   298  	}
   299  	if args.CodeFormat != nil {
   300  		values[types.TxValueKeyCodeFormat] = *args.CodeFormat
   301  	}
   302  	if args.HumanReadable != nil {
   303  		values[types.TxValueKeyHumanReadable] = *args.HumanReadable
   304  	}
   305  	if args.Key != nil {
   306  		serializer := accountkey.NewAccountKeySerializer()
   307  		if err := rlp.DecodeBytes(*args.Key, &serializer); err == nil {
   308  			values[types.TxValueKeyAccountKey] = serializer.GetKey()
   309  		}
   310  	}
   311  	if args.ChainID != nil {
   312  		values[types.TxValueKeyChainID] = (*big.Int)(args.ChainID)
   313  	}
   314  	if args.AccessList != nil {
   315  		values[types.TxValueKeyAccessList] = *args.AccessList
   316  	}
   317  	if args.MaxPriorityFeePerGas != nil {
   318  		values[types.TxValueKeyGasTipCap] = (*big.Int)(args.MaxPriorityFeePerGas)
   319  	}
   320  	if args.MaxFeePerGas != nil {
   321  		values[types.TxValueKeyGasFeeCap] = (*big.Int)(args.MaxFeePerGas)
   322  	}
   323  
   324  	return values
   325  }
   326  
   327  // toTransaction returns an unsigned transaction filled with values in SendTxArgs.
   328  func (args *SendTxArgs) toTransaction() (*types.Transaction, error) {
   329  	var input []byte
   330  
   331  	// provide detailed error messages to users (optional)
   332  	if err := args.checkArgs(); err != nil {
   333  		return nil, err
   334  	}
   335  
   336  	// for TxTypeLegacyTransaction
   337  	if *args.TypeInt == types.TxTypeLegacyTransaction {
   338  		if args.Data != nil && args.Payload != nil && !bytes.Equal(*args.Data, *args.Payload) {
   339  			return nil, errTxArgInvalidInputData
   340  		}
   341  
   342  		if args.Data != nil {
   343  			input = *args.Data
   344  		} else if args.Payload != nil {
   345  			input = *args.Payload
   346  		}
   347  
   348  		if args.Recipient == nil {
   349  			if len(input) == 0 {
   350  				return nil, errTxArgNilContractData
   351  			}
   352  			return types.NewContractCreation(uint64(*args.AccountNonce), (*big.Int)(args.Amount), uint64(*args.GasLimit), (*big.Int)(args.Price), input), nil
   353  		}
   354  		return types.NewTransaction(uint64(*args.AccountNonce), *args.Recipient, (*big.Int)(args.Amount), uint64(*args.GasLimit), (*big.Int)(args.Price), input), nil
   355  	}
   356  
   357  	// for other tx types except TxTypeLegacyTransaction
   358  	values := args.genTxValuesMap()
   359  	return types.NewTransactionWithMap(*args.TypeInt, values)
   360  }
   361  
   362  type ValueTransferTxArgs struct {
   363  	From     common.Address  `json:"from"`
   364  	Gas      *hexutil.Uint64 `json:"gas"`
   365  	GasPrice *hexutil.Big    `json:"gasPrice"`
   366  	Nonce    *hexutil.Uint64 `json:"nonce"`
   367  	To       common.Address  `json:"to"`
   368  	Value    *hexutil.Big    `json:"value"`
   369  }
   370  
   371  func (args *ValueTransferTxArgs) from() common.Address {
   372  	return args.From
   373  }
   374  
   375  // setDefaults is a helper function that fills in default values for unspecified tx fields.
   376  func (args *ValueTransferTxArgs) setDefaults(ctx context.Context, b Backend) error {
   377  	if args.Gas == nil {
   378  		args.Gas = new(hexutil.Uint64)
   379  		*(*uint64)(args.Gas) = 90000
   380  	}
   381  	if args.GasPrice == nil {
   382  		price, err := b.SuggestPrice(ctx)
   383  		if err != nil {
   384  			return err
   385  		}
   386  		args.GasPrice = (*hexutil.Big)(price)
   387  	}
   388  	if args.Nonce == nil {
   389  		nonce := b.GetPoolNonce(ctx, args.From)
   390  		args.Nonce = (*hexutil.Uint64)(&nonce)
   391  	}
   392  	return nil
   393  }
   394  
   395  func (args *ValueTransferTxArgs) toTransaction() (*types.Transaction, error) {
   396  	tx, err := types.NewTransactionWithMap(types.TxTypeValueTransfer, map[types.TxValueKeyType]interface{}{
   397  		types.TxValueKeyNonce:    (uint64)(*args.Nonce),
   398  		types.TxValueKeyGasLimit: (uint64)(*args.Gas),
   399  		types.TxValueKeyGasPrice: (*big.Int)(args.GasPrice),
   400  		types.TxValueKeyFrom:     args.From,
   401  		types.TxValueKeyTo:       args.To,
   402  		types.TxValueKeyAmount:   (*big.Int)(args.Value),
   403  	})
   404  	if err != nil {
   405  		return nil, err
   406  	}
   407  
   408  	return tx, nil
   409  }
   410  
   411  type AccountUpdateTxArgs struct {
   412  	From     common.Address  `json:"from"`
   413  	Gas      *hexutil.Uint64 `json:"gas"`
   414  	GasPrice *hexutil.Big    `json:"gasPrice"`
   415  	Nonce    *hexutil.Uint64 `json:"nonce"`
   416  	Key      *hexutil.Bytes  `json:"key"`
   417  }
   418  
   419  func (args *AccountUpdateTxArgs) from() common.Address {
   420  	return args.From
   421  }
   422  
   423  // setDefaults is a helper function that fills in default values for unspecified tx fields.
   424  func (args *AccountUpdateTxArgs) setDefaults(ctx context.Context, b Backend) error {
   425  	if args.Gas == nil {
   426  		args.Gas = new(hexutil.Uint64)
   427  		*(*uint64)(args.Gas) = 90000
   428  	}
   429  	if args.GasPrice == nil {
   430  		price, err := b.SuggestPrice(ctx)
   431  		if err != nil {
   432  			return err
   433  		}
   434  		args.GasPrice = (*hexutil.Big)(price)
   435  	}
   436  	if args.Nonce == nil {
   437  		nonce := b.GetPoolNonce(ctx, args.From)
   438  		args.Nonce = (*hexutil.Uint64)(&nonce)
   439  	}
   440  	return nil
   441  }
   442  
   443  func (args *AccountUpdateTxArgs) toTransaction() (*types.Transaction, error) {
   444  	serializer := accountkey.NewAccountKeySerializer()
   445  
   446  	if err := rlp.DecodeBytes(*args.Key, &serializer); err != nil {
   447  		return nil, err
   448  	}
   449  	tx, err := types.NewTransactionWithMap(types.TxTypeAccountUpdate, map[types.TxValueKeyType]interface{}{
   450  		types.TxValueKeyNonce:      (uint64)(*args.Nonce),
   451  		types.TxValueKeyGasLimit:   (uint64)(*args.Gas),
   452  		types.TxValueKeyGasPrice:   (*big.Int)(args.GasPrice),
   453  		types.TxValueKeyFrom:       args.From,
   454  		types.TxValueKeyAccountKey: serializer.GetKey(),
   455  	})
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  
   460  	return tx, nil
   461  }
   462  
   463  // isReverted checks given error is vm.ErrExecutionReverted
   464  func isReverted(err error) bool {
   465  	if errors.Is(err, vm.ErrExecutionReverted) {
   466  		return true
   467  	}
   468  	return false
   469  }
   470  
   471  // newRevertError wraps data returned when EVM execution was reverted.
   472  // Make sure that data is returned when execution reverted situation.
   473  func newRevertError(data []byte) *revertError {
   474  	reason, errUnpack := abi.UnpackRevert(data)
   475  	err := errors.New("execution reverted")
   476  	if errUnpack == nil {
   477  		err = fmt.Errorf("execution reverted: %v", reason)
   478  	}
   479  	return &revertError{
   480  		error:  err,
   481  		reason: hexutil.Encode(data),
   482  	}
   483  }
   484  
   485  // revertError is an API error that encompassas an EVM revertal with JSON error
   486  // code and a binary data blob.
   487  type revertError struct {
   488  	error
   489  	reason string // revert reason hex encoded
   490  }
   491  
   492  // ErrorCode returns the JSON error code for a revertal.
   493  // See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
   494  func (e *revertError) ErrorCode() int {
   495  	return 3
   496  }
   497  
   498  // ErrorData returns the hex encoded revert reason.
   499  func (e *revertError) ErrorData() interface{} {
   500  	return e.reason
   501  }