base.go 15.7 KB
Newer Older
1
// Copyright 2015 The go-ethereum Authors
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// 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 (
20
	"context"
21 22 23
	"errors"
	"fmt"
	"math/big"
24 25
	"strings"
	"sync"
26

27
	"github.com/ethereum/go-ethereum"
28 29 30
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
31
	"github.com/ethereum/go-ethereum/crypto"
32
	"github.com/ethereum/go-ethereum/event"
33 34 35 36
)

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

39 40
// CallOpts is the collection of options to fine tune a contract call request.
type CallOpts struct {
41 42 43 44
	Pending     bool            // Whether to operate on the pending state or the last known one
	From        common.Address  // Optional the sender address, otherwise the first account is used
	BlockNumber *big.Int        // Optional the block number on which the call should be performed
	Context     context.Context // Network context to support cancellation and timeouts (nil = no timeout)
45 46 47 48 49
}

// TransactOpts is the collection of authorization data required to create a
// valid Ethereum transaction.
type TransactOpts struct {
50 51 52
	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)
53

54 55 56 57 58
	Value     *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
	GasPrice  *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
	GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
	GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
	GasLimit  uint64   // Gas limit to set for the transaction execution (0 = estimate)
59 60

	Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
61 62

	NoSend bool // Do all transact steps but do not send the transaction
63 64
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
// FilterOpts is the collection of options to fine tune filtering for events
// within a bound contract.
type FilterOpts struct {
	Start uint64  // Start of the queried range
	End   *uint64 // End of the range (nil = latest)

	Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
}

// WatchOpts is the collection of options to fine tune subscribing for events
// within a bound contract.
type WatchOpts struct {
	Start   *uint64         // Start of the queried range (nil = latest)
	Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
// MetaData collects all metadata for a bound contract.
type MetaData struct {
	mu   sync.Mutex
	Sigs map[string]string
	Bin  string
	ABI  string
	ab   *abi.ABI
}

func (m *MetaData) GetAbi() (*abi.ABI, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.ab != nil {
		return m.ab, nil
	}
	if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
		return nil, err
	} else {
		m.ab = &parsed
	}
	return m.ab, nil
}

104 105 106 107 108 109 110 111
// 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
112
	filterer   ContractFilterer   // Event filtering to interact with the blockchain
113 114 115 116
}

// NewBoundContract creates a low level contract interface through which calls
// and transactions may be made through.
117
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
118 119 120 121 122
	return &BoundContract{
		address:    address,
		abi:        abi,
		caller:     caller,
		transactor: transactor,
123
		filterer:   filterer,
124 125 126
	}
}

127 128 129 130
// 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) {
	// Otherwise try to deploy the contract
131
	c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
132 133 134 135 136 137 138 139 140

	input, err := c.abi.Pack("", params...)
	if err != nil {
		return common.Address{}, nil, nil, err
	}
	tx, err := c.transact(opts, nil, append(bytecode, input...))
	if err != nil {
		return common.Address{}, nil, nil, err
	}
141
	c.address = crypto.CreateAddress(opts.From, tx.Nonce())
142 143 144
	return c.address, tx, c, nil
}

145 146 147 148
// 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.
149
func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
150 151 152 153
	// Don't crash on a lazy user
	if opts == nil {
		opts = new(CallOpts)
	}
154 155 156
	if results == nil {
		results = new([]interface{})
	}
157
	// Pack the input, call and unpack the results
158 159 160 161
	input, err := c.abi.Pack(method, params...)
	if err != nil {
		return err
	}
162
	var (
163
		msg    = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
		ctx    = ensureContext(opts.Context)
		code   []byte
		output []byte
	)
	if opts.Pending {
		pb, ok := c.caller.(PendingContractCaller)
		if !ok {
			return ErrNoPendingState
		}
		output, err = pb.PendingCallContract(ctx, msg)
		if err == nil && len(output) == 0 {
			// Make sure we have a contract to operate on, and bail out otherwise.
			if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
				return err
			} else if len(code) == 0 {
				return ErrNoCode
			}
		}
	} else {
183
		output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
184 185 186 187
		if err != nil {
			return err
		}
		if len(output) == 0 {
188
			// Make sure we have a contract to operate on, and bail out otherwise.
189
			if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
190 191 192 193 194 195
				return err
			} else if len(code) == 0 {
				return ErrNoCode
			}
		}
	}
