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 `