@@ -42,6 +42,8 @@ type Options struct {
42
42
Chain core.ChainContext // Chain context to access past block hashes
43
43
Header * types.Header // Header defining the block context to execute in
44
44
State * state.StateDB // Pre-state on top of which to estimate the gas
45
+
46
+ ErrorRatio float64 // Allowed overestimation ratio for faster estimation termination
45
47
}
46
48
47
49
// Estimate returns the lowest possible gas limit that allows the transaction to
@@ -86,16 +88,28 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
86
88
if transfer == nil {
87
89
transfer = new (big.Int )
88
90
}
89
- log .Warn ("Gas estimation capped by limited funds" , "original" , hi , "balance" , balance ,
91
+ log .Debug ("Gas estimation capped by limited funds" , "original" , hi , "balance" , balance ,
90
92
"sent" , transfer , "maxFeePerGas" , feeCap , "fundable" , allowance )
91
93
hi = allowance .Uint64 ()
92
94
}
93
95
}
94
96
// Recap the highest gas allowance with specified gascap.
95
97
if gasCap != 0 && hi > gasCap {
96
- log .Warn ("Caller gas above allowance, capping" , "requested" , hi , "cap" , gasCap )
98
+ log .Debug ("Caller gas above allowance, capping" , "requested" , hi , "cap" , gasCap )
97
99
hi = gasCap
98
100
}
101
+ // If the transaction is a plain value transfer, short circuit estimation and
102
+ // directly try 21000. Returning 21000 without any execution is dangerous as
103
+ // some tx field combos might bump the price up even for plain transfers (e.g.
104
+ // unused access list items). Ever so slightly wasteful, but safer overall.
105
+ if len (call .Data ) == 0 {
106
+ if call .To != nil && opts .State .GetCodeSize (* call .To ) == 0 {
107
+ failed , _ , err := execute (ctx , call , opts , params .TxGas )
108
+ if ! failed && err == nil {
109
+ return params .TxGas , nil , nil
110
+ }
111
+ }
112
+ }
99
113
// We first execute the transaction at the highest allowable gas limit, since if this fails we
100
114
// can return error immediately.
101
115
failed , result , err := execute (ctx , call , opts , hi )
@@ -115,8 +129,35 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
115
129
// limit for these cases anyway.
116
130
lo = result .UsedGas - 1
117
131
132
+ // There's a fairly high chance for the transaction to execute successfully
133
+ // with gasLimit set to the first execution's usedGas + gasRefund. Explicitly
134
+ // check that gas amount and use as a limit for the binary search.
135
+ optimisticGasLimit := (result .UsedGas + result .RefundedGas + params .CallStipend ) * 64 / 63
136
+ if optimisticGasLimit < hi {
137
+ failed , _ , err = execute (ctx , call , opts , optimisticGasLimit )
138
+ if err != nil {
139
+ // This should not happen under normal conditions since if we make it this far the
140
+ // transaction had run without error at least once before.
141
+ log .Error ("Execution error in estimate gas" , "err" , err )
142
+ return 0 , nil , err
143
+ }
144
+ if failed {
145
+ lo = optimisticGasLimit
146
+ } else {
147
+ hi = optimisticGasLimit
148
+ }
149
+ }
118
150
// Binary search for the smallest gas limit that allows the tx to execute successfully.
119
151
for lo + 1 < hi {
152
+ if opts .ErrorRatio > 0 {
153
+ // It is a bit pointless to return a perfect estimation, as changing
154
+ // network conditions require the caller to bump it up anyway. Since
155
+ // wallets tend to use 20-25% bump, allowing a small approximation
156
+ // error is fine (as long as it's upwards).
157
+ if float64 (hi - lo )/ float64 (hi ) < opts .ErrorRatio {
158
+ break
159
+ }
160
+ }
120
161
mid := (hi + lo ) / 2
121
162
if mid > lo * 2 {
122
163
// Most txs don't need much higher gas limit than their gas used, and most txs don't
0 commit comments