@@ -49,6 +49,10 @@ const minFlowBalance = 2
4949const coaFundingBalance = minFlowBalance - 1
5050const blockGasLimit = 120_000_000
5151
52+ // estimateGasErrorRatio is the amount of overestimation eth_estimateGas
53+ // is allowed to produce in order to speed up calculations.
54+ const estimateGasErrorRatio = 0.015
55+
5256type Requester interface {
5357 // SendRawTransaction will submit signed transaction data to the network.
5458 // The submitted EVM transaction hash is returned.
@@ -62,7 +66,7 @@ type Requester interface {
6266 // Note, this function doesn't make and changes in the state/blockchain and is
6367 // useful to execute and retrieve values.
6468 Call (
65- tx * types.LegacyTx ,
69+ tx * types.DynamicFeeTx ,
6670 from common.Address ,
6771 height uint64 ,
6872 stateOverrides * ethTypes.StateOverride ,
@@ -72,7 +76,7 @@ type Requester interface {
7276 // Note, this function doesn't make any changes in the state/blockchain and is
7377 // useful to executed and retrieve the gas consumption and possible failures.
7478 EstimateGas (
75- tx * types.LegacyTx ,
79+ tx * types.DynamicFeeTx ,
7680 from common.Address ,
7781 height uint64 ,
7882 stateOverrides * ethTypes.StateOverride ,
@@ -348,7 +352,7 @@ func (e *EVM) GetStorageAt(
348352}
349353
350354func (e * EVM ) Call (
351- tx * types.LegacyTx ,
355+ tx * types.DynamicFeeTx ,
352356 from common.Address ,
353357 height uint64 ,
354358 stateOverrides * ethTypes.StateOverride ,
@@ -358,42 +362,109 @@ func (e *EVM) Call(
358362 return nil , err
359363 }
360364
361- return result .ReturnedData , err
365+ resultSummary := result .ResultSummary ()
366+ if resultSummary .ErrorCode != 0 {
367+ if resultSummary .ErrorCode == evmTypes .ExecutionErrCodeExecutionReverted {
368+ return nil , errs .NewRevertError (resultSummary .ReturnedData )
369+ }
370+ return nil , errs .NewFailedTransactionError (resultSummary .ErrorMessage )
371+ }
372+
373+ return result .ReturnedData , nil
362374}
363375
364376func (e * EVM ) EstimateGas (
365- tx * types.LegacyTx ,
377+ tx * types.DynamicFeeTx ,
366378 from common.Address ,
367379 height uint64 ,
368380 stateOverrides * ethTypes.StateOverride ,
369381) (uint64 , error ) {
382+ // Binary search the gas limit, as it may need to be higher than the amount used
383+ var (
384+ failingGasLimit uint64 // lowest-known gas limit where tx execution fails
385+ passingGasLimit uint64 // lowest-known gas limit where tx execution succeeds
386+ )
387+ // Determine the highest gas limit that can be used during the estimation.
388+ passingGasLimit = blockGasLimit
389+ if tx .Gas >= gethParams .TxGas {
390+ passingGasLimit = tx .Gas
391+ }
392+ tx .Gas = passingGasLimit
393+ // We first execute the transaction at the highest allowable gas limit,
394+ // since if this fails we can return error immediately.
370395 result , err := e .dryRunTx (tx , from , height , stateOverrides )
371396 if err != nil {
372397 return 0 , err
373398 }
399+ resultSummary := result .ResultSummary ()
400+ if resultSummary .ErrorCode != 0 {
401+ if resultSummary .ErrorCode == evmTypes .ExecutionErrCodeExecutionReverted {
402+ return 0 , errs .NewRevertError (resultSummary .ReturnedData )
403+ }
404+ }
374405
375- if result .Successful () {
376- // As mentioned in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md#specification
377- // Define "all but one 64th" of N as N - floor(N / 64).
378- // If a call asks for more gas than the maximum allowed amount
379- // (i.e. the total amount of gas remaining in the parent after subtracting
380- // the gas cost of the call and memory expansion), do not return an OOG error;
381- // instead, if a call asks for more gas than all but one 64th of the maximum
382- // allowed amount, call with all but one 64th of the maximum allowed amount of
383- // gas (this is equivalent to a version of EIP-901 plus EIP-1142).
384- // CREATE only provides all but one 64th of the parent gas to the child call.
385- result .GasConsumed = AddOne64th (result .GasConsumed )
406+ // For almost any transaction, the gas consumed by the unconstrained execution
407+ // above lower-bounds the gas limit required for it to succeed. One exception
408+ // is those that explicitly check gas remaining in order to execute within a
409+ // given limit, but we probably don't want to return the lowest possible gas
410+ // limit for these cases anyway.
411+ failingGasLimit = result .GasConsumed - 1
412+
413+ // There's a fairly high chance for the transaction to execute successfully
414+ // with gasLimit set to the first execution's GasConsumed + GasRefund.
415+ // Explicitly check that gas amount and use as a limit for the binary search.
416+ optimisticGasLimit := (result .GasConsumed + result .GasRefund + gethParams .CallStipend ) * 64 / 63
417+ if optimisticGasLimit < passingGasLimit {
418+ tx .Gas = optimisticGasLimit
419+ result , err = e .dryRunTx (tx , from , height , stateOverrides )
420+ if err != nil {
421+ // This should not happen under normal conditions since if we make it this far the
422+ // transaction had run without error at least once before.
423+ return 0 , err
424+ }
425+ resultSummary := result .ResultSummary ()
426+ if resultSummary .ErrorCode == evmTypes .ExecutionErrCodeOutOfGas {
427+ failingGasLimit = optimisticGasLimit
428+ } else {
429+ passingGasLimit = optimisticGasLimit
430+ }
431+ }
386432
387- // Adding `gethParams.SstoreSentryGasEIP2200` is needed for this condition:
388- // https://github.com/onflow/go-ethereum/blob/master/core/vm/operations_acl.go#L29-L32
389- result .GasConsumed += gethParams .SstoreSentryGasEIP2200
433+ // Binary search for the smallest gas limit that allows the tx to execute successfully.
434+ for failingGasLimit + 1 < passingGasLimit {
435+ // It is a bit pointless to return a perfect estimation, as changing
436+ // network conditions require the caller to bump it up anyway. Since
437+ // wallets tend to use 20-25% bump, allowing a small approximation
438+ // error is fine (as long as it's upwards).
439+ if float64 (passingGasLimit - failingGasLimit )/ float64 (passingGasLimit ) < estimateGasErrorRatio {
440+ break
441+ }
442+ mid := (passingGasLimit + failingGasLimit ) / 2
443+ if mid > failingGasLimit * 2 {
444+ // Most txs don't need much higher gas limit than their gas used, and most txs don't
445+ // require near the full block limit of gas, so the selection of where to bisect the
446+ // range here is skewed to favor the low side.
447+ mid = failingGasLimit * 2
448+ }
449+ tx .Gas = mid
450+ result , err = e .dryRunTx (tx , from , height , stateOverrides )
451+ if err != nil {
452+ return 0 , err
453+ }
454+ resultSummary := result .ResultSummary ()
455+ if resultSummary .ErrorCode == evmTypes .ExecutionErrCodeOutOfGas {
456+ failingGasLimit = mid
457+ } else {
458+ passingGasLimit = mid
459+ }
460+ }
390461
391- // Take into account any gas refunds, which are calculated only after
392- // transaction execution.
393- result . GasConsumed += result . GasRefund
462+ if tx . AccessList != nil {
463+ passingGasLimit += uint64 ( len ( tx . AccessList )) * gethParams . TxAccessListAddressGas
464+ passingGasLimit += uint64 ( tx . AccessList . StorageKeys ()) * gethParams . TxAccessListStorageKeyGas
394465 }
395466
396- return result . GasConsumed , err
467+ return passingGasLimit , nil
397468}
398469
399470func (e * EVM ) GetCode (
@@ -485,7 +556,7 @@ func (e *EVM) evmToCadenceHeight(height uint64) (uint64, error) {
485556}
486557
487558func (e * EVM ) dryRunTx (
488- tx * types.LegacyTx ,
559+ tx * types.DynamicFeeTx ,
489560 from common.Address ,
490561 height uint64 ,
491562 stateOverrides * ethTypes.StateOverride ,
@@ -545,14 +616,6 @@ func (e *EVM) dryRunTx(
545616 return nil , err
546617 }
547618
548- resultSummary := result .ResultSummary ()
549- if resultSummary .ErrorCode != 0 {
550- if resultSummary .ErrorCode == evmTypes .ExecutionErrCodeExecutionReverted {
551- return nil , errs .NewRevertError (resultSummary .ReturnedData )
552- }
553- return nil , errs .NewFailedTransactionError (resultSummary .ErrorMessage )
554- }
555-
556619 return result , nil
557620}
558621
0 commit comments