Ver código fonte

accounts/abi/bind, cmd/abigen: port to templates, bind to solidity

Péter Szilágyi 9 anos atrás
pai
commit
73308dbe0e

+ 10 - 4
accounts/abi/bind/auth.go

@@ -18,6 +18,8 @@ package bind
 
 import (
 	"errors"
+	"io"
+	"io/ioutil"
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
@@ -25,9 +27,13 @@ import (
 )
 
 // NewTransactor is a utility method to easily create a transaction signer from
-// an encrypted json key file and the associated passphrase.
-func NewTransactor(keyjson string, passphrase string) (*TransactOpts, error) {
-	key, err := crypto.DecryptKey([]byte(keyjson), passphrase)
+// an encrypted json key stream and the associated passphrase.
+func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
+	json, err := ioutil.ReadAll(keyin)
+	if err != nil {
+		return nil, err
+	}
+	key, err := crypto.DecryptKey(json, passphrase)
 	if err != nil {
 		return nil, err
 	}
@@ -38,7 +44,7 @@ func NewTransactor(keyjson string, passphrase string) (*TransactOpts, error) {
 // from a plain go-ethereum crypto key.
 func NewKeyedTransactor(key *crypto.Key) *TransactOpts {
 	return &TransactOpts{
-		Account: key.Address,
+		From: key.Address,
 		Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
 			if address != key.Address {
 				return nil, errors.New("not authorized to sign this account")

+ 12 - 8
accounts/abi/bind/backend.go

@@ -38,14 +38,18 @@ type ContractCaller interface {
 // to the transactor to decide.
 type ContractTransactor interface {
 	// Nonce retrieves the current pending nonce associated with an account.
-	AccountNonce(account common.Address) (uint64, error)
-
-	// GasPrice retrieves the currently suggested gas price to allow a timely execution
-	// of a transaction.
-	GasPrice() (*big.Int, error)
-
-	// GasLimit tries to estimate the gas needed to execute a specific transaction.
-	GasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error)
+	PendingAccountNonce(account common.Address) (uint64, error)
+
+	// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
+	// execution of a transaction.
+	SuggestGasPrice() (*big.Int, error)
+
+	// EstimateGasLimit tries to estimate the gas needed to execute a specific
+	// transaction based on the current pending state of the backend blockchain.
+	// There is no guarantee that this is the true gas limit requirement as other
+	// transactions may be added or removed by miners, but it should provide a basis
+	// for setting a reasonable default.
+	EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error)
 
 	// SendTransaction injects the transaction into the pending pool for execution.
 	SendTransaction(*types.Transaction) error

+ 7 - 4
accounts/abi/bind/backends/nil.go

@@ -24,6 +24,9 @@ import (
 	"github.com/ethereum/go-ethereum/core/types"
 )
 
+// This nil assignment ensures compile time that nilBackend implements bind.ContractBackend.
+var _ bind.ContractBackend = (*nilBackend)(nil)
+
 // nilBackend implements bind.ContractBackend, but panics on any method call.
 // Its sole purpose is to support the binding tests to construct the generated
 // wrappers without calling any methods on them.
@@ -32,12 +35,12 @@ type nilBackend struct{}
 func (*nilBackend) ContractCall(common.Address, []byte, bool) ([]byte, error) {
 	panic("not implemented")
 }
-func (*nilBackend) GasLimit(common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
+func (*nilBackend) EstimateGasLimit(common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
 	panic("not implemented")
 }
-func (*nilBackend) GasPrice() (*big.Int, error)                 { panic("not implemented") }
-func (*nilBackend) AccountNonce(common.Address) (uint64, error) { panic("not implemented") }
-func (*nilBackend) SendTransaction(*types.Transaction) error    { panic("not implemented") }
+func (*nilBackend) SuggestGasPrice() (*big.Int, error)                 { panic("not implemented") }
+func (*nilBackend) PendingAccountNonce(common.Address) (uint64, error) { panic("not implemented") }
+func (*nilBackend) SendTransaction(*types.Transaction) error           { panic("not implemented") }
 
 // NewNilBackend creates a new binding backend that can be used for instantiation
 // but will panic on any invocation. Its sole purpose is to help testing.

+ 40 - 26
accounts/abi/bind/backends/remote.go

@@ -30,6 +30,9 @@ import (
 	"github.com/ethereum/go-ethereum/rpc"
 )
 
+// This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend.
+var _ bind.ContractBackend = (*rpcBackend)(nil)
+
 // rpcBackend implements bind.ContractBackend, and acts as the data provider to
 // Ethereum contracts bound to Go structs. It uses an RPC connection to delegate
 // all its functionality.
@@ -53,16 +56,16 @@ func NewRPCBackend(client rpc.Client) bind.ContractBackend {
 // request is a JSON RPC request package assembled internally from the client
 // method calls.
 type request struct {
-	JsonRpc string        `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
-	Id      int           `json:"id"`      // Auto incrementing ID number for this request
+	JSONRPC string        `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
+	ID      int           `json:"id"`      // Auto incrementing ID number for this request
 	Method  string        `json:"method"`  // Remote procedure name to invoke on the server
 	Params  []interface{} `json:"params"`  // List of parameters to pass through (keep types simple)
 }
 
 // response is a JSON RPC response package sent back from the API server.
 type response struct {
-	JsonRpc string          `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
-	Id      int             `json:"id"`      // Auto incrementing ID number for this request
+	JSONRPC string          `json:"jsonrpc"` // Version of the JSON RPC protocol, always set to 2.0
+	ID      int             `json:"id"`      // Auto incrementing ID number for this request
 	Error   json.RawMessage `json:"error"`   // Any error returned by the remote side
 	Result  json.RawMessage `json:"result"`  // Whatever the remote side sends us in reply
 }
@@ -71,9 +74,9 @@ type response struct {
 //
 // This is currently painfully non-concurrent, but it will have to do until we
 // find the time for niceties like this :P
-func (backend *rpcBackend) request(method string, params []interface{}) (json.RawMessage, error) {
-	backend.lock.Lock()
-	defer backend.lock.Unlock()
+func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessage, error) {
+	b.lock.Lock()
+	defer b.lock.Unlock()
 
 	// Ugly hack to serialize an empty list properly
 	if params == nil {
@@ -81,16 +84,16 @@ func (backend *rpcBackend) request(method string, params []interface{}) (json.Ra
 	}
 	// Assemble the request object
 	req := &request{
-		JsonRpc: "2.0",
-		Id:      int(atomic.AddUint32(&backend.autoid, 1)),
+		JSONRPC: "2.0",
+		ID:      int(atomic.AddUint32(&b.autoid, 1)),
 		Method:  method,
 		Params:  params,
 	}
-	if err := backend.client.Send(req); err != nil {
+	if err := b.client.Send(req); err != nil {
 		return nil, err
 	}
 	res := new(response)
-	if err := backend.client.Recv(res); err != nil {
+	if err := b.client.Recv(res); err != nil {
 		return nil, err
 	}
 	if len(res.Error) > 0 {
@@ -127,9 +130,9 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending
 	return common.FromHex(hex), nil
 }
 
-// AccountNonce implements ContractTransactor.AccountNonce, delegating the
-// current account nonce retrieval to the remote node.
-func (b *rpcBackend) AccountNonce(account common.Address) (uint64, error) {
+// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating
+// the current account nonce retrieval to the remote node.
+func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error) {
 	res, err := b.request("eth_getTransactionCount", []interface{}{account.Hex(), "pending"})
 	if err != nil {
 		return 0, err
@@ -138,12 +141,16 @@ func (b *rpcBackend) AccountNonce(account common.Address) (uint64, error) {
 	if err := json.Unmarshal(res, &hex); err != nil {
 		return 0, err
 	}
-	return new(big.Int).SetBytes(common.FromHex(hex)).Uint64(), nil
+	nonce, ok := new(big.Int).SetString(hex, 0)
+	if !ok {
+		return 0, fmt.Errorf("invalid nonce hex: %s", hex)
+	}
+	return nonce.Uint64(), nil
 }
 
-// GasPrice implements ContractTransactor.GasPrice, delegating the gas price
-// oracle request to the remote node.
-func (b *rpcBackend) GasPrice() (*big.Int, error) {
+// SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the
+// gas price oracle request to the remote node.
+func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) {
 	res, err := b.request("eth_gasPrice", nil)
 	if err != nil {
 		return nil, err
@@ -152,12 +159,16 @@ func (b *rpcBackend) GasPrice() (*big.Int, error) {
 	if err := json.Unmarshal(res, &hex); err != nil {
 		return nil, err
 	}
-	return new(big.Int).SetBytes(common.FromHex(hex)), nil
+	price, ok := new(big.Int).SetString(hex, 0)
+	if !ok {
+		return nil, fmt.Errorf("invalid price hex: %s", hex)
+	}
+	return price, nil
 }
 
-// GasLimit implements ContractTransactor.GasLimit, delegating the gas estimation
-// to the remote node.
-func (b *rpcBackend) GasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
+// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating
+// the gas estimation to the remote node.
+func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
 	// Pack up the request into an RPC argument
 	args := struct {
 		From  common.Address  `json:"from"`
@@ -179,12 +190,15 @@ func (b *rpcBackend) GasLimit(sender common.Address, contract *common.Address, v
 	if err := json.Unmarshal(res, &hex); err != nil {
 		return nil, err
 	}
-	// Convert the response back to a Go byte slice and return
-	return new(big.Int).SetBytes(common.FromHex(hex)), nil
+	estimate, ok := new(big.Int).SetString(hex, 0)
+	if !ok {
+		return nil, fmt.Errorf("invalid estimate hex: %s", hex)
+	}
+	return estimate, nil
 }
 
-// Transact implements ContractTransactor.SendTransaction, delegating the raw
-// transaction injection to the remote node.
+// SendTransaction implements ContractTransactor.SendTransaction, delegating the
+// raw transaction injection to the remote node.
 func (b *rpcBackend) SendTransaction(tx *types.Transaction) error {
 	data, err := rlp.EncodeToBytes(tx)
 	if err != nil {

+ 18 - 15
accounts/abi/bind/backends/simulated.go

@@ -19,6 +19,7 @@ package backends
 import (
 	"math/big"
 
+	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core"
 	"github.com/ethereum/go-ethereum/core/state"
@@ -27,6 +28,9 @@ import (
 	"github.com/ethereum/go-ethereum/event"
 )
 
+// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
+var _ bind.ContractBackend = (*SimulatedBackend)(nil)
+
 // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
 // the background. Its main purpose is to allow easily testing contract bindings.
 type SimulatedBackend struct {
@@ -79,13 +83,11 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
 		statedb *state.StateDB
 	)
 	if pending {
-		block, statedb = b.pendingBlock, b.pendingState
+		block, statedb = b.pendingBlock, b.pendingState.Copy()
 	} else {
 		block = b.blockchain.CurrentBlock()
 		statedb, _ = b.blockchain.State()
 	}
-	statedb = statedb.Copy()
-
 	// Set infinite balance to the a fake caller account
 	from := statedb.GetOrNewStateObject(common.Address{})
 	from.SetBalance(common.MaxBig)
@@ -100,28 +102,29 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
 		data:     data,
 	}
 	// Execute the call and return
-	vmenv := core.NewEnv(statedb, b.blockchain, msg, block.Header())
+	vmenv := core.NewEnv(statedb, b.blockchain, msg, block.Header(), nil)
 	gaspool := new(core.GasPool).AddGas(common.MaxBig)
 
 	out, _, err := core.ApplyMessage(vmenv, msg, gaspool)
 	return out, err
 }
 
-// AccountNonce implements ContractTransactor.AccountNonce, retrieving the nonce
-// currently pending for the account.
-func (b *SimulatedBackend) AccountNonce(account common.Address) (uint64, error) {
+// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving
+// the nonce currently pending for the account.
+func (b *SimulatedBackend) PendingAccountNonce(account common.Address) (uint64, error) {
 	return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
 }
 
-// GasPrice implements ContractTransactor.GasPrice. Since the simulated chain
-// doens't have miners, we just return a gas price of 1 for any call.
-func (b *SimulatedBackend) GasPrice() (*big.Int, error) {
+// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
+// chain doens't have miners, we just return a gas price of 1 for any call.
+func (b *SimulatedBackend) SuggestGasPrice() (*big.Int, error) {
 	return big.NewInt(1), nil
 }
 
-// GasLimit implements ContractTransactor.GasLimit, executing the requested code
-// against the currently pending block/state and returning the used gas.
-func (b *SimulatedBackend) GasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
+// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the
+// requested code against the currently pending block/state and returning the used
+// gas.
+func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
 	// Create a copy of the currently pending state db to screw around with
 	var (
 		block   = b.pendingBlock
@@ -142,14 +145,14 @@ func (b *SimulatedBackend) GasLimit(sender common.Address, contract *common.Addr
 		data:     data,
 	}
 	// Execute the call and return
-	vmenv := core.NewEnv(statedb, b.blockchain, msg, block.Header())
+	vmenv := core.NewEnv(statedb, b.blockchain, msg, block.Header(), nil)
 	gaspool := new(core.GasPool).AddGas(common.MaxBig)
 
 	_, gas, err := core.ApplyMessage(vmenv, msg, gaspool)
 	return gas, err
 }
 
-// Transact implements ContractTransactor.SendTransaction, delegating the raw
+// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
 // transaction injection to the remote node.
 func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error {
 	blocks, _ := core.GenerateChain(b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {

+ 9 - 17
accounts/abi/bind/base.go

@@ -39,9 +39,9 @@ type CallOpts struct {
 // TransactOpts is the collection of authorization data required to create a
 // valid Ethereum transaction.
 type TransactOpts struct {
-	Account common.Address // Ethereum account to send the transaction from
-	Nonce   *big.Int       // Nonce to use for the transaction execution (nil = use pending state)
-	Signer  SignerFn       // Method to use for signing the transaction (mandatory)
+	From   common.Address // Ethereum account to send the transaction from
+	Nonce  *big.Int       // Nonce to use for the transaction execution (nil = use pending state)
+	Signer SignerFn       // Method to use for signing the transaction (mandatory)
 
 	Value    *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
 	GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
@@ -72,12 +72,8 @@ func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller
 // DeployContract deploys a contract onto the Ethereum blockchain and binds the
 // deployment address with a Go wrapper.
 func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
-	// Sanity check the authorization options
-	if opts == nil {
-		return common.Address{}, nil, nil, errors.New("transaction options missing")
-	}
 	// Otherwise try to deploy the contract
-	c := NewBoundContract(common.Address{}, abi, backend.(ContractCaller), backend.(ContractTransactor))
+	c := NewBoundContract(common.Address{}, abi, backend, backend)
 
 	input, err := c.abi.Pack("", params...)
 	if err != nil {
@@ -87,7 +83,7 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
 	if err != nil {
 		return common.Address{}, nil, nil, err
 	}
-	c.address = crypto.CreateAddress(opts.Account, tx.Nonce())
+	c.address = crypto.CreateAddress(opts.From, tx.Nonce())
 	return c.address, tx, c, nil
 }
 
@@ -115,10 +111,6 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
 // Transact invokes the (paid) contract method with params as input values and
 // value as the fund transfer to the contract.
 func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
-	// Sanity check the authorization options
-	if opts == nil {
-		return nil, errors.New("transaction options missing")
-	}
 	// Otherwise pack up the parameters and invoke the contract
 	input, err := c.abi.Pack(method, params...)
 	if err != nil {
@@ -139,7 +131,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
 	}
 	nonce := uint64(0)
 	if opts.Nonce == nil {
-		nonce, err = c.transactor.AccountNonce(opts.Account)
+		nonce, err = c.transactor.PendingAccountNonce(opts.From)
 		if err != nil {
 			return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
 		}
@@ -149,14 +141,14 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
 	// Figure out the gas allowance and gas price values
 	gasPrice := opts.GasPrice
 	if gasPrice == nil {
-		gasPrice, err = c.transactor.GasPrice()
+		gasPrice, err = c.transactor.SuggestGasPrice()
 		if err != nil {
 			return nil, fmt.Errorf("failed to suggest gas price: %v", err)
 		}
 	}
 	gasLimit := opts.GasLimit
 	if gasLimit == nil {
-		gasLimit, err = c.transactor.GasLimit(opts.Account, contract, value, input)
+		gasLimit, err = c.transactor.EstimateGasLimit(opts.From, contract, value, input)
 		if err != nil {
 			return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
 		}
@@ -171,7 +163,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
 	if opts.Signer == nil {
 		return nil, errors.New("no signer to authorize the transaction with")
 	}
-	signedTx, err := opts.Signer(opts.Account, rawTx)
+	signedTx, err := opts.Signer(opts.From, rawTx)
 	if err != nil {
 		return nil, err
 	}

+ 87 - 334
accounts/abi/bind/bind.go

@@ -15,13 +15,17 @@
 // 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"
-	"sort"
 	"strings"
+	"text/template"
+	"unicode"
 
 	"github.com/ethereum/go-ethereum/accounts/abi"
 	"golang.org/x/tools/imports"
@@ -31,254 +35,84 @@ import (
 // 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(abijson string, bytecode string, pkg string, kind string) (string, error) {
-	// Parse the actual ABI to generate the binding for
-	abi, err := abi.JSON(strings.NewReader(abijson))
-	if err != nil {
-		return "", err
-	}
-	// Generate the contract type, fields and methods
-	code := new(bytes.Buffer)
-	kind = strings.ToUpper(kind[:1]) + kind[1:]
-
-	fmt.Fprintf(code, "%s\n", bindContract(kind, strings.TrimSpace(abijson)))
-	fmt.Fprintf(code, "%s\n", bindConstructor(kind, strings.TrimSpace(bytecode), abi.Constructor))
-
-	methods := make([]string, 0, len(abi.Methods))
-	for name, _ := range abi.Methods {
-		methods = append(methods, name)
-	}
-	sort.Strings(methods)
-
-	for _, method := range methods {
-		fmt.Fprintf(code, "%s\n", bindMethod(kind, abi.Methods[method]))
-	}
-	// Format the code with goimports and return
-	buffer := new(bytes.Buffer)
-
-	fmt.Fprintf(buffer, "// This file is an automatically generated Go binding based on the contract ABI\n")
-	fmt.Fprintf(buffer, "// defined in %sABI. Do not modify as any change will likely be lost!\n\n", kind)
-	fmt.Fprintf(buffer, "package %s\n\n", pkg)
-	fmt.Fprintf(buffer, "%s\n\n", string(code.Bytes()))
-
-	blob, err := imports.Process("", buffer.Bytes(), nil)
-	if err != nil {
-		return "", fmt.Errorf("%v\n%s", err, code)
-	}
-	return string(blob), nil
-}
-
-// bindContract generates the basic wrapper code for interacting with an Ethereum
-// contract via the abi package. All contract methods will call into the generic
-// ones generated here.
-func bindContract(kind string, abijson string) string {
-	code := ""
-
-	// Generate the hard coded ABI used for Ethereum interaction
-	code += fmt.Sprintf("// Ethereum ABI used to generate the binding from.\nconst %sABI = `%s`\n\n", kind, abijson)
-
-	// Generate the high level contract wrapper types
-	code += fmt.Sprintf("// %s is an auto generated Go binding around an Ethereum contract.\n", kind)
-	code += fmt.Sprintf("type %s struct {\n", kind)
-	code += fmt.Sprintf("  %sCaller     // Read-only binding to the contract\n", kind)
-	code += fmt.Sprintf("  %sTransactor // Write-only binding to the contract\n", kind)
-	code += fmt.Sprintf("}\n\n")
-
-	code += fmt.Sprintf("// %sCaller is an auto generated read-only Go binding around an Ethereum contract.\n", kind)
-	code += fmt.Sprintf("type %sCaller struct {\n", kind)
-	code += fmt.Sprintf("  contract *bind.BoundContract // Generic contract wrapper for the low level calls\n")
-	code += fmt.Sprintf("}\n\n")
-
-	code += fmt.Sprintf("// %sTransactor is an auto generated write-only Go binding around an Ethereum contract.\n", kind)
-	code += fmt.Sprintf("type %sTransactor struct {\n", kind)
-	code += fmt.Sprintf("  contract *bind.BoundContract // Generic contract wrapper for the low level calls\n")
-	code += fmt.Sprintf("}\n\n")
-
-	// Generate the high level contract session wrapper types
-	code += fmt.Sprintf("// %sSession is an auto generated Go binding around an Ethereum contract,\n// with pre-set call and transact options.\n", kind)
-	code += fmt.Sprintf("type %sSession struct {\n", kind)
-	code += fmt.Sprintf("  Contract     *%s               // Generic contract binding to set the session for\n", kind)
-	code += fmt.Sprintf("  CallOpts     bind.CallOpts     // Call options to use throughout this session\n")
-	code += fmt.Sprintf("  TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session\n")
-	code += fmt.Sprintf("}\n\n")
-
-	code += fmt.Sprintf("// %sCallerSession is an auto generated read-only Go binding around an Ethereum contract,\n// with pre-set call options.\n", kind)
-	code += fmt.Sprintf("type %sCallerSession struct {\n", kind)
-	code += fmt.Sprintf("  Contract *%sCaller     // Generic contract caller binding to set the session for\n", kind)
-	code += fmt.Sprintf("  CallOpts bind.CallOpts // Call options to use throughout this session\n")
-	code += fmt.Sprintf("}\n\n")
-
-	code += fmt.Sprintf("// %sTransactorSession is an auto generated write-only Go binding around an Ethereum contract,\n// with pre-set transact options.\n", kind)
-	code += fmt.Sprintf("type %sTransactorSession struct {\n", kind)
-	code += fmt.Sprintf("  Contract     *%sTransactor     // Generic contract transactor binding to set the session for\n", kind)
-	code += fmt.Sprintf("  TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session\n")
-	code += fmt.Sprintf("}\n\n")
-
-	// Generate the constructor to create a bound contract
-	code += fmt.Sprintf("// New%s creates a new instance of %s, bound to a specific deployed contract.\n", kind, kind)
-	code += fmt.Sprintf("func New%s(address common.Address, backend bind.ContractBackend) (*%s, error) {\n", kind, kind)
-	code += fmt.Sprintf("  contract, err := bind%s(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))\n", kind)
-	code += fmt.Sprintf("  if err != nil {\n")
-	code += fmt.Sprintf("    return nil, err\n")
-	code += fmt.Sprintf("  }\n")
-	code += fmt.Sprintf("  return &%s{%sCaller: %sCaller{contract: contract}, %sTransactor: %sTransactor{contract: contract}}, nil\n", kind, kind, kind, kind, kind)
-	code += fmt.Sprintf("}\n\n")
-
-	code += fmt.Sprintf("// New%sCaller creates a new read-only instance of %s, bound to a specific deployed contract.\n", kind, kind)
-	code += fmt.Sprintf("func New%sCaller(address common.Address, caller bind.ContractCaller) (*%sCaller, error) {\n", kind, kind)
-	code += fmt.Sprintf("  contract, err := bind%s(address, caller, nil)\n", kind)
-	code += fmt.Sprintf("  if err != nil {\n")
-	code += fmt.Sprintf("    return nil, err\n")
-	code += fmt.Sprintf("  }\n")
-	code += fmt.Sprintf("  return &%sCaller{contract: contract}, nil\n", kind)
-	code += fmt.Sprintf("}\n\n")
-
-	code += fmt.Sprintf("// New%sTransactor creates a new write-only instance of %s, bound to a specific deployed contract.\n", kind, kind)
-	code += fmt.Sprintf("func New%sTransactor(address common.Address, transactor bind.ContractTransactor) (*%sTransactor, error) {\n", kind, kind)
-	code += fmt.Sprintf("  contract, err := bind%s(address, nil, transactor)\n", kind)
-	code += fmt.Sprintf("  if err != nil {\n")
-	code += fmt.Sprintf("    return nil, err\n")
-	code += fmt.Sprintf("  }\n")
-	code += fmt.Sprintf("  return &%sTransactor{contract: contract}, nil\n", kind)
-	code += fmt.Sprintf("}\n\n")
-
-	code += fmt.Sprintf("// bind%s binds a generic wrapper to an already deployed contract.\n", kind)
-	code += fmt.Sprintf("func bind%s(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {\n", kind)
-	code += fmt.Sprintf("  parsed, err := abi.JSON(strings.NewReader(%sABI))\n", kind)
-	code += fmt.Sprintf("  if err != nil {\n")
-	code += fmt.Sprintf("    return nil, err\n")
-	code += fmt.Sprintf("  }\n")
-	code += fmt.Sprintf("  return bind.NewBoundContract(address, parsed, caller, transactor), nil\n")
-	code += fmt.Sprintf("}")
-
-	return code
-}
-
-// bindConstructor
-func bindConstructor(kind string, bytecode string, constructor abi.Method) string {
-	// If no byte code was supplied, we cannot deploy
-	if bytecode == "" {
-		return ""
-	}
-	// Otherwise store the bytecode into a global constant
-	code := fmt.Sprintf("// Ethereum VM bytecode used for deploying new contracts.\nconst %sBin = `%s`\n\n", kind, bytecode)
-
-	// Generate the argument list for the constructor
-	args := make([]string, 0, len(constructor.Inputs))
-	for i, arg := range constructor.Inputs {
-		param := arg.Name
-		if param == "" {
-			param = fmt.Sprintf("arg%d", i)
+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
 		}
-		args = append(args, fmt.Sprintf("%s %s", param, bindType(arg.Type)))
-	}
-	arglist := ""
-	if len(args) > 0 {
-		arglist = "," + strings.Join(args, ",")
-	}
-	// Generate the cal parameter list for the dpeloyer
-	params := make([]string, len(args))
-	for i, param := range args {
-		params[i] = strings.Split(param, " ")[0]
-	}
-	paramlist := ""
-	if len(params) > 0 {
-		paramlist = "," + strings.Join(params, ",")
-	}
-	// And generate the global deployment function
-	code += fmt.Sprintf("// Deploy%s deploys a new contract, binding an instance of %s to it.\n", kind, kind)
-	code += fmt.Sprintf("func Deploy%s(auth *bind.TransactOpts, backend bind.ContractBackend %s) (common.Address, *types.Transaction, *%s, error) {\n", kind, arglist, kind)
-	code += fmt.Sprintf("  parsed, err := abi.JSON(strings.NewReader(%sABI))\n", kind)
-	code += fmt.Sprintf("  if err != nil {\n")
-	code += fmt.Sprintf("    return common.Address{}, nil, nil, err\n")
-	code += fmt.Sprintf("  }\n")
-	code += fmt.Sprintf("  address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(%sBin), backend %s)\n", kind, paramlist)
-	code += fmt.Sprintf("  if err != nil {\n")
-	code += fmt.Sprintf("    return common.Address{}, nil, nil, err\n")
-	code += fmt.Sprintf("  }\n")
-	code += fmt.Sprintf("  return address, tx, &%s{%sCaller: %sCaller{contract: contract}, %sTransactor: %sTransactor{contract: contract}}, nil\n", kind, kind, kind, kind, kind)
-	code += fmt.Sprintf("}\n\n")
-
-	return code
-}
-
-// bindMethod
-func bindMethod(kind string, method abi.Method) string {
-	var (
-		name     = strings.ToUpper(method.Name[:1]) + method.Name[1:]
-		prologue = new(bytes.Buffer)
-	)
-	// Generate the argument and return list for the function
-	args := make([]string, 0, len(method.Inputs))
-	for i, arg := range method.Inputs {
-		param := arg.Name
-		if param == "" {
-			param = fmt.Sprintf("arg%d", i)
+		// 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,
 		}
-		args = append(args, fmt.Sprintf("%s %s", param, bindType(arg.Type)))
 	}
-	returns, _ := bindReturn(prologue, name, method.Outputs)
-
-	// Generate the docs to help with coding against the binding
-	callTypeDoc := "free data retrieval call"
-	if !method.Const {
-		callTypeDoc = "paid mutator transaction"
+	// Generate the contract template data content and render it
+	data := &tmplData{
+		Package:   pkg,
+		Contracts: contracts,
 	}
-	docs := fmt.Sprintf("// %s is a %s binding the contract method 0x%x.\n", name, callTypeDoc, method.Id())
-	docs += fmt.Sprintf("// \n")
-	docs += fmt.Sprintf("// Solidity: %s", strings.TrimPrefix(method.String(), "function "))
+	buffer := new(bytes.Buffer)
 
-	// Generate the passthrough argument list for sessions
-	params := make([]string, len(args))
-	for i, param := range args {
-		params[i] = strings.Split(param, " ")[0]
+	funcs := map[string]interface{}{
+		"bindtype": bindType,
 	}
-	sessargs := ""
-	if len(params) > 0 {
-		sessargs = "," + strings.Join(params, ",")
+	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource))
+	if err := tmpl.Execute(buffer, data); err != nil {
+		return "", err
 	}
-	// Generate the method itself for both the read/write version and the combo too
-	code := fmt.Sprintf("%s\n", prologue)
-	if method.Const {
-		// Create the main call implementation
-		callargs := append([]string{"opts *bind.CallOpts"}, args...)
-
-		code += fmt.Sprintf("%s\n", docs)
-		code += fmt.Sprintf("func (_%s *%sCaller) %s(%s) (%s) {\n", kind, kind, name, strings.Join(callargs, ","), strings.Join(returns, ","))
-		code += fmt.Sprintf("  %s\n", bindCallBody(kind, method.Name, callargs, returns))
-		code += fmt.Sprintf("}\n\n")
-
-		// Create the wrapping session call implementation
-		code += fmt.Sprintf("%s\n", docs)
-		code += fmt.Sprintf("func (_%s *%sSession) %s(%s) (%s) {\n", kind, kind, name, strings.Join(args, ","), strings.Join(returns, ","))
-		code += fmt.Sprintf("  return _%s.Contract.%s(&_%s.CallOpts %s)\n", kind, name, kind, sessargs)
-		code += fmt.Sprintf("}\n\n")
-
-		code += fmt.Sprintf("%s\n", docs)
-		code += fmt.Sprintf("func (_%s *%sCallerSession) %s(%s) (%s) {\n", kind, kind, name, strings.Join(args, ","), strings.Join(returns, ","))
-		code += fmt.Sprintf("  return _%s.Contract.%s(&_%s.CallOpts %s)\n", kind, name, kind, sessargs)
-		code += fmt.Sprintf("}\n\n")
-	} else {
-		// Create the main transaction implementation
-		txargs := append([]string{"opts *bind.TransactOpts"}, args...)
-
-		code += fmt.Sprintf("%s\n", docs)
-		code += fmt.Sprintf("func (_%s *%sTransactor) %s(%s) (*types.Transaction, error) {\n", kind, kind, name, strings.Join(txargs, ","))
-		code += fmt.Sprintf("  %s\n", bindTransactionBody(kind, method.Name, txargs))
-		code += fmt.Sprintf("}\n\n")
-
-		// Create the wrapping session call implementation
-		code += fmt.Sprintf("%s\n", docs)
-		code += fmt.Sprintf("func (_%s *%sSession) %s(%s) (*types.Transaction, error) {\n", kind, kind, name, strings.Join(args, ","))
-		code += fmt.Sprintf("  return _%s.Contract.%s(&_%s.TransactOpts %s)\n", kind, name, kind, sessargs)
-		code += fmt.Sprintf("}\n\n")
-
-		code += fmt.Sprintf("%s\n", docs)
-		code += fmt.Sprintf("func (_%s *%sTransactorSession) %s(%s) (*types.Transaction, error) {\n", kind, kind, name, strings.Join(args, ","))
-		code += fmt.Sprintf("  return _%s.Contract.%s(&_%s.TransactOpts %s)\n", kind, name, kind, sessargs)
-		code += fmt.Sprintf("}\n\n")
+	// 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 code
+	return string(code), nil
 }
 
 // bindType converts a Solidity type to a Go one. Since there is no clear mapping
@@ -319,102 +153,21 @@ func bindType(kind abi.Type) string {
 	}
 }
 
-// bindReturn creates the list of return parameters for a method invocation. If
-// all the fields of the return type are named, and there is more than one value
-// being returned, the returns are wrapped in a result struct.
-func bindReturn(prologue *bytes.Buffer, method string, outputs []abi.Argument) ([]string, string) {
-	// Generate the anonymous return list for when a struct is not needed/possible
-	var (
-		returns   = make([]string, 0, len(outputs)+1)
-		anonymous = false
-	)
-	for _, ret := range outputs {
-		returns = append(returns, bindType(ret.Type))
-		if ret.Name == "" {
-			anonymous = true
-		}
-	}
-	if anonymous || len(returns) < 2 {
-		returns = append(returns, "error")
-		return returns, ""
-	}
-	// If the returns are named and numerous, wrap in a result struct
-	wrapper, impl := bindReturnStruct(method, outputs)
-	prologue.WriteString(impl + "\n")
-	return []string{"*" + wrapper, "error"}, wrapper
+// capitalise makes the first character of a string upper case.
+func capitalise(input string) string {
+	return strings.ToUpper(input[:1]) + input[1:]
 }
 
-// bindReturnStruct creates a Go structure with the specified fields to be used
-// as the return type from a method call.
-func bindReturnStruct(method string, returns []abi.Argument) (string, string) {
-	fields := make([]string, 0, len(returns))
-	for _, ret := range returns {
-		fields = append(fields, fmt.Sprintf("%s %s", strings.ToUpper(ret.Name[:1])+ret.Name[1:], bindType(ret.Type)))
+// 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
 	}
-	kind := fmt.Sprintf("%sResult", method)
-	docs := fmt.Sprintf("// %s is the result of the %s invocation.", kind, method)
-
-	return kind, fmt.Sprintf("%s\ntype %s struct {\n%s\n}", docs, kind, strings.Join(fields, "\n"))
-}
-
-// bindCallBody creates the Go code to declare a batch of return values, invoke
-// an Ethereum method call with the requested parameters, parse the binary output
-// into the return values and return them.
-func bindCallBody(kind string, method string, params []string, returns []string) string {
-	body := ""
-
-	// Allocate memory for each of the return values
-	rets := make([]string, 0, len(returns)-1)
-	if len(returns) > 1 {
-		body += "var ("
-		for i, kind := range returns[:len(returns)-1] { // Omit the final error
-			name := fmt.Sprintf("ret%d", i)
-
-			rets = append(rets, name)
-			body += fmt.Sprintf("%s = new(%s)\n", name, kind)
+	for _, out := range method.Outputs {
+		if out.Name == "" {
+			return false
 		}
-		body += ")\n"
-	}
-	// Assemble a single collector variable for the result ABI initialization
-	result := strings.Join(rets, ",")
-	if len(returns) > 2 {
-		result = "[]interface{}{" + result + "}"
-	}
-	// Extract the parameter list into a flat variable name list
-	inputs := make([]string, len(params)-1) // Omit the call options
-	for i, param := range params[1:] {
-		inputs[i] = strings.Split(param, " ")[0]
-	}
-	input := ""
-	if len(inputs) > 0 {
-		input = "," + strings.Join(inputs, ",")
-	}
-	// Request executing the contract call and return the results with the errors
-	body += fmt.Sprintf("err := _%s.contract.Call(opts, %s, \"%s\" %s)\n", kind, result, method, input)
-
-	outs := make([]string, 0, len(returns))
-	for _, ret := range rets { // Handle th final error separately
-		outs = append(outs, "*"+ret)
-	}
-	outs = append(outs, "err")
-
-	body += fmt.Sprintf("return %s", strings.Join(outs, ","))
-
-	return body
-}
-
-// bindTransactionBody creates the Go code to invoke an Ethereum transaction call
-// with the requested parameters, and return the assembled transaction object.
-func bindTransactionBody(kind string, method string, params []string) string {
-	// Extract the parameter list into a flat variable name list
-	inputs := make([]string, len(params)-1) // Omit the auth options
-	for i, param := range params[1:] {
-		inputs[i] = strings.Split(param, " ")[0]
-	}
-	input := ""
-	if len(inputs) > 0 {
-		input = "," + strings.Join(inputs, ",")
 	}
-	// Request executing the contract call and return the results with the errors
-	return fmt.Sprintf("return _%s.contract.Transact(opts, \"%s\" %s)", kind, method, input)
+	return true
 }

Diferenças do arquivo suprimidas por serem muito extensas
+ 11 - 9
accounts/abi/bind/bind_test.go


+ 212 - 0
accounts/abi/bind/template.go

@@ -0,0 +1,212 @@
+// 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
+
+import "github.com/ethereum/go-ethereum/accounts/abi"
+
+// tmplData is the data structure required to fill the binding template.
+type tmplData struct {
+	Package   string                   // Name of the package to place the generated file in
+	Contracts map[string]*tmplContract // List of contracts to generate into this file
+}
+
+// tmplContract contains the data needed to generate an individual contract binding.
+type tmplContract struct {
+	Type        string                 // Type name of the main contract binding
+	InputABI    string                 // JSON ABI used as the input to generate the binding from
+	InputBin    string                 // Optional EVM bytecode used to denetare deploy code from
+	Constructor abi.Method             // Contract constructor for deploy parametrization
+	Calls       map[string]*tmplMethod // Contract calls that only read state data
+	Transacts   map[string]*tmplMethod // Contract calls that write state data
+}
+
+// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
+// and cached data fields.
+type tmplMethod struct {
+	Original   abi.Method // Original method as parsed by the abi package
+	Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
+	Structured bool       // Whether the returns should be accumulated into a contract
+}
+
+// tmplSource is the Go source template use to generate the contract binding
+// based on.
+const tmplSource = `
+// This file is an automatically generated Go binding. Do not modify as any
+// change will likely be lost upon the next re-generation!
+
+package {{.Package}}
+
+{{range $contract := .Contracts}}
+	// {{.Type}}ABI is the input ABI used to generate the binding from.
+	const {{.Type}}ABI = ` + "`" + `{{.InputABI}}` + "`" + `
+
+	{{if .InputBin}}
+		// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
+		const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + `
+
+		// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
+		func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
+		  parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
+		  if err != nil {
+		    return common.Address{}, nil, nil, err
+		  }
+		  address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
+		  if err != nil {
+		    return common.Address{}, nil, nil, err
+		  }
+		  return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil
+		}
+	{{end}}
+
+	// {{.Type}} is an auto generated Go binding around an Ethereum contract.
+	type {{.Type}} struct {
+	  {{.Type}}Caller     // Read-only binding to the contract
+	  {{.Type}}Transactor // Write-only binding to the contract
+	}
+
+	// {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
+	type {{.Type}}Caller struct {
+	  contract *bind.BoundContract // Generic contract wrapper for the low level calls
+	}
+
+	// {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract.
+	type {{.Type}}Transactor struct {
+	  contract *bind.BoundContract // Generic contract wrapper for the low level calls
+	}
+
+	// {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
+	// with pre-set call and transact options.
+	type {{.Type}}Session struct {
+	  Contract     *{{.Type}}        // 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
+	}
+
+	// {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract,
+	// with pre-set call options.
+	type {{.Type}}CallerSession struct {
+	  Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
+	  CallOpts bind.CallOpts    // Call options to use throughout this session
+	}
+
+	// {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract,
+	// with pre-set transact options.
+	type {{.Type}}TransactorSession struct {
+	  Contract     *{{.Type}}Transactor // Generic contract transactor binding to set the session for
+	  TransactOpts bind.TransactOpts    // Transaction auth options to use throughout this session
+	}
+
+	// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
+	func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
+	  contract, err := bind{{.Type}}(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
+	  if err != nil {
+	    return nil, err
+	  }
+	  return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil
+	}
+
+	// New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
+	func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
+	  contract, err := bind{{.Type}}(address, caller, nil)
+	  if err != nil {
+	    return nil, err
+	  }
+	  return &{{.Type}}Caller{contract: contract}, nil
+	}
+
+	// New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
+	func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
+	  contract, err := bind{{.Type}}(address, nil, transactor)
+	  if err != nil {
+	    return nil, err
+	  }
+	  return &{{.Type}}Transactor{contract: contract}, nil
+	}
+
+	// bind{{.Type}} binds a generic wrapper to an already deployed contract.
+	func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
+	  parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
+	  if err != nil {
+	    return nil, err
+	  }
+	  return bind.NewBoundContract(address, parsed, caller, transactor), nil
+	}
+
+	{{range .Calls}}
+		{{if .Structured}}
+			// {{.Normalized.Name}}Result is the result of the {{.Normalized.Name}} invocation."
+			type {{.Normalized.Name}}Result struct {
+					{{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}}
+					{{end}}
+			}
+		{{end}}
+
+		// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
+		//
+		// Solidity: {{.Original.String}}
+		func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}*{{.Normalized.Name}}Result,{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}},{{end}}{{end}} error) {
+			var (
+				{{if .Structured}}ret = new(*{{.Normalized.Name}}Result){{else}}{{range $i, $_ := .Normalized.Outputs}}ret{{$i}} = new({{bindtype .Type}})
+				{{end}}{{end}}
+			)
+			out := {{if .Structured}}ret{{else}}{{if eq (len .Normalized.Outputs) 1}}ret0{{else}}[]interface{}{
+				{{range $i, $_ := .Normalized.Outputs}}ret{{$i}},
+				{{end}}
+			}{{end}}{{end}}
+			err := _{{$contract.Type}}.contract.Call(opts, out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+			return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err
+		}
+
+		// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
+		//
+		// Solidity: {{.Original.String}}
+		func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}*{{.Normalized.Name}}Result, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type}},{{end}} {{end}} error) {
+		  return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+		}
+
+		// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
+		//
+		// Solidity: {{.Original.String}}
+		func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}*{{.Normalized.Name}}Result, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type}},{{end}} {{end}} error) {
+		  return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+		}
+	{{end}}
+
+	{{range .Transacts}}
+		// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
+		//
+		// Solidity: {{.Original.String}}
+		func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) {
+			return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+		}
+
+		// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
+		//
+		// Solidity: {{.Original.String}}
+		func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) {
+		  return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+		}
+
+		// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
+		//
+		// Solidity: {{.Original.String}}
+		func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) {
+		  return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+		}
+	{{end}}
+{{end}}
+`

+ 64 - 20
cmd/abigen/main.go

@@ -17,53 +17,97 @@
 package main
 
 import (
+	"encoding/json"
 	"flag"
 	"fmt"
 	"io/ioutil"
 	"os"
 
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
+	"github.com/ethereum/go-ethereum/common/compiler"
 )
 
 var (
 	abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind")
 	binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)")
-	pkgFlag = flag.String("pkg", "", "Go package name to generate the binding into")
 	typFlag = flag.String("type", "", "Go struct name for the binding (default = package name)")
-	outFlag = flag.String("out", "", "Output path for the generated binding (default = stdout)")
+
+	solFlag  = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind")
+	solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested")
+
+	pkgFlag = flag.String("pkg", "", "Go package name to generate the binding into")
+	outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)")
 )
 
 func main() {
-	// Parse and validate the command line flags
+	// Parse and ensure all needed inputs are specified
 	flag.Parse()
 
-	if *abiFlag == "" {
-		fmt.Printf("No contract ABI path specified (--abi)\n")
+	if *abiFlag == "" && *solFlag == "" {
+		fmt.Printf("No contract ABI (--abi) or Solidity source (--sol) specified\n")
+		os.Exit(-1)
+	} else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && *solFlag != "" {
+		fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n")
 		os.Exit(-1)
 	}
 	if *pkgFlag == "" {
 		fmt.Printf("No destination Go package specified (--pkg)\n")
 		os.Exit(-1)
 	}
-	// Read the ABI json from disk and optionally the contract bytecode too
-	abi, err := ioutil.ReadFile(*abiFlag)
-	if err != nil {
-		fmt.Printf("Failed to read input ABI: %v\n", err)
-		os.Exit(-1)
-	}
-	bin := []byte{}
-	if *binFlag != "" {
-		if bin, err = ioutil.ReadFile(*binFlag); err != nil {
-			fmt.Printf("Failed to read input bytecode: %v\n", err)
+	// If the entire solidity code was specified, build and bind based on that
+	var (
+		abis  []string
+		bins  []string
+		types []string
+	)
+	if *solFlag != "" {
+		solc, err := compiler.New(*solcFlag)
+		if err != nil {
+			fmt.Printf("Failed to locate Solidity compiler: %v\n", err)
+			os.Exit(-1)
+		}
+		source, err := ioutil.ReadFile(*solFlag)
+		if err != nil {
+			fmt.Printf("Failed to read Soldity source code: %v\n", err)
 			os.Exit(-1)
 		}
+		contracts, err := solc.Compile(string(source))
+		if err != nil {
+			fmt.Printf("Failed to build Solidity contract: %v\n", err)
+			os.Exit(-1)
+		}
+		for name, contract := range contracts {
+			abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
+			abis = append(abis, string(abi))
+			bins = append(bins, contract.Code)
+			types = append(types, name)
+		}
+	} else {
+		// Otherwise load up the ABI, optional bytecode and type name from the parameters
+		abi, err := ioutil.ReadFile(*abiFlag)
+		if err != nil {
+			fmt.Printf("Failed to read input ABI: %v\n", err)
+			os.Exit(-1)
+		}
+		abis = append(abis, string(abi))
+
+		bin := []byte{}
+		if *binFlag != "" {
+			if bin, err = ioutil.ReadFile(*binFlag); err != nil {
+				fmt.Printf("Failed to read input bytecode: %v\n", err)
+				os.Exit(-1)
+			}
+		}
+		bins = append(bins, string(bin))
+
+		kind := *typFlag
+		if kind == "" {
+			kind = *pkgFlag
+		}
+		types = append(types, kind)
 	}
 	// Generate the contract binding
-	kind := *typFlag
-	if kind == "" {
-		kind = *pkgFlag
-	}
-	code, err := bind.Bind(string(abi), string(bin), *pkgFlag, kind)
+	code, err := bind.Bind(types, abis, bins, *pkgFlag)
 	if err != nil {
 		fmt.Printf("Failed to generate ABI binding: %v\n", err)
 		os.Exit(-1)

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff