aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi/bind/base.go
blob: 75e3c1d2d0f32b01d7d90efb49e2487f261c036e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 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 (
    "errors"
    "fmt"
    "math/big"

    "github.com/ethereum/go-ethereum/accounts/abi"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
)

// SignerFn is a signer function callback when a contract requires a method to
// sign the transaction before submission.
type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)

// AuthOpts is the authorization data required to create a valid Ethereum transaction.
type AuthOpts 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)

    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)
    GasLimit *big.Int // Gas limit to set for the transaction execution (nil = estimate + 10%)
}

// BoundContract is the base wrapper object that reflects a contract on the
// Ethereum network. It contains a collection of methods that are used by the
// higher level contract bindings to operate.
type BoundContract struct {
    address    common.Address     // Deployment address of the contract on the Ethereum blockchain
    abi        abi.ABI            // Reflect based ABI to access the correct Ethereum methods
    caller     ContractCaller     // Read interface to interact with the blockchain
    transactor ContractTransactor // Write interface to interact with the blockchain
}

// NewBoundContract creates a low level contract interface through which calls
// and transactions may be made through.
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor) *BoundContract {
    return &BoundContract{
        address:    address,
        abi:        abi,
        caller:     caller,
        transactor: transactor,
    }
}

// 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 (c *BoundContract) Call(result interface{}, method string, params ...interface{}) error {
    input, err := c.abi.Pack(method, params...)
    if err != nil {
        return err
    }
    output, err := c.caller.ContractCall(c.address, input)
    if err != nil {
        return err
    }
    return c.abi.Unpack(result, method, output)
}

// 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 *AuthOpts, method string, params ...interface{}) (*types.Transaction, error) {
    input, err := c.abi.Pack(method, params...)
    if err != nil {
        return nil, err
    }
    // Ensure a valid value field and resolve the account nonce
    value := opts.Value
    if value == nil {
        value = new(big.Int)
    }
    nonce := uint64(0)
    if opts.Nonce == nil {
        nonce, err = c.transactor.AccountNonce(opts.Account)
        if err != nil {
            return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
        }
    } else {
        nonce = opts.Nonce.Uint64()
    }
    // Figure out the gas allowance and gas price values
    gasPrice := opts.GasPrice
    if gasPrice == nil {
        gasPrice, err = c.transactor.GasPrice()
        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, c.address, value, input)
        if err != nil {
            return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
        }
    }
    // Create the transaction, sign it and schedule it for execution
    rawTx := types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
    if opts.Signer == nil {
        return nil, errors.New("no signer to authorize the transaction with")
    }
    signedTx, err := opts.Signer(opts.Account, rawTx)
    if err != nil {
        return nil, err
    }
    if err := c.transactor.SendTransaction(signedTx); err != nil {
        return nil, err
    }
    return signedTx, nil
}