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 `