@@ -17,6 +17,7 @@ import (
17
17
"github.com/harmony-one/harmony/core/types"
18
18
"github.com/harmony-one/harmony/core/vm"
19
19
internal_common "github.com/harmony-one/harmony/internal/common"
20
+ "github.com/harmony-one/harmony/internal/params"
20
21
"github.com/harmony-one/harmony/internal/utils"
21
22
)
22
23
@@ -269,3 +270,66 @@ func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformati
269
270
header , _ := s .b .HeaderByNumber (context .Background (), rpc .LatestBlockNumber ) // latest header should always be available
270
271
return newHeaderInformation (header )
271
272
}
273
+
274
+ // doEstimateGas ..
275
+ func doEstimateGas (ctx context.Context , b Backend , args CallArgs , gasCap * big.Int ) (hexutil.Uint64 , error ) {
276
+ // Binary search the gas requirement, as it may be higher than the amount used
277
+ var (
278
+ lo uint64 = params .TxGas - 1
279
+ hi uint64
280
+ cap uint64
281
+ )
282
+ blockNum := rpc .LatestBlockNumber
283
+ if args .Gas != nil && uint64 (* args .Gas ) >= params .TxGas {
284
+ hi = uint64 (* args .Gas )
285
+ } else {
286
+ // Retrieve the block to act as the gas ceiling
287
+ block , err := b .BlockByNumber (ctx , blockNum )
288
+ if err != nil {
289
+ return 0 , err
290
+ }
291
+ hi = block .GasLimit ()
292
+ }
293
+ if gasCap != nil && hi > gasCap .Uint64 () {
294
+ // log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
295
+ hi = gasCap .Uint64 ()
296
+ }
297
+ cap = hi
298
+
299
+ // Use zero-address if none other is available
300
+ if args .From == nil {
301
+ args .From = & common.Address {}
302
+ }
303
+ // Create a helper to check if a gas allowance results in an executable transaction
304
+ executable := func (gas uint64 ) bool {
305
+ args .Gas = (* hexutil .Uint64 )(& gas )
306
+
307
+ _ , _ , failed , err := doCall (ctx , b , args , blockNum , vm.Config {}, 0 , big .NewInt (int64 (cap )))
308
+ if err != nil || failed {
309
+ return false
310
+ }
311
+ return true
312
+ }
313
+ // Execute the binary search and hone in on an executable gas limit
314
+ for lo + 1 < hi {
315
+ mid := (hi + lo ) / 2
316
+ if ! executable (mid ) {
317
+ lo = mid
318
+ } else {
319
+ hi = mid
320
+ }
321
+ }
322
+ // Reject the transaction as invalid if it still fails at the highest allowance
323
+ if hi == cap {
324
+ if ! executable (hi ) {
325
+ return 0 , fmt .Errorf ("gas required exceeds allowance (%d) or always failing transaction" , cap )
326
+ }
327
+ }
328
+ return hexutil .Uint64 (hi ), nil
329
+ }
330
+
331
+ // EstimateGas returns an estimate of the amount of gas needed to execute the
332
+ // given transaction against the current pending block.
333
+ func (s * PublicBlockChainAPI ) EstimateGas (ctx context.Context , args CallArgs ) (hexutil.Uint64 , error ) {
334
+ return doEstimateGas (ctx , s .b , args , nil )
335
+ }
0 commit comments