196 197 198 199

	if len(*results) == 0 {
		res, err := c.abi.Unpack(method, output)
		*results = res
200 201
		return err
	}
202 203
	res := *results
	return c.abi.UnpackIntoInterface(res[0], method, output)
204 205
}

206
// Transact invokes the (paid) contract method with params as input values.
207 208
func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
	// Otherwise pack up the parameters and invoke the contract
209 210 211 212
	input, err := c.abi.Pack(method, params...)
	if err != nil {
		return nil, err
	}
213 214
	// todo(rjl493456442) check the method is payable or not,
	// reject invalid transaction at the first place
215 216 217
	return c.transact(opts, &c.address, input)
}

218
// RawTransact initiates a transaction with the given raw calldata as the input.
219
// It's usually used to initiate transactions for invoking **Fallback** function.
220 221 222 223 224 225
func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
	// todo(rjl493456442) check the method is payable or not,
	// reject invalid transaction at the first place
	return c.transact(opts, &c.address, calldata)
}

226 227 228
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
229 230
	// todo(rjl493456442) check the payable fallback or receive is defined
	// or not, reject invalid transaction at the first place
231 232 233
	return c.transact(opts, &c.address, nil)
}

234 235 236 237 238
// transact executes an actual transaction invocation, first deriving any missing
// authorization fields, and then scheduling the transaction for execution.
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
	var err error

239 240 241 242 243
	// Ensure a valid value field and resolve the account nonce
	value := opts.Value
	if value == nil {
		value = new(big.Int)
	}
244
	var nonce uint64
245
	if opts.Nonce == nil {
246
		nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
247 248 249 250 251 252
		if err != nil {
			return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
		}
	} else {
		nonce = opts.Nonce.Uint64()
	}
253 254 255 256
	// Figure out reasonable gas price values
	if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
		return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
	}
257
	head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil)
258 259 260 261 262
	if err != nil {
		return nil, err
	}
	if head.BaseFee != nil && opts.GasPrice == nil {
		if opts.GasTipCap == nil {
263
			tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
			if err != nil {
				return nil, err
			}
			opts.GasTipCap = tip
		}
		if opts.GasFeeCap == nil {
			gasFeeCap := new(big.Int).Add(
				opts.GasTipCap,
				new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
			)
			opts.GasFeeCap = gasFeeCap
		}
		if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 {
			return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap)
		}
	} else {
		if opts.GasFeeCap != nil || opts.GasTipCap != nil {
			return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
		}
		if opts.GasPrice == nil {
284
			price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
285 286 287 288
			if err != nil {
				return nil, err
			}
			opts.GasPrice = price
289 290 291
		}
	}
	gasLimit := opts.GasLimit
292
	if gasLimit == 0 {
293
		// Gas estimation cannot succeed without code for method invocations
294 295
		if contract != nil {
			if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
296
				return nil, err
297
			} else if len(code) == 0 {
298 299 300 301
				return nil, ErrNoCode
			}
		}
		// If the contract surely has code (or code is not needed), estimate the transaction
302
		msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input}
303
		gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
304
		if err != nil {
305
			return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
306 307 308
		}
	}
	// Create the transaction, sign it and schedule it for execution
309
	var rawTx *types.Transaction
