#37359 [BC-Insight] Failure to Generate ABI Binding in Golang

Submitted on Dec 2nd 2024 at 20:36:56 UTC by @CertiK for Attackathon | Ethereum Protocol

  • Report ID: #37359

  • Report Type: Blockchain/DLT

  • Report severity: Insight

  • Target: https://github.com/ledgerwatch/erigon

  • Impacts:

    • (Specifications) A bug in specifications with no direct impact on client implementations

Description

Brief/Intro

Abigen fails to generate ABI binding of Solidity code in the accounts/abi package when the keyword of Golang is passed as the inputs of the binding.

Vulnerability Details

Affected Codebase: https://github.com/erigontech/erigon/tree/v2.61.0-beta1

The Ethereum client Erigon (https://github.com/erigontech/erigon) provides the Abigen tool to generate the Golang wrapper around a Solidity contract ABI:

https://github.com/erigontech/erigon/blob/v2.61.0-beta1/accounts/abi/bind/bind.go#L60

func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
	var (
		// contracts is the map of each individual contract requested binding
		contracts = make(map[string]*tmplContract)


		// structs is the map of all redeclared structs shared by passed contracts.
		structs = make(map[string]*tmplStruct)


		// isLib is the map used to flag each encountered library as such
		isLib = make(map[string]struct{})
	)
	for i := 0; i < len(types); i++ {
		// Parse the actual ABI to generate the binding for
		evmABI, err := abi.JSON(strings.NewReader(abis[i]))
		if err != nil {
			return "", err
		}
		// Strip any whitespace from the JSON ABI
		strippedABI := strings.Map(func(r rune) rune {
			if unicode.IsSpace(r) {
				return -1
			}
			return r
		}, abis[i])


		// Extract the call and transact methods; events, struct definitions; and sort them alphabetically
		var (
			calls     = make(map[string]*tmplMethod)
			transacts = make(map[string]*tmplMethod)
			events    = make(map[string]*tmplEvent)
			fallback  *tmplMethod
			receive   *tmplMethod


			// identifiers are used to detect duplicated identifiers of functions
			// and events. For all calls, transacts and events, abigen will generate
			// corresponding bindings. However we have to ensure there is no
			// identifier collisions in the bindings of these categories.
			callIdentifiers     = make(map[string]bool)
			transactIdentifiers = make(map[string]bool)
			eventIdentifiers    = make(map[string]bool)
		)
		for _, original := range evmABI.Methods {
			// Normalize the method for capital cases and non-anonymous inputs/outputs
			normalized := original
			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
			// Ensure there is no duplicated identifier
			var identifiers = callIdentifiers
			if !original.IsConstant() {
				identifiers = transactIdentifiers
			}
			if identifiers[normalizedName] {
				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
			}
			identifiers[normalizedName] = true
			normalized.Name = normalizedName
			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
			copy(normalized.Inputs, original.Inputs)
			for j, input := range normalized.Inputs {
				if input.Name == "" {
					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
				}
				if hasStruct(input.Type) {
					bindStructType[lang](input.Type, structs)
				}
			}
			normalized.Outputs = make([]abi.Argument, len(original.Outputs))
			copy(normalized.Outputs, original.Outputs)
			for j, output := range normalized.Outputs {
				if output.Name != "" {
					normalized.Outputs[j].Name = capitalise(output.Name)
				}
				if hasStruct(output.Type) {
					bindStructType[lang](output.Type, structs)
				}
			}
			// Append the methods to the call or transact lists
			if original.IsConstant() {
				calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
			} else {
				transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
			}
		}
		for _, original := range evmABI.Events {
			// Skip anonymous events as they don't support explicit filtering
			if original.Anonymous {
				continue
			}
			// Normalize the event for capital cases and non-anonymous outputs
			normalized := original


			// Ensure there is no duplicated identifier
			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
			if eventIdentifiers[normalizedName] {
				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
			}
			eventIdentifiers[normalizedName] = true
			normalized.Name = normalizedName


			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
			copy(normalized.Inputs, original.Inputs)
			for j, input := range normalized.Inputs {
				if input.Name == "" {
					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
				}
				if hasStruct(input.Type) {
					bindStructType[lang](input.Type, structs)
				}
			}
			// Append the event to the accumulator list
			events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
		}
		// Add two special fallback functions if they exist
		if evmABI.HasFallback() {
			fallback = &tmplMethod{Original: evmABI.Fallback}
		}
		if evmABI.HasReceive() {
			receive = &tmplMethod{Original: evmABI.Receive}
		}
		// There is no easy way to pass arbitrary java objects to the Go side.
		if len(structs) > 0 && lang == LangJava {
			return "", errors.New("java binding for tuple arguments is not supported yet")
		}


		contracts[types[i]] = &tmplContract{
			Type:        capitalise(types[i]),
			InputABI:    strings.ReplaceAll(strippedABI, "\"", "\\\""),
			InputBin:    strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
			Constructor: evmABI.Constructor,
			Calls:       calls,
			Transacts:   transacts,
			Fallback:    fallback,
			Receive:     receive,
			Events:      events,
			Libraries:   make(map[string]string),
		}
		// Function 4-byte signatures are stored in the same sequence
		// as types, if available.
		if len(fsigs) > i {
			contracts[types[i]].FuncSigs = fsigs[i]
		}
		// Parse library references.
		for pattern, name := range libs {
			matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
			if err != nil {
				log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
			}
			if matched {
				contracts[types[i]].Libraries[pattern] = name
				// keep track that this type is a library
				if _, ok := isLib[name]; !ok {
					isLib[name] = struct{}{}
				}
			}
		}
	}
	// Check if that type has already been identified as a library
	for i := 0; i < len(types); i++ {
		_, ok := isLib[types[i]]
		contracts[types[i]].Library = ok
	}
	// Generate the contract template data content and render it
	data := &tmplData{
		Package:   pkg,
		Contracts: contracts,
		Libraries: libs,
		Structs:   structs,
	}
	buffer := new(bytes.Buffer)


	funcs := map[string]interface{}{
		"bindtype":      bindType[lang],
		"bindtopictype": bindTopicType[lang],
		"namedtype":     namedType[lang],
		"capitalise":    capitalise,
		"decapitalise":  decapitalise,
	}
	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
	if err := tmpl.Execute(buffer, data); err != nil {
		return "", err
	}
	// For Go bindings pass the code through gofmt to clean it up
	if lang == LangGo {
		code, err := format.Source(buffer.Bytes())
		if err != nil {
			return "", fmt.Errorf("%w\n%s", err, buffer)
		}
		return string(code), nil
	}
	// For all others just return as is for now
	return buffer.String(), nil
}

