Skip to content

Commit 0414c11

Browse files
committed
accounts/abi/bind: refactor transact method (ethereum#23719)
1 parent 9b84e81 commit 0414c11

File tree

2 files changed

+308
-81
lines changed

2 files changed

+308
-81
lines changed

accounts/abi/bind/base.go

Lines changed: 131 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -188,108 +188,158 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
188188
return c.transact(opts, &c.address, nil)
189189
}
190190

191-
// transact executes an actual transaction invocation, first deriving any missing
192-
// authorization fields, and then scheduling the transaction for execution.
193-
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
194-
var err error
195-
196-
// Ensure a valid value field and resolve the account nonce
191+
func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
192+
// Normalize value
197193
value := opts.Value
198194
if value == nil {
199195
value = new(big.Int)
200196
}
201-
var nonce uint64
202-
if opts.Nonce == nil {
203-
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
197+
// Estimate TipCap
198+
gasTipCap := opts.GasTipCap
199+
if gasTipCap == nil {
200+
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
204201
if err != nil {
205-
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
202+
return nil, err
206203
}
207-
} else {
208-
nonce = opts.Nonce.Uint64()
204+
gasTipCap = tip
209205
}
210-
// Figure out reasonable gas price values
211-
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
212-
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
206+
// Estimate FeeCap
207+
gasFeeCap := opts.GasFeeCap
208+
if gasFeeCap == nil {
209+
gasFeeCap = new(big.Int).Add(
210+
gasTipCap,
211+
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
212+
)
213213
}
214-
head, err := c.transactor.HeaderByNumber(opts.Context, nil)
214+
if gasFeeCap.Cmp(gasTipCap) < 0 {
215+
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
216+
}
217+
// Estimate GasLimit
218+
gasLimit := opts.GasLimit
219+
if opts.GasLimit == 0 {
220+
var err error
221+
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
222+
if err != nil {
223+
return nil, err
224+
}
225+
}
226+
// create the transaction
227+
nonce, err := c.getNonce(opts)
215228
if err != nil {
216229
return nil, err
217230
}
218-
if head.BaseFee != nil && opts.GasPrice == nil {
219-
if opts.GasTipCap == nil {
220-
tip, err := c.transactor.SuggestGasTipCap(opts.Context)
221-
if err != nil {
222-
return nil, err
223-
}
224-
opts.GasTipCap = tip
225-
}
226-
if opts.GasFeeCap == nil {
227-
gasFeeCap := new(big.Int).Add(
228-
opts.GasTipCap,
229-
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
230-
)
231-
opts.GasFeeCap = gasFeeCap
232-
}
233-
if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 {
234-
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap)
235-
}
236-
} else {
237-
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
238-
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
239-
}
240-
if opts.GasPrice == nil {
241-
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
242-
if err != nil {
243-
return nil, err
244-
}
245-
opts.GasPrice = price
231+
baseTx := &types.DynamicFeeTx{
232+
To: contract,
233+
Nonce: nonce,
234+
GasFeeCap: gasFeeCap,
235+
GasTipCap: gasTipCap,
236+
Gas: gasLimit,
237+
Value: value,
238+
Data: input,
239+
}
240+
return types.NewTx(baseTx), nil
241+
}
242+
243+
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
244+
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
245+
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but EIP-1559 is not active yet")
246+
}
247+
// Normalize value
248+
value := opts.Value
249+
if value == nil {
250+
value = new(big.Int)
251+
}
252+
// Estimate GasPrice
253+
gasPrice := opts.GasPrice
254+
if gasPrice == nil {
255+
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
256+
if err != nil {
257+
return nil, err
246258
}
259+
gasPrice = price
247260
}
261+
// Estimate GasLimit
248262
gasLimit := opts.GasLimit
249-
if gasLimit == 0 {
250-
// Gas estimation cannot succeed without code for method invocations
251-
if contract != nil {
252-
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
253-
return nil, err
254-
} else if len(code) == 0 {
255-
return nil, ErrNoCode
256-
}
257-
}
258-
// If the contract surely has code (or code is not needed), estimate the transaction
259-
msg := XDPoSChain.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input}
260-
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
263+
if opts.GasLimit == 0 {
264+
var err error
265+
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
261266
if err != nil {
262-
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
267+
return nil, err
263268
}
264269
}
265-
// Create the transaction, sign it and schedule it for execution
266-
var rawTx *types.Transaction
267-
if opts.GasFeeCap == nil {
268-
baseTx := &types.LegacyTx{
269-
Nonce: nonce,
270-
GasPrice: opts.GasPrice,
271-
Gas: gasLimit,
272-
Value: value,
273-
Data: input,
274-
}
275-
if contract != nil {
276-
baseTx.To = &c.address
270+
// create the transaction
271+
nonce, err := c.getNonce(opts)
272+
if err != nil {
273+
return nil, err
274+
}
275+
baseTx := &types.LegacyTx{
276+
To: contract,
277+
Nonce: nonce,
278+
GasPrice: gasPrice,
279+
Gas: gasLimit,
280+
Value: value,
281+
Data: input,
282+
}
283+
return types.NewTx(baseTx), nil
284+
}
285+
286+
func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
287+
if contract != nil {
288+
// Gas estimation cannot succeed without code for method invocations.
289+
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
290+
return 0, err
291+
} else if len(code) == 0 {
292+
return 0, ErrNoCode
277293
}
278-
rawTx = types.NewTx(baseTx)
294+
}
295+
msg := XDPoSChain.CallMsg{
296+
From: opts.From,
297+
To: contract,
298+
GasPrice: gasPrice,
299+
GasTipCap: gasTipCap,
300+
GasFeeCap: gasFeeCap,
301+
Value: value,
302+
Data: input,
303+
}
304+
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
305+
}
306+
307+
func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
308+
if opts.Nonce == nil {
309+
return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
279310
} else {
280-
baseTx := &types.DynamicFeeTx{
281-
Nonce: nonce,
282-
GasFeeCap: opts.GasFeeCap,
283-
GasTipCap: opts.GasTipCap,
284-
Gas: gasLimit,
285-
Value: value,
286-
Data: input,
287-
}
288-
if contract != nil {
289-
baseTx.To = &c.address
311+
return opts.Nonce.Uint64(), nil
312+
}
313+
}
314+
315+
// transact executes an actual transaction invocation, first deriving any missing
316+
// authorization fields, and then scheduling the transaction for execution.
317+
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
318+
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
319+
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
320+
}
321+
// Create the transaction
322+
var (
323+
rawTx *types.Transaction
324+
err error
325+
)
326+
if opts.GasPrice != nil {
327+
rawTx, err = c.createLegacyTx(opts, contract, input)
328+
} else {
329+
// Only query for basefee if gasPrice not specified
330+
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); err != nil {
331+
return nil, errHead
332+
} else if head.BaseFee != nil {
333+
rawTx, err = c.createDynamicTx(opts, contract, input, head)
334+
} else {
335+
// Chain is not London ready -> use legacy transaction
336+
rawTx, err = c.createLegacyTx(opts, contract, input)
290337
}
291-
rawTx = types.NewTx(baseTx)
292338
}
339+
if err != nil {
340+
return nil, err
341+
}
342+
// Sign the transaction and schedule it for execution
293343
if opts.Signer == nil {
294344
return nil, errors.New("no signer to authorize the transaction with")
295345
}

0 commit comments

Comments
 (0)