310 311 312 313 314 315 316 317 318 319 320 321
	if opts.GasFeeCap == nil {
		baseTx := &types.LegacyTx{
			Nonce:    nonce,
			GasPrice: opts.GasPrice,
			Gas:      gasLimit,
			Value:    value,
			Data:     input,
		}
		if contract != nil {
			baseTx.To = &c.address
		}
		rawTx = types.NewTx(baseTx)
322
	} else {
323 324 325 326 327 328 329 330 331 332 333 334
		baseTx := &types.DynamicFeeTx{
			Nonce:     nonce,
			GasFeeCap: opts.GasFeeCap,
			GasTipCap: opts.GasTipCap,
			Gas:       gasLimit,
			Value:     value,
			Data:      input,
		}
		if contract != nil {
			baseTx.To = &c.address
		}
		rawTx = types.NewTx(baseTx)
335
	}
336 337 338
	if opts.Signer == nil {
		return nil, errors.New("no signer to authorize the transaction with")
	}
339
	signedTx, err := opts.Signer(opts.From, rawTx)
340 341 342
	if err != nil {
		return nil, err
	}
343 344 345
	if opts.NoSend {
		return signedTx, nil
	}
346
	if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
347 348 349 350
		return nil, err
	}
	return signedTx, nil
}
351

352 353 354 355 356 357 358 359
// FilterLogs filters contract logs for past blocks, returning the necessary
// channels to construct a strongly typed bound iterator on top of them.
func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
	// Don't crash on a lazy user
	if opts == nil {
		opts = new(FilterOpts)
	}
	// Append the event selector to the query parameters and construct the topic set
360
	query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
361

362
	topics, err := abi.MakeTopics(query...)
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
	if err != nil {
		return nil, nil, err
	}
	// Start the background filtering
	logs := make(chan types.Log, 128)

	config := ethereum.FilterQuery{
		Addresses: []common.Address{c.address},
		Topics:    topics,
		FromBlock: new(big.Int).SetUint64(opts.Start),
	}
	if opts.End != nil {
		config.ToBlock = new(big.Int).SetUint64(*opts.End)
	}
	/* TODO(karalabe): Replace the rest of the method below with this when supported
	sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
	*/
	buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
	if err != nil {
		return nil, nil, err
	}
	sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
		for _, log := range buff {
			select {
			case logs <- log:
			case <-quit:
				return nil
			}
		}
		return nil
	}), nil

	if err != nil {
		return nil, nil, err
	}
	return logs, sub, nil
}

// WatchLogs filters subscribes to contract logs for future blocks, returning a
// subscription object that can be used to tear down the watcher.
func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
	// Don't crash on a lazy user
	if opts == nil {
		opts = new(WatchOpts)
	}
	// Append the event selector to the query parameters and construct the topic set
409
	query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
410

411
	topics, err := abi.MakeTopics(query...)
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
	if err != nil {
		return nil, nil, err
	}
	// Start the background filtering
	logs := make(chan types.Log, 128)

	config := ethereum.FilterQuery{
		Addresses: []common.Address{c.address},
		Topics:    topics,
	}
	if opts.Start != nil {
		config.FromBlock = new(big.Int).SetUint64(*opts.Start)
	}
	sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
	if err != nil {
		return nil, nil, err
	}
	return logs, sub, nil
}

// UnpackLog unpacks a retrieved log into the provided output structure.
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
	if len(log.Data) > 0 {
435
		if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
436 437 438 439 440 441 442 443 444
			return err
		}
	}
	var indexed abi.Arguments
	for _, arg := range c.abi.Events[event].Inputs {
		if arg.Indexed {
			indexed = append(indexed, arg)
		}
	}
445
	return abi.ParseTopics(out, indexed, log.Topics[1:])
446 447
}

448 449 450 451 452 453 454 455 456 457 458 459 460
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
	if len(log.Data) > 0 {
		if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
			return err
		}
	}
	var indexed abi.Arguments
	for _, arg := range c.abi.Events[event].Inputs {
		if arg.Indexed {
			indexed = append(indexed, arg)
		}
	}
461
	return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
462 463
}

464 465
// ensureContext is a helper method to ensure a context is not nil, even if the
// user specified it as such.
466 467
func ensureContext(ctx context.Context) context.Context {
	if ctx == nil {
468
		return context.Background()
469 470 471
	}
	return ctx
}