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 `