@@ -188,108 +188,158 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
188
188
return c .transact (opts , & c .address , nil )
189
189
}
190
190
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
197
193
value := opts .Value
198
194
if value == nil {
199
195
value = new (big.Int )
200
196
}
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 ))
204
201
if err != nil {
205
- return nil , fmt . Errorf ( "failed to retrieve account nonce: %v" , err )
202
+ return nil , err
206
203
}
207
- } else {
208
- nonce = opts .Nonce .Uint64 ()
204
+ gasTipCap = tip
209
205
}
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
+ )
213
213
}
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 )
215
228
if err != nil {
216
229
return nil , err
217
230
}
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
246
258
}
259
+ gasPrice = price
247
260
}
261
+ // Estimate GasLimit
248
262
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 )
261
266
if err != nil {
262
- return nil , fmt . Errorf ( "failed to estimate gas needed: %v" , err )
267
+ return nil , err
263
268
}
264
269
}
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
277
293
}
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 )
279
310
} 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 )
290
337
}
291
- rawTx = types .NewTx (baseTx )
292
338
}
339
+ if err != nil {
340
+ return nil , err
341
+ }
342
+ // Sign the transaction and schedule it for execution
293
343
if opts .Signer == nil {
294
344
return nil , errors .New ("no signer to authorize the transaction with" )
295
345
}
0 commit comments