@@ -997,13 +997,66 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOr
997997}
998998
999999func DoEstimateGas (ctx context.Context , b Backend , args CallArgs , blockNrOrHash rpc.BlockNumberOrHash , gasCap * big.Int ) (hexutil.Uint64 , error ) {
1000- // Retrieve the block to act as the gas ceiling
1001- block , err := b .BlockByNumberOrHash (ctx , blockNrOrHash )
1002- if err != nil {
1003- return 0 , err
1000+ // Binary search the gas requirement, as it may be higher than the amount used
1001+ var (
1002+ lo uint64 = params .TxGas - 1
1003+ hi uint64
1004+ cap uint64
1005+ )
1006+ if args .Gas != nil && uint64 (* args .Gas ) >= params .TxGas {
1007+ hi = uint64 (* args .Gas )
1008+ } else {
1009+ // Retrieve the block to act as the gas ceiling
1010+ block , err := b .BlockByNumberOrHash (ctx , blockNrOrHash )
1011+ if err != nil {
1012+ return 0 , err
1013+ }
1014+ hi = block .GasLimit ()
1015+ }
1016+ if gasCap != nil && hi > gasCap .Uint64 () {
1017+ log .Warn ("Caller gas above allowance, capping" , "requested" , hi , "cap" , gasCap )
1018+ hi = gasCap .Uint64 ()
1019+ }
1020+ cap = hi
1021+
1022+ // Set sender address or use a default if none specified
1023+ if args .From == nil {
1024+ if wallets := b .AccountManager ().Wallets (); len (wallets ) > 0 {
1025+ if accounts := wallets [0 ].Accounts (); len (accounts ) > 0 {
1026+ args .From = & accounts [0 ].Address
1027+ }
1028+ }
1029+ }
1030+ // Use zero-address if none other is available
1031+ if args .From == nil {
1032+ args .From = & common.Address {}
1033+ }
1034+ // Create a helper to check if a gas allowance results in an executable transaction
1035+ executable := func (gas uint64 ) bool {
1036+ args .Gas = (* hexutil .Uint64 )(& gas )
1037+
1038+ _ , _ , failed , err := DoCall (ctx , b , args , blockNrOrHash , nil , vm.Config {}, 0 , gasCap )
1039+ if err != nil || failed {
1040+ return false
1041+ }
1042+ return true
1043+ }
1044+ // Execute the binary search and hone in on an executable gas limit
1045+ for lo + 1 < hi {
1046+ mid := (hi + lo ) / 2
1047+ if ! executable (mid ) {
1048+ lo = mid
1049+ } else {
1050+ hi = mid
1051+ }
1052+ }
1053+ // Reject the transaction as invalid if it still fails at the highest allowance
1054+ if hi == cap {
1055+ if ! executable (hi ) {
1056+ return 0 , fmt .Errorf ("gas required exceeds allowance (%d) or always failing transaction" , cap )
1057+ }
10041058 }
1005- // For now always return the gas limit
1006- return hexutil .Uint64 (block .GasLimit () - 1 ), nil
1059+ return hexutil .Uint64 (hi ), nil
10071060}
10081061
10091062// EstimateGas returns an estimate of the amount of gas needed to execute the
0 commit comments