However, in case that the Golang keyword is passed in as the function parameter names, the ABI wrapper generation fails.

This issue of Abigen has been reported in the Go-ethereum: https://github.com/ethereum/go-ethereum/issues/25252

And it has been patched in the PR: https://github.com/ethereum/go-ethereum/pull/25307

Impact Details

The tool Abigen fails in case that the Golang keywords are passed as the function parameter names.

References

  • https://github.com/erigontech/erigon/tree/v2.61.0-beta1

  • https://github.com/ethereum/go-ethereum/issues/25252

  • https://github.com/ethereum/go-ethereum/pull/25307

Proof of Concept

Proof of Concept

For simplicity, we can reuse and modify the test case from go-ethereum (https://github.com/ethereum/go-ethereum/pull/25307 ) to verify the issue:

 // Test Golang Keyword conflict, for example, range keword
   {
      `RangeKeyword`,
      `
      // SPDX-License-Identifier: GPL-3.0
      pragma solidity >=0.4.22 <0.9.0;
      contract keywordcontract {
         function functionWithKeywordParameter(range uint256) public pure {}
      }
      `,
      []string{"0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"},
      []string{`[{"inputs":[{"internalType":"uint256","name":"range","type":"uint256"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`},
      `
         "context"
         "math/big"


         "github.com/ledgerwatch/erigon/accounts/abi/bind"
         "github.com/ledgerwatch/erigon/accounts/abi/bind/backends"
         "github.com/ledgerwatch/erigon/types"
         "github.com/ledgerwatch/erigon/crypto"
         "github.com/ledgerwatch/erigon/eth/ethconfig"
      `,
      `
var (
            key, _  = crypto.GenerateKey()
            user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
            sim     = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
         )
         _, tx, _, err := DeployRangeKeyword(user, sim)
         if err != nil {
            t.Fatalf("error deploying contract: %v", err)
         }
         sim.Commit()
         if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
            t.Errorf("error deploying the contract: %v", err)
         }
   `,
      nil,
      nil,
      nil,
      nil,
   }

Run the following unit test:

// Tests that packages generated by the binder can be successfully compiled and
// the requested tester run against it.
func TestGolangBindings(t *testing.T) {
   // Skip the test if no Go command can be found
   //gocmd := "go"
   gocmd := runtime.GOROOT() + "/bin/go"
   if !dir.FileExist(gocmd) {
      t.Skip("go sdk not found for testing")
   }
   // Create a temporary workspace for the test suite
   ws := t.TempDir()


   pkg := filepath.Join(ws, "bindtest")
   if err := os.MkdirAll(pkg, 0700); err != nil {
      t.Fatalf("failed to create package: %v", err)
   }
   // Generate the test suite for all the contracts
   for i, tt := range bindTests {
      var types []string
      if tt.types != nil {
         types = tt.types
      } else {
         types = []string{tt.name}
      }
      // Generate the binding and create a Go source file in the workspace
      bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs, tt.aliases)
      if err != nil {
         t.Fatalf("test %d: failed to generate binding: %v", i, err)
      }
      if err = os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil {
         t.Fatalf("test %d: failed to write binding: %v", i, err)
      }
      // Generate the test file with the injected test code
      code := fmt.Sprintf(`
         package bindtest


         import (
            "testing"
            %s
         )


         func Test%s(t *testing.T) {
            %s
         }
      `, tt.imports, tt.name, tt.tester)
      if err := os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0600); err != nil {
         t.Fatalf("test %d: failed to write tests: %v", i, err)
      }
   }
   // Convert the package to go modules and use the current source for go-ethereum
   moder := exec.Command(gocmd, "mod", "init", "bindtest")
   moder.Dir = pkg
   if out, err := moder.CombinedOutput(); err != nil {
      t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out)
   }
   pwd, _ := os.Getwd()
   replacer := exec.Command(gocmd, "mod", "edit", "-replace", "github.com/ledgerwatch/erigon="+filepath.Join(pwd, "..", "..", "..")) // Repo root
   replacer.Dir = pkg
   if out, err := replacer.CombinedOutput(); err != nil {
      t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
   }


   tidier := exec.Command(gocmd, "mod", "tidy")
   tidier.Dir = pkg
   if out, err := tidier.CombinedOutput(); err != nil {
      t.Fatalf("failed to tidy Go module file: %v\n%s", err, out)
   }
   //Test the entire package and report any failures
   cmd := exec.Command(gocmd, "test", "-v", "-count", "1")
   cmd.Dir = pkg
   if out, err := cmd.CombinedOutput(); err != nil {
      t.Fatalf("failed to run binding test: %v\n%s", err, out)
   }
}

