github.com/cgcardona/r-subnet-evm@v0.1.5/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/cgcardona/r-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/cgcardona/r-subnet-evm/accounts/abi"
    36  	{{- if .Contract.AllowList}}
    37  	"github.com/cgcardona/r-subnet-evm/precompile/allowlist"
    38  	{{- end}}
    39  	"github.com/cgcardona/r-subnet-evm/precompile/contract"
    40  	"github.com/cgcardona/r-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  )
    71  
    72  // Singleton StatefulPrecompiledContract and signatures.
    73  var (
    74  	{{- range .Contract.Funcs}}
    75  
    76  	{{- if not .Original.IsConstant | and $contract.AllowList}}
    77  
    78  	ErrCannot{{.Normalized.Name}} = errors.New("non-enabled cannot call {{.Original.Name}}")
    79  	{{- end}}
    80  	{{- end}}
    81  
    82  	{{- if .Contract.Fallback | and $contract.AllowList}}
    83  	Err{{.Contract.Type}}CannotFallback = errors.New("non-enabled cannot call fallback function")
    84  	{{- end}}
    85  
    86  	// {{.Contract.Type}}RawABI contains the raw ABI of {{.Contract.Type}} contract.
    87  	{{- if .Contract.ABIFilename | eq ""}}
    88  	{{.Contract.Type}}RawABI = "{{.Contract.InputABI}}"
    89  	{{- else}}
    90  	//go:embed {{.Contract.ABIFilename}}
    91  	{{.Contract.Type}}RawABI string
    92  	{{- end}}
    93  
    94  	{{.Contract.Type}}ABI = contract.ParseABI({{.Contract.Type}}RawABI)
    95  
    96  	{{.Contract.Type}}Precompile = create{{.Contract.Type}}Precompile()
    97  )
    98  
    99  {{$structs := .Structs}}
   100  {{range $structs}}
   101  	// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
   102  	type {{.Name}} struct {
   103  	{{range $field := .Fields}}
   104  	{{$field.Name}} {{$field.Type}}{{end}}
   105  	}
   106  {{- end}}
   107  
   108  {{- range .Contract.Funcs}}
   109  {{ if len .Normalized.Inputs | lt 1}}
   110  type {{capitalise .Normalized.Name}}Input struct{
   111  {{range .Normalized.Inputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}}
   112  }
   113  {{- end}}
   114  {{ if len .Normalized.Outputs | lt 1}}
   115  type {{capitalise .Normalized.Name}}Output struct{
   116  {{range .Normalized.Outputs}} {{capitalise .Name}} {{bindtype .Type $structs}}; {{end}}
   117  }
   118  {{- end}}
   119  {{- end}}
   120  
   121  {{if .Contract.AllowList}}
   122  // Get{{.Contract.Type}}AllowListStatus returns the role of [address] for the {{.Contract.Type}} list.
   123  func Get{{.Contract.Type}}AllowListStatus(stateDB contract.StateDB, address common.Address) allowlist.Role {
   124  	return allowlist.GetAllowListStatus(stateDB, ContractAddress, address)
   125  }
   126  
   127  // Set{{.Contract.Type}}AllowListStatus sets the permissions of [address] to [role] for the
   128  // {{.Contract.Type}} list. Assumes [role] has already been verified as valid.
   129  // This stores the [role] in the contract storage with address [ContractAddress]
   130  // and [address] hash. It means that any reusage of the [address] key for different value
   131  // conflicts with the same slot [role] is stored.
   132  // Precompile implementations must use a different key than [address] for their storage.
   133  func Set{{.Contract.Type}}AllowListStatus(stateDB contract.StateDB, address common.Address, role allowlist.Role) {
   134  	allowlist.SetAllowListRole(stateDB, ContractAddress, address, role)
   135  }
   136  {{end}}
   137  
   138  {{range .Contract.Funcs}}
   139  {{if len .Normalized.Inputs | lt 1}}
   140  // Unpack{{capitalise .Normalized.Name}}Input attempts to unpack [input] as {{capitalise .Normalized.Name}}Input
   141  // assumes that [input] does not include selector (omits first 4 func signature bytes)
   142  func Unpack{{capitalise .Normalized.Name}}Input(input []byte) ({{capitalise .Normalized.Name}}Input, error) {
   143  	inputStruct := {{capitalise .Normalized.Name}}Input{}
   144  	err := {{$contract.Type}}ABI.UnpackInputIntoInterface(&inputStruct, "{{.Original.Name}}", input)
   145  
   146  	return inputStruct, err
   147  }
   148  
   149  // Pack{{.Normalized.Name}} packs [inputStruct] of type {{capitalise .Normalized.Name}}Input into the appropriate arguments for {{.Original.Name}}.
   150  func Pack{{.Normalized.Name}}(inputStruct {{capitalise .Normalized.Name}}Input) ([]byte, error) {
   151  	return {{$contract.Type}}ABI.Pack("{{.Original.Name}}", {{range .Normalized.Inputs}} inputStruct.{{capitalise .Name}}, {{end}})
   152  }
   153  {{else if len .Normalized.Inputs | eq 1 }}
   154  {{$method := .}}
   155  {{$input := index $method.Normalized.Inputs 0}}
   156  // Unpack{{capitalise .Normalized.Name}}Input attempts to unpack [input] into the {{bindtype $input.Type $structs}} type argument
   157  // assumes that [input] does not include selector (omits first 4 func signature bytes)
   158  func Unpack{{capitalise .Normalized.Name}}Input(input []byte)({{bindtype $input.Type $structs}}, error) {
   159  res, err := {{$contract.Type}}ABI.UnpackInput("{{$method.Original.Name}}", input)
   160  if err != nil {
   161  	return {{convertToNil $input.Type}}, err
   162  }
   163  unpacked := *abi.ConvertType(res[0], new({{bindtype $input.Type $structs}})).(*{{bindtype $input.Type $structs}})
   164  return unpacked, nil
   165  }
   166  
   167  // Pack{{.Normalized.Name}} packs [{{decapitalise $input.Name}}] of type {{bindtype $input.Type $structs}} into the appropriate arguments for {{$method.Original.Name}}.
   168  // the packed bytes include selector (first 4 func signature bytes).
   169  // This function is mostly used for tests.
   170  func Pack{{$method.Normalized.Name}}( {{decapitalise $input.Name}} {{bindtype $input.Type $structs}},) ([]byte, error) {
   171  	return {{$contract.Type}}ABI.Pack("{{$method.Original.Name}}", {{decapitalise $input.Name}},)
   172  }
   173  {{else}}
   174  // Pack{{.Normalized.Name}} packs the include selector (first 4 func signature bytes).
   175  // This function is mostly used for tests.
   176  func Pack{{.Normalized.Name}}() ([]byte, error) {
   177  	return {{$contract.Type}}ABI.Pack("{{.Original.Name}}")
   178  }
   179  {{end}}
   180  
   181  {{if len .Normalized.Outputs | lt 1}}
   182  // Pack{{capitalise .Normalized.Name}}Output attempts to pack given [outputStruct] of type {{capitalise .Normalized.Name}}Output
   183  // to conform the ABI outputs.
   184  func Pack{{capitalise .Normalized.Name}}Output (outputStruct {{capitalise .Normalized.Name}}Output) ([]byte, error) {
   185  	return {{$contract.Type}}ABI.PackOutput("{{.Original.Name}}",
   186  		{{- range .Normalized.Outputs}}
   187  		outputStruct.{{capitalise .Name}},
   188  		{{- end}}
   189  	)
   190  }
   191  
   192  {{else if len .Normalized.Outputs | eq 1 }}
   193  {{$method := .}}
   194  {{$output := index $method.Normalized.Outputs 0}}
   195  // Pack{{capitalise .Normalized.Name}}Output attempts to pack given {{decapitalise $output.Name}} of type {{bindtype $output.Type $structs}}
   196  // to conform the ABI outputs.
   197  func Pack{{$method.Normalized.Name}}Output ({{decapitalise $output.Name}} {{bindtype $output.Type $structs}}) ([]byte, error) {
   198  	return {{$contract.Type}}ABI.PackOutput("{{$method.Original.Name}}", {{decapitalise $output.Name}})
   199  }
   200  {{end}}
   201  
   202  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) {
   203  	if remainingGas, err = contract.DeductGas(suppliedGas, {{.Normalized.Name}}GasCost); err != nil {
   204  		return nil, 0, err
   205  	}
   206  
   207  	{{- if not .Original.IsConstant}}
   208  	if readOnly {
   209  		return nil, remainingGas, vmerrs.ErrWriteProtection
   210  	}
   211   	{{- end}}
   212  
   213  	{{- if len .Normalized.Inputs | eq 0}}
   214  	// no input provided for this function
   215  	{{else}}
   216  	// attempts to unpack [input] into the arguments to the {{.Normalized.Name}}Input.
   217  	// Assumes that [input] does not include selector
   218  	// You can use unpacked [inputStruct] variable in your code
   219  	inputStruct, err := Unpack{{capitalise .Normalized.Name}}Input(input)
   220  	if err != nil{
   221  		return nil, remainingGas, err
   222  	}
   223  	{{- end}}
   224  
   225  	{{if not .Original.IsConstant | and $contract.AllowList}}
   226  	// Allow list is enabled and {{.Normalized.Name}} is a state-changer function.
   227  	// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
   228  	// You can modify/delete this code if you don't want this function to be restricted by the allow list.
   229  	stateDB := accessibleState.GetStateDB()
   230  	// Verify that the caller is in the allow list and therefore has the right to call this function.
   231  	callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
   232  	if !callerStatus.IsEnabled() {
   233  		return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannot{{.Normalized.Name}}, caller)
   234  	}
   235  	// allow list code ends here.
   236    {{end}}
   237  	// CUSTOM CODE STARTS HERE
   238  	{{- if len .Normalized.Inputs | ne 0}}
   239  	_ = inputStruct // CUSTOM CODE OPERATES ON INPUT
   240  	{{- end}}
   241  
   242  	{{- if len .Normalized.Outputs | eq 0}}
   243  	// this function does not return an output, leave this one as is
   244  	packedOutput := []byte{}
   245  	{{- else}}
   246  	{{- if len .Normalized.Outputs | lt 1}}
   247  	var output {{capitalise .Normalized.Name}}Output // CUSTOM CODE FOR AN OUTPUT
   248  	{{- else }}
   249  	{{$output := index .Normalized.Outputs 0}}
   250  	var output {{bindtype $output.Type $structs}} // CUSTOM CODE FOR AN OUTPUT
   251  	{{- end}}
   252  	packedOutput, err := Pack{{.Normalized.Name}}Output(output)
   253  	if err != nil {
   254  		return nil, remainingGas, err
   255  	}
   256  	{{- end}}
   257  
   258  	// Return the packed output and the remaining gas
   259  	return packedOutput, remainingGas, nil
   260  }
   261  {{end}}
   262  
   263  {{- if .Contract.Fallback}}
   264  {{- with .Contract.Fallback}}
   265  // {{decapitalise $contract.Type}}Fallback executed if a function identifier does not match any of the available functions in a smart contract.
   266  // This function cannot take an input or return an output.
   267  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) {
   268  	if remainingGas, err = contract.DeductGas(suppliedGas, {{$contract.Type}}FallbackGasCost); err != nil {
   269  		return nil, 0, err
   270  	}
   271  
   272  	if readOnly {
   273  		return nil, remainingGas, vmerrs.ErrWriteProtection
   274  	}
   275  
   276  	{{- if $contract.AllowList}}
   277  	// Allow list is enabled and {{.Normalized.Name}} is a state-changer function.
   278  	// This part of the code restricts the function to be called only by enabled/admin addresses in the allow list.
   279  	// You can modify/delete this code if you don't want this function to be restricted by the allow list.
   280  	stateDB := accessibleState.GetStateDB()
   281  	// Verify that the caller is in the allow list and therefore has the right to call this function.
   282  	callerStatus := allowlist.GetAllowListStatus(stateDB, ContractAddress, caller)
   283  	if !callerStatus.IsEnabled() {
   284  		return nil, remainingGas, fmt.Errorf("%w: %s", Err{{$contract.Type}}CannotFallback, caller)
   285  	}
   286  	// allow list code ends here.
   287  	{{- end}}
   288  
   289  	// CUSTOM CODE STARTS HERE
   290  
   291  	// Fallback can return data in output.
   292  	// The returned data will not be ABI-encoded.
   293  	// Instead it will be returned without modifications (not even padding).
   294  	output := []byte{}
   295  	// return raw output
   296  	return output, remainingGas, nil
   297  }
   298  {{- end}}
   299  {{- end}}
   300  
   301  // create{{.Contract.Type}}Precompile returns a StatefulPrecompiledContract with getters and setters for the precompile.
   302  {{if .Contract.AllowList}} // Access to the getters/setters is controlled by an allow list for ContractAddress.{{end}}
   303  func create{{.Contract.Type}}Precompile() contract.StatefulPrecompiledContract {
   304  	var functions []*contract.StatefulPrecompileFunction
   305  	{{- if .Contract.AllowList}}
   306  	functions = append(functions, allowlist.CreateAllowListFunctions(ContractAddress)...)
   307  	{{- end}}
   308  
   309  	abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{
   310  		{{- range .Contract.Funcs}}
   311  		"{{.Original.Name}}": {{decapitalise .Normalized.Name}},
   312  		{{- end}}
   313  	}
   314  
   315  	for name, function := range abiFunctionMap {
   316  		method, ok := {{$contract.Type}}ABI.Methods[name]
   317  		if !ok {
   318  			panic(fmt.Errorf("given method (%s) does not exist in the ABI", name))
   319  		}
   320  		functions = append(functions, contract.NewStatefulPrecompileFunction(method.ID, function))
   321  	}
   322  
   323  	{{- if .Contract.Fallback}}
   324  	// Construct the contract with the fallback function.
   325  	statefulContract, err :=  contract.NewStatefulPrecompileContract({{decapitalise $contract.Type}}Fallback, functions)
   326  	if err != nil {
   327  		panic(err)
   328  	}
   329  	return statefulContract
   330  	{{- else}}
   331  	// Construct the contract with no fallback function.
   332  	statefulContract, err := contract.NewStatefulPrecompileContract(nil, functions)
   333  	if err != nil {
   334  		panic(err)
   335  	}
   336  	return statefulContract
   337  	{{- end}}
   338  }
   339  `