github.com/ava-labs/subnet-evm@v0.6.4/accounts/abi/bind/precompilebind/precompile_contract_template.go (about)

     1  // (c) 2019-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  package precompilebind
     4  
     5  import "github.com/ava-labs/subnet-evm/accounts/abi/bind"
     6  
     7  // tmplPrecompileData is the data structure required to fill the binding template.
     8  type tmplPrecompileData struct {
     9  	Package  string
    10  	Contract *tmplPrecompileContract     // The contract to generate into this file
    11  	Structs  map[string]*bind.TmplStruct // Contract struct type definitions
    12  }
    13  
    14  // tmplPrecompileContract contains the data needed to generate an individual contract binding.
    15  type tmplPrecompileContract struct {
    16  	*bind.TmplContract
    17  	AllowList   bool                        // Indicator whether the contract uses AllowList precompile
    18  	Funcs       map[string]*bind.TmplMethod // Contract functions that include both Calls + Transacts in tmplContract
    19  	ABIFilename string                      // Path to the ABI file
    20  }
    21  
    22  // tmplSourcePrecompileContractGo is the Go precompiled contract source template.
    23  const tmplSourcePrecompileContractGo = `
    24  // Code generated
    25  // This file is a generated precompile contract config with stubbed abstract functions.
    26  // The file is generated by a template. Please inspect every code and comment in this file before use.
    27  
    28  package {{.Package}}
    29  
    30  import (
    31  	"errors"
    32  	"fmt"
    33  	"math/big"
    34  
    35  	"github.com/ava-labs/subnet-evm/accounts/abi"
    36  	{{- if .Contract.AllowList}}
    37  	"github.com/ava-labs/subnet-evm/precompile/allowlist"
    38  	{{- end}}
    39  	"github.com/ava-labs/subnet-evm/precompile/contract"
    40  	"github.com/ava-labs/subnet-evm/vmerrs"
    41  
    42  	_ "embed"
    43  
    44  	"github.com/ethereum/go-ethereum/common"
    45  )
    46  {{$contract := .Contract}}
    47  const (
    48  	// Gas costs for each function. These are set to 1 by default.
    49  	// You should set a gas cost for each function in your contract.
    50  	// Generally, you should not set gas costs very low as this may cause your network to be vulnerable to DoS attacks.
    51  	// There are some predefined gas costs in contract/utils.go that you can use.
    52  	{{- if .Contract.AllowList}}
    53  	// This contract also uses AllowList precompile.
    54  	// You should also increase gas costs of functions that read from AllowList storage.
    55  	{{- end}}
    56  	{{- range .Contract.Funcs}}
    57  	{{.Normalized.Name}}GasCost uint64 = 1 /* SET A GAS COST HERE */ {{if not .Original.IsConstant | and $contract.AllowList}} + allowlist.ReadAllowListGasCost {{end}}
    58  	{{- end}}
    59  	{{- if .Contract.Fallback}}
    60  	{{.Contract.Type}}FallbackGasCost uint64 = 1 // SET A GAS COST LESS THAN 2300 HERE
    61    {{- end}}
    62  )
    63  
    64  // CUSTOM CODE STARTS HERE
    65  // Reference imports to suppress errors from unused imports. This code and any unnecessary imports can be removed.
    66  var (
    67  	_ = abi.JSON
    68  	_ = errors.New
    69  	_ = big.NewInt
    70  	_ = vmerrs.ErrOutOfGas
    71  	_ = common.Big0
    72  )
    73  
    74  // Singleton StatefulPrecompiledContract and signatures.
    75  var (
    76  	{{- range .Contract.Funcs}}
    77  
    78  	{{- if not .Original.IsConstant | and $contract.AllowList}}
    79  
    80  	ErrCannot{{.Normalized.Name}} = errors.New("non-enabled cannot call {{.Original.Name}}")
    81  	{{- end}}
    82  	{{- end}}
    83  
    84  	{{- if .Contract.Fallback | and $contract.AllowList}}
    85  	Err{{.Contract.Type}}CannotFallback = errors.New("non-enabled cannot call fallback function")
    86  	{{- end}}
    87  
    88  	// {{.Contract.Type}}RawABI contains the raw ABI of {{.Contract.Type}} contract.
    89  	{{- if .Contract.ABIFilename | eq ""}}
    90  	{{.Contract.Type}}RawABI = "{{.Contract.InputABI}}"
    91  	{{- else}}
    92  	//go:embed {{.Contract.ABIFilename}}
    93  	{{.Contract.Type}}RawABI string
    94  	{{- end}}
    95  
    96  	{{.Contract.Type}}ABI = contract.ParseABI({{.Contract.Type}}RawABI)
    97  
    98  	{{.Contract.Type}}Precompile = create{{.Contract.Type}}Precompile()
    99  )
   100  
   101  {{$structs := .Structs}}
   102  {{range $structs}}
   103  	// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
   104  	type {{.Name}} struct {
   105  	{{range $field := .Fields}}
   106  	{{$field.Name}} {{$field.Type}}{{end}}
   107  	}
   108  {{- end}}
   109  
   110  {{- range .Contract.Funcs}}
   111  {{ if len .Normalized.Inputs | lt 1}}
   112  type {{capitalise .Normalized.Name}}Input struct{
   113  {{range .Normalized.Inputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}}
   114  }
   115  {{- end}}
   116  {{ if len .Normalized.Outputs | lt 1}}
   117  type {{capitalise .Normalized.Name}}Output struct{
   118  {{range .Normalized.Outputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}}
   119  }
   120  {{- end}}
   121  {{- end}}
   122  
   123  {{if .Contract.AllowList}}
   124  // Get{{.Contract.Type}}AllowListStatus returns the role of [address] for the {{.Contract.Type}} list.
   125  func Get{{.Contract.Type}}AllowListStatus(stateDB contract.StateDB, address common.Address) allowlist.Role {
   126  	return allowlist.GetAllowListStatus(stateDB, ContractAddress, address)
   127  }
   128  
   129  // Set{{.Contract.Type}}AllowListStatus sets the permissions of [address] to [role] for the
   130  // {{.Contract.Type}} list. Assumes [role] has already been verified as valid.
   131  // This stores the [role] in the contract storage with address [ContractAddress]
   132  // and [address] hash. It means that any reusage of the [address] key for different value
   133  // conflicts with the same slot [role] is stored.
   134  // Precompile implementations must use a different key than [address] for their storage.
   135  func Set{{.Contract.Type}}AllowListStatus(stateDB contract.StateDB, address common.Address, role allowlist.Role) {
   136  	allowlist.SetAllowListRole(stateDB, ContractAddress, address, role)
   137  }
   138  {{end}}
   139  
   140  {{range .Contract.Funcs}}
   141  {{if len .Normalized.Inputs | lt 1}}
   142  // Unpack{{capitalise .Normalized.Name}}Input attempts to unpack [input] as {{capitalise .Normalized.Name}}Input
   143  // assumes that [input] does not include selector (omits first 4 func signature bytes)
   144  func Unpack{{capitalise .Normalized.Name}}Input(input []byte) ({{capitalise .Normalized.Name}}Input, error) {
   145  	inputStruct := {{capitalise .Normalized.Name}}Input{}
   146  	// The strict mode in decoding is disabled after Durango. You can re-enable by changing the last argument to true.
   147  	err := {{$contract.Type}}ABI.UnpackInputIntoInterface(&inputStruct, "{{.Original.Name}}", input, false)
   148  
   149  	return inputStruct, err
   150  }
   151  
   152  // Pack{{.Normalized.Name}} packs [inputStruct] of type {{capitalise .Normalized.Name}}Input into the appropriate arguments for {{.Original.Name}}.
   153  func Pack{{.Normalized.Name}}(inputStruct {{capitalise .Normalized.Name}}Input) ([]byte, error) {
   154  	return {{$contract.Type}}ABI.Pack("{{.Original.Name}}", {{range .Normalized.Inputs}} inputStruct.{{capitalise .Name}}, {{end}})
   155  }
   156  {{else if len .Normalized.Inputs | eq 1 }}
   157  {{$method := .}}
   158  {{$input := index $method.Normalized.Inputs 0}}
   159  {{$bindedType := bindtype $input.Type $structs}}
   160  // Unpack{{capitalise .Normalized.Name}}Input attempts to unpack [input] into the {{$bindedType}} type argument
   161  // assumes that [input] does not include selector (omits first 4 func signature bytes)
   162  func Unpack{{capitalise .Normalized.Name}}Input(input []byte)({{$bindedType}}, error) {
   163  // The strict mode in decoding is disabled after Durango. You can re-enable by changing the last argument to true.
   164  res, err := {{$contract.Type}}ABI.UnpackInput("{{$method.Original.Name}}", input, false)
   165  if err != nil {
   166  	return {{bindtypenew $input.Type $structs}}, err
   167  }
   168  unpacked := *abi.ConvertType(res[0], new({{$bindedType}})).(*{{$bindedType}})
   169  return unpacked, nil
   170  }
   171  
   172  // Pack{{.Normalized.Name}} packs [{{decapitalise $input.Name}}] of type {{$bindedType}} into the appropriate arguments for {{$method.Original.Name}}.
   173  // the packed bytes include selector (first 4 func signature bytes).
   174  // This function is mostly used for tests.
   175  func Pack{{$method.Normalized.Name}}( {{decapitalise $input.Name}} {{$bindedType}},) ([]byte, error) {
   176  	return {{$contract.Type}}ABI.Pack("{{$method.Original.Name}}", {{decapitalise $input.Name}},)
   177  }
   178  {{else}}
   179  // Pack{{.Normalized.Name}} packs the include selector (first 4 func signature bytes).
   180  // This function is mostly used for tests.
   181  func Pack{{.Normalized.Name}}() ([]byte, error) {
   182  	return {{$contract.Type}}ABI.Pack("{{.Original.Name}}")
   183  }
   184  {{end}}
   185  
   186  {{if len .Normalized.Outputs | lt 1}}
   187  // Pack{{capitalise .Normalized.Name}}Output attempts to pack given [outputStruct] of type {{capitalise .Normalized.Name}}Output
   188  // to conform the ABI outputs.
   189  func Pack{{capitalise .Normalized.Name}}Output (outputStruct {{capitalise .Normalized.Name}}Output) ([]byte, error) {
   190  	return {{$contract.Type}}ABI.PackOutput("{{.Original.Name}}",
   191  		{{- range .Normalized.Outputs}}
   192  		outputStruct.{{capitalise .Name}},
   193  		{{- end}}
   194  	)
   195  }
   196  
   197  // Unpack{{capitalise .Normalized.Name}}Output attempts to unpack [output] as {{capitalise .Normalized.Name}}Output
   198  // assumes that [output] does not include selector (omits first 4 func signature bytes)
   199  func Unpack{{capitalise .Normalized.Name}}Output(output []byte) ({{capitalise .Normalized.Name}}Output, error) {
   200  	outputStruct := {{capitalise .Normalized.Name}}Output{}
   201  	err := {{$contract.Type}}ABI.UnpackIntoInterface(&outputStruct, "{{.Original.Name}}", output)
   202  
   203  	return outputStruct, err
   204  }
   205  
   206  {{else if len .Normalized.Outputs | eq 1 }}
   207  {{$method := .}}
   208  {{$output := index $method.Normalized.Outputs 0}}
   209  {{$bindedType := bindtype $output.Type $structs}}
   210  // Pack{{capitalise .Normalized.Name}}Output attempts to pack given {{decapitalise $output.Name}} of type {{$bindedType}}
   211  // to conform the ABI outputs.
   212  func Pack{{$method.Normalized.Name}}Output ({{decapitalise $output.Name}} {{$bindedType}}) ([]byte, error) {
   213  	return {{$contract.Type}}ABI.PackOutput("{{$method.Original.Name}}", {{decapitalise $output.Name}})
   214  }
   215  
   216  // Unpack{{capitalise .Normalized.Name}}Output attempts to unpack given [output] into the {{$bindedType}} type output
   217  // assumes that [output] does not include selector (omits first 4 func signature bytes)
   218  func Unpack{{capitalise .Normalized.Name}}Output(output []byte)({{$bindedType}}, error) {
   219  	res, err := {{$contract.Type}}ABI.Unpack("{{$method.Original.Name}}", output)
   220  	if err != nil {
   221  		return {{bindtypenew $output.Type $structs}}, err
   222  	}
   223  	unpacked := *abi.ConvertType(res[0], new({{$bindedType}})).(*{{$bindedType}})
   224  	return unpacked, nil
   225  }
   226  {{end}}
   227  
   228  func {{decapitalise .Normalized.Name}}(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
   229  	if remainingGas, err = contract.DeductGas(suppliedGas, {{.Normalized.Name}}GasCost); err != nil {
   230  		return nil, 0, err
   231  	}
   232  
   233  	{{- if not .Original.IsConstant}}
   234  	if readOnly {
   235  		return nil, remainingGas, vmerrs.ErrWriteProtection
   236  	}
   237   	{{- end}}
   238  
   239  	{{- if len .Normalized.Inputs | eq 0}}
   240  	// no input provided for this function
   241  	{{else}}
   242  	// attempts to unpack [input] into the arguments to the {{.Normalized.Name}}Input.
   243  	// Assumes that [input] does not include selector
   244  	// You can use unpacked [inputStruct] variable in your code
   245  	inputStruct, err := Unpack{{capitalise .Normalized.Name}}Input(input)
   246  	if err != nil{
   247  		return nil, remainingGas, err
   248  	}
   249  	{{- end}}
   250  
   251  	{{if not .Original.IsConstant | and $contract.AllowList}}
   252  	// Allow list is enabled and {{.Normalized.Name}} is a state-changer function.
   253  	// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
   254  	// You can modify/delete this code if you don't want this function to be restricted by the allow list.
   255  	stateDB := accessibleState.GetStateDB()
   256  	// Verify that the caller is in the allow list and therefore has the right to call this function.
   257  	callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
   258  	if !callerStatus.IsEnabled() {
   259  		return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannot{{.Normalized.Name}}, caller)
   260  	}
   261  	// allow list code ends here.
   262    {{end}}
   263  	// CUSTOM CODE STARTS HERE
   264  	{{- if len .Normalized.Inputs | ne 0}}
   265  	_ = inputStruct // CUSTOM CODE OPERATES ON INPUT
   266  	{{- end}}
   267  
   268  	{{- if len .Normalized.Outputs | eq 0}}
   269  	// this function does not return an output, leave this one as is
   270  	packedOutput := []byte{}
   271  	{{- else}}
   272  	{{- if len .Normalized.Outputs | lt 1}}
   273  	var output {{capitalise .Normalized.Name}}Output // CUSTOM CODE FOR AN OUTPUT
   274  	{{- else }}
   275  	{{$output := index .Normalized.Outputs 0}}
   276  	var output {{bindtype $output.Type $structs}} // CUSTOM CODE FOR AN OUTPUT
   277  	{{- end}}
   278  	packedOutput, err := Pack{{.Normalized.Name}}Output(output)
   279  	if err != nil {
   280  		return nil, remainingGas, err
   281  	}
   282  	{{- end}}
   283  
   284  	// Return the packed output and the remaining gas
   285  	return packedOutput, remainingGas, nil
   286  }
   287  {{end}}
   288  
   289  {{- if .Contract.Fallback}}
   290  {{- with .Contract.Fallback}}
   291  // {{decapitalise $contract.Type}}Fallback executed if a function identifier does not match any of the available functions in a smart contract.
   292  // This function cannot take an input or return an output.
   293  func {{decapitalise $contract.Type}}Fallback (accessibleState contract.AccessibleState, caller common.Address, addr common.Address, _ []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
   294  	if remainingGas, err = contract.DeductGas(suppliedGas, {{$contract.Type}}FallbackGasCost); err != nil {
   295  		return nil, 0, err
   296  	}
   297  
   298  	if readOnly {
   299  		return nil, remainingGas, vmerrs.ErrWriteProtection
   300  	}
   301  
   302  	{{- if $contract.AllowList}}
   303  	// Allow list is enabled and {{.Normalized.Name}} is a state-changer function.
   304  	// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
   305  	// You can modify/delete this code if you don't want this function to be restricted by the allow list.
   306  	stateDB := accessibleState.GetStateDB()
   307  	// Verify that the caller is in the allow list and therefore has the right to call this function.
   308  	callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
   309  	if !callerStatus.IsEnabled() {
   310  		return nil, remainingGas, fmt.Errorf("%w: %s", Err{{$contract.Type}}CannotFallback, caller)
   311  	}
   312  	// allow list code ends here.
   313  	{{- end}}
   314  
   315  	// CUSTOM CODE STARTS HERE
   316  
   317  	// Fallback can return data in output.
   318  	// The returned data will not be ABI-encoded.
   319  	// Instead it will be returned without modifications (not even padding).
   320  	output := []byte{}
   321  	// return raw output
   322  	return output, remainingGas, nil
   323  }
   324  {{- end}}
   325  {{- end}}
   326  
   327  // create{{.Contract.Type}}Precompile returns a StatefulPrecompiledContract with getters and setters for the precompile.
   328  {{if .Contract.AllowList}} // Access to the getters/setters is controlled by an allow list for ContractAddress.{{end}}
   329  func create{{.Contract.Type}}Precompile() contract.StatefulPrecompiledContract {
   330  	var functions []*contract.StatefulPrecompileFunction
   331  	{{- if .Contract.AllowList}}
   332  	functions = append(functions, allowlist.CreateAllowListFunctions(ContractAddress)...)
   333  	{{- end}}
   334  
   335  	abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{
   336  		{{- range .Contract.Funcs}}
   337  		"{{.Original.Name}}": {{decapitalise .Normalized.Name}},
   338  		{{- end}}
   339  	}
   340  
   341  	for name, function := range abiFunctionMap {
   342  		method, ok := {{$contract.Type}}ABI.Methods[name]
   343  		if !ok {
   344  			panic(fmt.Errorf("given method (%s) does not exist in the ABI", name))
   345  		}
   346  		functions = append(functions, contract.NewStatefulPrecompileFunction(method.ID, function))
   347  	}
   348  
   349  	{{- if .Contract.Fallback}}
   350  	// Construct the contract with the fallback function.
   351  	statefulContract, err :=  contract.NewStatefulPrecompileContract({{decapitalise $contract.Type}}Fallback, functions)
   352  	if err != nil {
   353  		panic(err)
   354  	}
   355  	return statefulContract
   356  	{{- else}}
   357  	// Construct the contract with no fallback function.
   358  	statefulContract, err := contract.NewStatefulPrecompileContract(nil, functions)
   359  	if err != nil {
   360  		panic(err)
   361  	}
   362  	return statefulContract
   363  	{{- end}}
   364  }
   365  `