The test result shows the Abigen fails due to the range keyword conflict:

=== RUN   TestGolangBindings
    bind_test.go:1859: test 28: failed to generate binding: 208:95: expected ')', found 'range' (and 9 more errors)
        
        // Code generated by abigen. DO NOT EDIT.
        // This file is a generated binding and any manual changes will be lost.
        
        package bindtest
        
        import (
        	"math/big"
        	"strings"
        	"fmt"
        	"reflect"
        
        	ethereum "github.com/ledgerwatch/erigon"
        	"github.com/ledgerwatch/erigon/accounts/abi"
        	"github.com/ledgerwatch/erigon/accounts/abi/bind"
        	libcommon "github.com/ledgerwatch/erigon-lib/common"
        	"github.com/ledgerwatch/erigon/core/types"
        	"github.com/ledgerwatch/erigon/event"
        )
        
        // Reference imports to suppress errors if they are not otherwise used.
        var (
        	_ = big.NewInt
        	_ = strings.NewReader
        	_ = ethereum.NotFound
        	_ = bind.Bind
        	_ = libcommon.Big1
        	_ = types.BloomLookup
        	_ = event.NewSubscription
        	_ = fmt.Errorf
        	_ = reflect.ValueOf
        )
        
        
        
        
        
        	// RangeKeywordABI is the input ABI used to generate the binding from.
        	const RangeKeywordABI = "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"range\",\"type\":\"uint256\"}],\"name\":\"functionWithKeywordParameter\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}]"
        
        	
        
        	
        		// RangeKeywordBin is the compiled bytecode used for deploying new contracts.
        		var RangeKeywordBin = "0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"
        
        		// DeployRangeKeyword deploys a new Ethereum contract, binding an instance of RangeKeyword to it.
        		func DeployRangeKeyword(auth *bind.TransactOpts, backend bind.ContractBackend ) (libcommon.Address, types.Transaction, *RangeKeyword, error) {
        		  parsed, err := abi.JSON(strings.NewReader(RangeKeywordABI))
        		  if err != nil {
        		    return libcommon.Address{}, nil, nil, err
        		  }
        		  
        		  address, tx, contract, err := bind.DeployContract(auth, parsed, libcommon.FromHex(RangeKeywordBin), backend )
        		  if err != nil {
        		    return libcommon.Address{}, nil, nil, err
        		  }
        		  return address, tx, &RangeKeyword{ RangeKeywordCaller: RangeKeywordCaller{contract: contract}, RangeKeywordTransactor: RangeKeywordTransactor{contract: contract}, RangeKeywordFilterer: RangeKeywordFilterer{contract: contract} }, nil
        		}
        	
        
        	// RangeKeyword is an auto generated Go binding around an Ethereum contract.
        	type RangeKeyword struct {
        	  RangeKeywordCaller     // Read-only binding to the contract
        	  RangeKeywordTransactor // Write-only binding to the contract
        	  RangeKeywordFilterer   // Log filterer for contract events
        	}
        
        	// RangeKeywordCaller is an auto generated read-only Go binding around an Ethereum contract.
        	type RangeKeywordCaller struct {
        	  contract *bind.BoundContract // Generic contract wrapper for the low level calls
        	}
        
        	// RangeKeywordTransactor is an auto generated write-only Go binding around an Ethereum contract.
        	type RangeKeywordTransactor struct {
        	  contract *bind.BoundContract // Generic contract wrapper for the low level calls
        	}
        
        	// RangeKeywordFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
        	type RangeKeywordFilterer struct {
        	  contract *bind.BoundContract // Generic contract wrapper for the low level calls
        	}
        
        	// RangeKeywordSession is an auto generated Go binding around an Ethereum contract,
        	// with pre-set call and transact options.
        	type RangeKeywordSession struct {
        	  Contract     *RangeKeyword        // Generic contract binding to set the session for
        	  CallOpts     bind.CallOpts     // Call options to use throughout this session
        	  TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
        	}
        
        	// RangeKeywordCallerSession is an auto generated read-only Go binding around an Ethereum contract,
        	// with pre-set call options.
        	type RangeKeywordCallerSession struct {
        	  Contract *RangeKeywordCaller // Generic contract caller binding to set the session for
        	  CallOpts bind.CallOpts    // Call options to use throughout this session
        	}
        
        	// RangeKeywordTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
        	// with pre-set transact options.
        	type RangeKeywordTransactorSession struct {
        	  Contract     *RangeKeywordTransactor // Generic contract transactor binding to set the session for
        	  TransactOpts bind.TransactOpts    // Transaction auth options to use throughout this session
        	}
        
        	// RangeKeywordRaw is an auto generated low-level Go binding around an Ethereum contract.
        	type RangeKeywordRaw struct {
        	  Contract *RangeKeyword // Generic contract binding to access the raw methods on
        	}
        
        	// RangeKeywordCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
        	type RangeKeywordCallerRaw struct {
        		Contract *RangeKeywordCaller // Generic read-only contract binding to access the raw methods on
        	}
        
        	// RangeKeywordTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
        	type RangeKeywordTransactorRaw struct {
        		Contract *RangeKeywordTransactor // Generic write-only contract binding to access the raw methods on
        	}
        
        	// NewRangeKeyword creates a new instance of RangeKeyword, bound to a specific deployed contract.
        	func NewRangeKeyword(address libcommon.Address, backend bind.ContractBackend) (*RangeKeyword, error) {
        	  contract, err := bindRangeKeyword(address, backend, backend, backend)
        	  if err != nil {
        	    return nil, err
        	  }
        	  return &RangeKeyword{ RangeKeywordCaller: RangeKeywordCaller{contract: contract}, RangeKeywordTransactor: RangeKeywordTransactor{contract: contract}, RangeKeywordFilterer: RangeKeywordFilterer{contract: contract} }, nil
        	}
        
        	// NewRangeKeywordCaller creates a new read-only instance of RangeKeyword, bound to a specific deployed contract.
        	func NewRangeKeywordCaller(address libcommon.Address, caller bind.ContractCaller) (*RangeKeywordCaller, error) {
        	  contract, err := bindRangeKeyword(address, caller, nil, nil)
        	  if err != nil {
        	    return nil, err
        	  }
        	  return &RangeKeywordCaller{contract: contract}, nil
        	}
        
        	// NewRangeKeywordTransactor creates a new write-only instance of RangeKeyword, bound to a specific deployed contract.
        	func NewRangeKeywordTransactor(address libcommon.Address, transactor bind.ContractTransactor) (*RangeKeywordTransactor, error) {
        	  contract, err := bindRangeKeyword(address, nil, transactor, nil)
        	  if err != nil {
        	    return nil, err
        	  }
        	  return &RangeKeywordTransactor{contract: contract}, nil
        	}
        
        	// NewRangeKeywordFilterer creates a new log filterer instance of RangeKeyword, bound to a specific deployed contract.
         	func NewRangeKeywordFilterer(address libcommon.Address, filterer bind.ContractFilterer) (*RangeKeywordFilterer, error) {
         	  contract, err := bindRangeKeyword(address, nil, nil, filterer)
         	  if err != nil {
         	    return nil, err
         	  }
         	  return &RangeKeywordFilterer{contract: contract}, nil
         	}
        
        	// bindRangeKeyword binds a generic wrapper to an already deployed contract.
        	func bindRangeKeyword(address libcommon.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
        	  parsed, err := abi.JSON(strings.NewReader(RangeKeywordABI))
        	  if err != nil {
        	    return nil, err
        	  }
        	  return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
        	}
        
        	// Call invokes the (constant) contract method with params as input values and
        	// sets the output to result. The result type might be a single field for simple
        	// returns, a slice of interfaces for anonymous returns and a struct for named
        	// returns.
        	func (_RangeKeyword *RangeKeywordRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
        		return _RangeKeyword.Contract.RangeKeywordCaller.contract.Call(opts, result, method, params...)
        	}
        
        	// Transfer initiates a plain transaction to move funds to the contract, calling
        	// its default method if one is available.
        	func (_RangeKeyword *RangeKeywordRaw) Transfer(opts *bind.TransactOpts) (types.Transaction, error) {
        		return _RangeKeyword.Contract.RangeKeywordTransactor.contract.Transfer(opts)
        	}
        
        	// Transact invokes the (paid) contract method with params as input values.
        	func (_RangeKeyword *RangeKeywordRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (types.Transaction, error) {
        		return _RangeKeyword.Contract.RangeKeywordTransactor.contract.Transact(opts, method, params...)
        	}
        
        	// Call invokes the (constant) contract method with params as input values and
        	// sets the output to result. The result type might be a single field for simple
        	// returns, a slice of interfaces for anonymous returns and a struct for named
        	// returns.
        	func (_RangeKeyword *RangeKeywordCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
        		return _RangeKeyword.Contract.contract.Call(opts, result, method, params...)
        	}
        
        	// Transfer initiates a plain transaction to move funds to the contract, calling
        	// its default method if one is available.
        	func (_RangeKeyword *RangeKeywordTransactorRaw) Transfer(opts *bind.TransactOpts) (types.Transaction, error) {
        		return _RangeKeyword.Contract.contract.Transfer(opts)
        	}
        
        	// Transact invokes the (paid) contract method with params as input values.
        	func (_RangeKeyword *RangeKeywordTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (types.Transaction, error) {
        		return _RangeKeyword.Contract.contract.Transact(opts, method, params...)
        	}
        
        	
        		// FunctionWithKeywordParameter is a free data retrieval call binding the contract method 0x527a119f.
        		//
        		// Solidity: function functionWithKeywordParameter(uint256 range) pure returns()
        		func (_RangeKeyword *RangeKeywordCaller) FunctionWithKeywordParameter(opts *bind.CallOpts , range *big.Int ) ( error) {
        			var out []interface{}
        			err := _RangeKeyword.contract.Call(opts, &out, "functionWithKeywordParameter" , range)
        			
        			if err != nil {
        				return  err
        			}
        			
        			
        			return  err
        			
        		}
        
        		// FunctionWithKeywordParameter is a free data retrieval call binding the contract method 0x527a119f.
        		//
        		// Solidity: function functionWithKeywordParameter(uint256 range) pure returns()
        		func (_RangeKeyword *RangeKeywordSession) FunctionWithKeywordParameter( range *big.Int ) (   error) {
        		  return _RangeKeyword.Contract.FunctionWithKeywordParameter(&_RangeKeyword.CallOpts , range)
        		}
        
        		// FunctionWithKeywordParameter is a free data retrieval call binding the contract method 0x527a119f.
        		//
        		// Solidity: function functionWithKeywordParameter(uint256 range) pure returns()
        		func (_RangeKeyword *RangeKeywordCallerSession) FunctionWithKeywordParameter( range *big.Int ) (   error) {
        		  return _RangeKeyword.Contract.FunctionWithKeywordParameter(&_RangeKeyword.CallOpts , range)
        		}




        
--- FAIL: TestGolangBindings (0.15s)


FAIL

Was this helpful?