github.com/MetalBlockchain/subnet-evm@v0.4.9/accounts/abi/bind/precompile_template.go (about)

     1  // (c) 2019-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  package bind
     4  
     5  // tmplPrecompileData is the data structure required to fill the binding template.
     6  type tmplPrecompileData struct {
     7  	Contract *tmplPrecompileContract // The contract to generate into this file
     8  	Structs  map[string]*tmplStruct  // Contract struct type definitions
     9  }
    10  
    11  // tmplPrecompileContract contains the data needed to generate an individual contract binding.
    12  type tmplPrecompileContract struct {
    13  	*tmplContract
    14  	AllowList bool                   // Indicator whether the contract uses AllowList precompile
    15  	Funcs     map[string]*tmplMethod // Contract functions that include both Calls + Transacts in tmplContract
    16  }
    17  
    18  // tmplSourcePrecompileGo is the Go precompiled source template.
    19  const tmplSourcePrecompileGo = `
    20  // Code generated
    21  // This file is a generated precompile contract with stubbed abstract functions.
    22  // The file is generated by a template. Please inspect every code and comment in this file before use.
    23  
    24  // There are some must-be-done changes waiting in the file. Each area requiring you to add your code is marked with CUSTOM CODE to make them easy to find and modify.
    25  // Additionally there are other files you need to edit to activate your precompile.
    26  // These areas are highlighted with comments "ADD YOUR PRECOMPILE HERE".
    27  // For testing take a look at other precompile tests in core/stateful_precompile_test.go
    28  
    29  /* General guidelines for precompile development:
    30  1- Read the comment and set a suitable contract address in precompile/params.go. E.g:
    31  	{{.Contract.Type}}Address = common.HexToAddress("ASUITABLEHEXADDRESS")
    32  2- Set gas costs here
    33  3- It is recommended to only modify code in the highlighted areas marked with "CUSTOM CODE STARTS HERE". Modifying code outside of these areas should be done with caution and with a deep understanding of how these changes may impact the EVM.
    34  Typically, custom codes are required in only those areas.
    35  4- Add your upgradable config in params/precompile_config.go
    36  5- Add your precompile upgrade in params/config.go
    37  6- Add your solidity interface and test contract to contract-examples/contracts
    38  7- Write solidity tests for your precompile in contract-examples/test
    39  8- Create your genesis with your precompile enabled in tests/e2e/genesis/
    40  9- Create e2e test for your solidity test in tests/e2e/solidity/suites.go
    41  10- Run your e2e precompile Solidity tests with './scripts/run_ginkgo.sh'
    42  
    43  */
    44  
    45  package precompile
    46  
    47  import (
    48  	"encoding/json"
    49  	"errors"
    50  	"fmt"
    51  	"math/big"
    52  	"strings"
    53  
    54  	"github.com/MetalBlockchain/subnet-evm/accounts/abi"
    55  	"github.com/MetalBlockchain/subnet-evm/vmerrs"
    56  
    57  	"github.com/ethereum/go-ethereum/common"
    58  )
    59  
    60  const (
    61  	{{- range .Contract.Funcs}}
    62  	{{.Normalized.Name}}GasCost uint64 = 0 // SET A GAS COST HERE
    63  	{{- end}}
    64  	{{- if .Contract.Fallback}}
    65  	{{.Contract.Type}}FallbackGasCost uint64 = 0 // SET A GAS COST LESS THAN 2300 HERE
    66    {{- end}}
    67  
    68  	// {{.Contract.Type}}RawABI contains the raw ABI of {{.Contract.Type}} contract.
    69  	{{.Contract.Type}}RawABI = "{{.Contract.InputABI}}"
    70  )
    71  
    72  // CUSTOM CODE STARTS HERE
    73  // Reference imports to suppress errors from unused imports. This code and any unnecessary imports can be removed.
    74  var (
    75  	_ = errors.New
    76  	_ = big.NewInt
    77  	_ = strings.NewReader
    78  	_ = fmt.Printf
    79  )
    80  
    81  {{$contract := .Contract}}
    82  // Singleton StatefulPrecompiledContract and signatures.
    83  var (
    84  	_ StatefulPrecompileConfig = &{{.Contract.Type}}Config{}
    85  
    86  	{{- range .Contract.Funcs}}
    87  
    88  	{{- if not .Original.IsConstant | and $contract.AllowList}}
    89  
    90  	ErrCannot{{.Normalized.Name}} = errors.New("non-enabled cannot call {{.Original.Name}}")
    91  	{{- end}}
    92  	{{- end}}
    93  
    94  	{{- if .Contract.Fallback | and $contract.AllowList}}
    95  	Err{{.Contract.Type}}CannotFallback = errors.New("non-enabled cannot call fallback function")
    96  	{{- end}}
    97  
    98  	{{.Contract.Type}}ABI abi.ABI // will be initialized by init function
    99  
   100  	{{.Contract.Type}}Precompile StatefulPrecompiledContract // will be initialized by init function
   101  
   102  	// CUSTOM CODE STARTS HERE
   103  	// THIS SHOULD BE MOVED TO precompile/params.go with a suitable hex address.
   104  	{{.Contract.Type}}Address = common.HexToAddress("ASUITABLEHEXADDRESS")
   105  )
   106  
   107  // {{.Contract.Type}}Config implements the StatefulPrecompileConfig
   108  // interface while adding in the {{.Contract.Type}} specific precompile address.
   109  type {{.Contract.Type}}Config struct {
   110  	{{- if .Contract.AllowList}}
   111  	AllowListConfig
   112  	{{- end}}
   113  	UpgradeableConfig
   114  }
   115  
   116  {{$structs := .Structs}}
   117  {{range $structs}}
   118  	// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
   119  	type {{.Name}} struct {
   120  	{{range $field := .Fields}}
   121  	{{$field.Name}} {{$field.Type}}{{end}}
   122  	}
   123  {{- end}}
   124  
   125  {{- range .Contract.Funcs}}
   126  {{ if len .Normalized.Inputs | lt 1}}
   127  type {{capitalise .Normalized.Name}}Input struct{
   128  {{range .Normalized.Inputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}}
   129  }
   130  {{- end}}
   131  {{ if len .Normalized.Outputs | lt 1}}
   132  type {{capitalise .Normalized.Name}}Output struct{
   133  {{range .Normalized.Outputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}}
   134  }
   135  {{- end}}
   136  {{- end}}
   137  
   138  func init() {
   139  	parsed, err := abi.JSON(strings.NewReader({{.Contract.Type}}RawABI))
   140  	if err != nil {
   141  		panic(err)
   142  	}
   143  	{{.Contract.Type}}ABI = parsed
   144  
   145  	{{.Contract.Type}}Precompile = create{{.Contract.Type}}Precompile({{.Contract.Type}}Address)
   146  }
   147  
   148  // New{{.Contract.Type}}Config returns a config for a network upgrade at [blockTimestamp] that enables
   149  // {{.Contract.Type}} {{if .Contract.AllowList}} with the given [admins] as members of the allowlist {{end}}.
   150  func New{{.Contract.Type}}Config(blockTimestamp *big.Int{{if .Contract.AllowList}}, admins []common.Address{{end}}) *{{.Contract.Type}}Config {
   151  	return &{{.Contract.Type}}Config{
   152  		{{if .Contract.AllowList}}AllowListConfig:   AllowListConfig{AllowListAdmins: admins},{{end}}
   153  		UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp},
   154  	}
   155  }
   156  
   157  // NewDisable{{.Contract.Type}}Config returns config for a network upgrade at [blockTimestamp]
   158  // that disables {{.Contract.Type}}.
   159  func NewDisable{{.Contract.Type}}Config(blockTimestamp *big.Int) *{{.Contract.Type}}Config {
   160  	return &{{.Contract.Type}}Config{
   161  		UpgradeableConfig: UpgradeableConfig{
   162  			BlockTimestamp: blockTimestamp,
   163  			Disable:        true,
   164  		},
   165  	}
   166  }
   167  
   168  // Equal returns true if [s] is a [*{{.Contract.Type}}Config] and it has been configured identical to [c].
   169  func (c *{{.Contract.Type}}Config) Equal(s StatefulPrecompileConfig) bool {
   170  	// typecast before comparison
   171  	other, ok := (s).(*{{.Contract.Type}}Config)
   172  	if !ok {
   173  		return false
   174  	}
   175  	// CUSTOM CODE STARTS HERE
   176  	// modify this boolean accordingly with your custom {{.Contract.Type}}Config, to check if [other] and the current [c] are equal
   177  	// if {{.Contract.Type}}Config contains only UpgradeableConfig {{if .Contract.AllowList}} and AllowListConfig {{end}} you can skip modifying it.
   178  	equals := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) {{if .Contract.AllowList}} && c.AllowListConfig.Equal(&other.AllowListConfig) {{end}}
   179  	return equals
   180  }
   181  
   182  // String returns a string representation of the {{.Contract.Type}}Config.
   183  func (c *{{.Contract.Type}}Config) String() string {
   184  	bytes, _ := json.Marshal(c)
   185  	return string(bytes)
   186  }
   187  
   188  // Address returns the address of the {{.Contract.Type}}. Addresses reside under the precompile/params.go
   189  // Select a non-conflicting address and set it in the params.go.
   190  func (c *{{.Contract.Type}}Config) Address() common.Address {
   191  	return {{.Contract.Type}}Address
   192  }
   193  
   194  // Configure configures [state] with the initial configuration.
   195  func (c *{{.Contract.Type}}Config) Configure(_ ChainConfig, state StateDB, _ BlockContext) {
   196  	{{if .Contract.AllowList}}c.AllowListConfig.Configure(state, {{.Contract.Type}}Address){{end}}
   197  	// CUSTOM CODE STARTS HERE
   198  }
   199  
   200  // Contract returns the singleton stateful precompiled contract to be used for {{.Contract.Type}}.
   201  func (c *{{.Contract.Type}}Config) Contract() StatefulPrecompiledContract {
   202  	return {{.Contract.Type}}Precompile
   203  }
   204  
   205  // Verify tries to verify {{.Contract.Type}}Config and returns an error accordingly.
   206  func (c *{{.Contract.Type}}Config) Verify() error {
   207  	{{if .Contract.AllowList}}
   208  	// Verify AllowList first
   209  	if err := c.AllowListConfig.Verify(); err != nil {
   210  		return err
   211  	}
   212  	{{end}}
   213  	// CUSTOM CODE STARTS HERE
   214  	// Add your own custom verify code for {{.Contract.Type}}Config here
   215  	// and return an error accordingly
   216  	return nil
   217  }
   218  
   219  {{if .Contract.AllowList}}
   220  // Get{{.Contract.Type}}AllowListStatus returns the role of [address] for the {{.Contract.Type}} list.
   221  func Get{{.Contract.Type}}AllowListStatus(stateDB StateDB, address common.Address) AllowListRole {
   222  	return getAllowListStatus(stateDB, {{.Contract.Type}}Address, address)
   223  }
   224  
   225  // Set{{.Contract.Type}}AllowListStatus sets the permissions of [address] to [role] for the
   226  // {{.Contract.Type}} list. Assumes [role] has already been verified as valid.
   227  // This stores the [role] in the contract storage with address [{{.Contract.Type}}Address]
   228  // and [address] hash. It means that any reusage of the [address] key for different value
   229  // conflicts with the same slot [role] is stored.
   230  // Precompile implementations must use a different key than [address] for their storage.
   231  func Set{{.Contract.Type}}AllowListStatus(stateDB StateDB, address common.Address, role AllowListRole) {
   232  	setAllowListRole(stateDB, {{.Contract.Type}}Address, address, role)
   233  }
   234  {{end}}
   235  
   236  {{range .Contract.Funcs}}
   237  {{if len .Normalized.Inputs | lt 1}}
   238  // Unpack{{capitalise .Normalized.Name}}Input attempts to unpack [input] into the arguments for the {{capitalise .Normalized.Name}}Input{}
   239  // assumes that [input] does not include selector (omits first 4 func signature bytes)
   240  func Unpack{{capitalise .Normalized.Name}}Input(input []byte) ({{capitalise .Normalized.Name}}Input, error) {
   241  	inputStruct := {{capitalise .Normalized.Name}}Input{}
   242  	err := {{$contract.Type}}ABI.UnpackInputIntoInterface(&inputStruct, "{{.Original.Name}}", input)
   243  
   244  	return inputStruct, err
   245  }
   246  
   247  // Pack{{.Normalized.Name}} packs [inputStruct] of type {{capitalise .Normalized.Name}}Input into the appropriate arguments for {{.Original.Name}}.
   248  func Pack{{.Normalized.Name}}(inputStruct {{capitalise .Normalized.Name}}Input) ([]byte, error) {
   249  	return {{$contract.Type}}ABI.Pack("{{.Original.Name}}", {{range .Normalized.Inputs}} inputStruct.{{capitalise .Name}}, {{end}})
   250  }
   251  {{else if len .Normalized.Inputs | eq 1 }}
   252  {{$method := .}}
   253  {{$input := index $method.Normalized.Inputs 0}}
   254  // Unpack{{capitalise .Normalized.Name}}Input attempts to unpack [input] into the {{bindtype $input.Type $structs}} type argument
   255  // assumes that [input] does not include selector (omits first 4 func signature bytes)
   256  func Unpack{{capitalise .Normalized.Name}}Input(input []byte)({{bindtype $input.Type $structs}}, error) {
   257  res, err := {{$contract.Type}}ABI.UnpackInput("{{$method.Original.Name}}", input)
   258  if err != nil {
   259  	return {{convertToNil $input.Type}}, err
   260  }
   261  unpacked := *abi.ConvertType(res[0], new({{bindtype $input.Type $structs}})).(*{{bindtype $input.Type $structs}})
   262  return unpacked, nil
   263  }
   264  
   265  // Pack{{.Normalized.Name}} packs [{{decapitalise $input.Name}}] of type {{bindtype $input.Type $structs}} into the appropriate arguments for {{$method.Original.Name}}.
   266  // the packed bytes include selector (first 4 func signature bytes).
   267  // This function is mostly used for tests.
   268  func Pack{{$method.Normalized.Name}}( {{decapitalise $input.Name}} {{bindtype $input.Type $structs}},) ([]byte, error) {
   269  	return {{$contract.Type}}ABI.Pack("{{$method.Original.Name}}", {{decapitalise $input.Name}},)
   270  }
   271  {{else}}
   272  // Pack{{.Normalized.Name}} packs the include selector (first 4 func signature bytes).
   273  // This function is mostly used for tests.
   274  func Pack{{.Normalized.Name}}() ([]byte, error) {
   275  	return {{$contract.Type}}ABI.Pack("{{.Original.Name}}")
   276  }
   277  {{end}}
   278  
   279  {{if len .Normalized.Outputs | lt 1}}
   280  // Pack{{capitalise .Normalized.Name}}Output attempts to pack given [outputStruct] of type {{capitalise .Normalized.Name}}Output
   281  // to conform the ABI outputs.
   282  func Pack{{capitalise .Normalized.Name}}Output (outputStruct {{capitalise .Normalized.Name}}Output) ([]byte, error) {
   283  	return {{$contract.Type}}ABI.PackOutput("{{.Original.Name}}",
   284  		{{- range .Normalized.Outputs}}
   285  		outputStruct.{{capitalise .Name}},
   286  		{{- end}}
   287  	)
   288  }
   289  
   290  {{else if len .Normalized.Outputs | eq 1 }}
   291  {{$method := .}}
   292  {{$output := index $method.Normalized.Outputs 0}}
   293  // Pack{{capitalise .Normalized.Name}}Output attempts to pack given {{decapitalise $output.Name}} of type {{bindtype $output.Type $structs}}
   294  // to conform the ABI outputs.
   295  func Pack{{$method.Normalized.Name}}Output ({{decapitalise $output.Name}} {{bindtype $output.Type $structs}}) ([]byte, error) {
   296  	return {{$contract.Type}}ABI.PackOutput("{{$method.Original.Name}}", {{decapitalise $output.Name}})
   297  }
   298  {{end}}
   299  
   300  func {{decapitalise .Normalized.Name}}(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
   301  	if remainingGas, err = deductGas(suppliedGas, {{.Normalized.Name}}GasCost); err != nil {
   302  		return nil, 0, err
   303  	}
   304  
   305  	{{- if not .Original.IsConstant}}
   306  	if readOnly {
   307  		return nil, remainingGas, vmerrs.ErrWriteProtection
   308  	}
   309   	{{- end}}
   310  
   311  	{{- if len .Normalized.Inputs | eq 0}}
   312  	// no input provided for this function
   313  	{{else}}
   314  	// attempts to unpack [input] into the arguments to the {{.Normalized.Name}}Input.
   315  	// Assumes that [input] does not include selector
   316  	// You can use unpacked [inputStruct] variable in your code
   317  	inputStruct, err := Unpack{{capitalise .Normalized.Name}}Input(input)
   318  	if err != nil{
   319  		return nil, remainingGas, err
   320  	}
   321  	{{- end}}
   322  
   323  	{{if not .Original.IsConstant | and $contract.AllowList}}
   324  	// Allow list is enabled and {{.Normalized.Name}} is a state-changer function.
   325  	// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
   326  	// You can modify/delete this code if you don't want this function to be restricted by the allow list.
   327  	stateDB := accessibleState.GetStateDB()
   328  	// Verify that the caller is in the allow list and therefore has the right to modify it
   329  	callerStatus := getAllowListStatus(stateDB, {{$contract.Type}}Address, caller)
   330  	if !callerStatus.IsEnabled() {
   331  		return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannot{{.Normalized.Name}}, caller)
   332  	}
   333  	// allow list code ends here.
   334    {{end}}
   335  	// CUSTOM CODE STARTS HERE
   336  	{{- if len .Normalized.Inputs | ne 0}}
   337  	_ = inputStruct // CUSTOM CODE OPERATES ON INPUT
   338  	{{- end}}
   339  
   340  	{{- if len .Normalized.Outputs | eq 0}}
   341  	// this function does not return an output, leave this one as is
   342  	packedOutput := []byte{}
   343  	{{- else}}
   344  	{{- if len .Normalized.Outputs | lt 1}}
   345  	var output {{capitalise .Normalized.Name}}Output // CUSTOM CODE FOR AN OUTPUT
   346  	{{- else }}
   347  	{{$output := index .Normalized.Outputs 0}}
   348  	var output {{bindtype $output.Type $structs}} // CUSTOM CODE FOR AN OUTPUT
   349  	{{- end}}
   350  	packedOutput, err := Pack{{.Normalized.Name}}Output(output)
   351  	if err != nil {
   352  		return nil, remainingGas, err
   353  	}
   354  	{{- end}}
   355  
   356  	// Return the packed output and the remaining gas
   357  	return packedOutput, remainingGas, nil
   358  }
   359  {{end}}
   360  
   361  {{- if .Contract.Fallback}}
   362  {{- with .Contract.Fallback}}
   363  // {{decapitalise $contract.Type}}Fallback executed if a function identifier does not match any of the available functions in a smart contract.
   364  // This function cannot take an input or return an output.
   365  func {{decapitalise $contract.Type}}Fallback (accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, _ []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
   366  	if remainingGas, err = deductGas(suppliedGas, {{$contract.Type}}FallbackGasCost); err != nil {
   367  		return nil, 0, err
   368  	}
   369  
   370  	if readOnly {
   371  		return nil, remainingGas, vmerrs.ErrWriteProtection
   372  	}
   373  
   374  	{{- if $contract.AllowList}}
   375  	// Allow list is enabled and {{.Normalized.Name}} is a state-changer function.
   376  	// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
   377  	// You can modify/delete this code if you don't want this function to be restricted by the allow list.
   378  	stateDB := accessibleState.GetStateDB()
   379  	// Verify that the caller is in the allow list and therefore has the right to modify it
   380  	callerStatus := getAllowListStatus(stateDB, {{$contract.Type}}Address, caller)
   381  	if !callerStatus.IsEnabled() {
   382  		return nil, remainingGas, fmt.Errorf("%w: %s", Err{{$contract.Type}}CannotFallback, caller)
   383  	}
   384  	// allow list code ends here.
   385  	{{- end}}
   386  
   387  	// CUSTOM CODE STARTS HERE
   388  
   389  	// Fallback can return data in output.
   390  	// The returned data will not be ABI-encoded.
   391  	// Instead it will be returned without modifications (not even padding).
   392  	output := []byte{}
   393  	// return raw output
   394  	return output, remainingGas, nil
   395  }
   396  {{- end}}
   397  {{- end}}
   398  
   399  // create{{.Contract.Type}}Precompile returns a StatefulPrecompiledContract with getters and setters for the precompile.
   400  {{if .Contract.AllowList}} // Access to the getters/setters is controlled by an allow list for [precompileAddr].{{end}}
   401  func create{{.Contract.Type}}Precompile(precompileAddr common.Address) StatefulPrecompiledContract {
   402  	var functions []*statefulPrecompileFunction
   403  	{{- if .Contract.AllowList}}
   404  	functions = append(functions, createAllowListFunctions(precompileAddr)...)
   405  	{{- end}}
   406  
   407  	{{range .Contract.Funcs}}
   408  	method{{.Normalized.Name}}, ok := {{$contract.Type}}ABI.Methods["{{.Original.Name}}"]
   409  	if !ok{
   410  		panic("given method does not exist in the ABI")
   411  	}
   412  	functions = append(functions, newStatefulPrecompileFunction(method{{.Normalized.Name}}.ID, {{decapitalise .Normalized.Name}}))
   413  	{{end}}
   414  
   415  	{{- if .Contract.Fallback}}
   416  	// Construct the contract with the fallback function.
   417  	contract := newStatefulPrecompileWithFunctionSelectors({{decapitalise $contract.Type}}Fallback, functions)
   418  	{{- else}}
   419  	// Construct the contract with no fallback function.
   420  	contract := newStatefulPrecompileWithFunctionSelectors(nil, functions)
   421  	{{- end}}
   422  	return contract
   423  }
   424  `