diff options
author | Péter Szilágyi <peterke@gmail.com> | 2016-03-24 23:35:42 +0800 |
---|---|---|
committer | Péter Szilágyi <peterke@gmail.com> | 2016-03-24 23:35:42 +0800 |
commit | cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b (patch) | |
tree | aa6d4c875884f20fc96f055f99f867f779500980 /accounts/abi/bind/bind.go | |
parent | 75c86f8646ed6a21116d6c5f5e400dd966bb218d (diff) | |
parent | 73308dbe0e08db015a7c461b5be1755dc3fcc737 (diff) | |
download | dexon-cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b.tar dexon-cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b.tar.gz dexon-cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b.tar.bz2 dexon-cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b.tar.lz dexon-cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b.tar.xz dexon-cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b.tar.zst dexon-cfb3a8ea8f98ef5a31d9e4a84a66eb5857461a7b.zip |
Merge pull request #2357 from karalabe/abi-binding-generator
accounts/abi/bind: Go ABI binding generator
Diffstat (limited to 'accounts/abi/bind/bind.go')
-rw-r--r-- | accounts/abi/bind/bind.go | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go new file mode 100644 index 000000000..8b587f1aa --- /dev/null +++ b/accounts/abi/bind/bind.go @@ -0,0 +1,173 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package bind generates Ethereum contract Go bindings. +// +// Detailed usage document and tutorial available on the go-ethereum Wiki page: +// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts +package bind + +import ( + "bytes" + "fmt" + "strings" + "text/template" + "unicode" + + "github.com/ethereum/go-ethereum/accounts/abi" + "golang.org/x/tools/imports" +) + +// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant +// to be used as is in client code, but rather as an intermediate struct which +// enforces compile time type safety and naming convention opposed to having to +// manually maintain hard coded strings that break on runtime. +func Bind(types []string, abis []string, bytecodes []string, pkg string) (string, error) { + // Process each individual contract requested binding + contracts := make(map[string]*tmplContract) + + 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, and sort them alphabetically + var ( + calls = make(map[string]*tmplMethod) + transacts = make(map[string]*tmplMethod) + ) + for _, original := range evmABI.Methods { + // Normalize the method for capital cases and non-anonymous inputs/outputs + normalized := original + normalized.Name = capitalise(original.Name) + + 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) + } + } + 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) + } + } + // Append the methos to the call or transact lists + if original.Const { + calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} + } else { + transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} + } + } + contracts[types[i]] = &tmplContract{ + Type: capitalise(types[i]), + InputABI: strippedABI, + InputBin: strings.TrimSpace(bytecodes[i]), + Constructor: evmABI.Constructor, + Calls: calls, + Transacts: transacts, + } + } + // Generate the contract template data content and render it + data := &tmplData{ + Package: pkg, + Contracts: contracts, + } + buffer := new(bytes.Buffer) + + funcs := map[string]interface{}{ + "bindtype": bindType, + } + tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource)) + if err := tmpl.Execute(buffer, data); err != nil { + return "", err + } + // Pass the code through goimports to clean it up and double check + code, err := imports.Process("", buffer.Bytes(), nil) + if err != nil { + return "", fmt.Errorf("%v\n%s", err, buffer) + } + return string(code), nil +} + +// bindType converts a Solidity type to a Go one. Since there is no clear mapping +// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly +// mapped will use an upscaled type (e.g. *big.Int). +func bindType(kind abi.Type) string { + stringKind := kind.String() + + switch { + case stringKind == "address": + return "common.Address" + + case stringKind == "hash": + return "common.Hash" + + case strings.HasPrefix(stringKind, "bytes"): + if stringKind == "bytes" { + return "[]byte" + } + return fmt.Sprintf("[%s]byte", stringKind[5:]) + + case strings.HasPrefix(stringKind, "int"): + switch stringKind[:3] { + case "8", "16", "32", "64": + return stringKind + } + return "*big.Int" + + case strings.HasPrefix(stringKind, "uint"): + switch stringKind[:4] { + case "8", "16", "32", "64": + return stringKind + } + return "*big.Int" + + default: + return stringKind + } +} + +// capitalise makes the first character of a string upper case. +func capitalise(input string) string { + return strings.ToUpper(input[:1]) + input[1:] +} + +// structured checks whether a method has enough information to return a proper +// Go struct ot if flat returns are needed. +func structured(method abi.Method) bool { + if len(method.Outputs) < 2 { + return false + } + for _, out := range method.Outputs { + if out.Name == "" { + return false + } + } + return true +} |