Skip to content

Commit

Permalink
Merge pull request #5 from KyberNetwork/feat/estimate-gas-bundle
Browse files Browse the repository at this point in the history
feat: estimate gas bundle
  • Loading branch information
tiennampham23 authored Mar 11, 2024
2 parents 28a95cf + 9e132c6 commit 0670753
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 4 deletions.
85 changes: 85 additions & 0 deletions eth/api_simulation.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import (
"sync/atomic"
"time"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/gasestimator"
"github.com/ethereum/go-ethereum/internal/ethapi"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/rpc"
Expand All @@ -30,6 +35,8 @@ import (
"github.com/ethereum/go-ethereum/log"
)

const estimateGasErrorRatio = 0.015

var (
enableDevnet = os.Getenv("ENABLE_DEVNET")
)
Expand All @@ -51,9 +58,34 @@ type CallBundleArgs struct {
BaseFee *big.Int `json:"baseFee"`
}

type EstimateGasBundleArgs struct {
Transactions []ethapi.TransactionArgs `json:"transactions"`
}

type revertError struct {
error
reason string // revert reason hex encoded
}

// This function is copied from
// https://github.com/KyberNetwork/geth/blob/6b0de79935110fb5f63a60288191848dd98980ea/internal/ethapi/errors.go#L46
func newRevertError(revert []byte) *revertError {
err := vm.ErrExecutionReverted

reason, errUnpack := abi.UnpackRevert(revert)
if errUnpack == nil {
err = fmt.Errorf("%w: %v", vm.ErrExecutionReverted, reason)
}
return &revertError{
error: err,
reason: hexutil.Encode(revert),
}
}

type Backend interface {
BlockChain() *core.BlockChain
TxPool() *txpool.TxPool
Config() *ethconfig.Config
}

var (
Expand Down Expand Up @@ -474,3 +506,56 @@ func (b *SimulationAPIBackend) CallBundle(ctx context.Context, args CallBundleAr
ret["bundleHash"] = "0x" + common.Bytes2Hex(bundleHash.Sum(nil))
return ret, nil
}

func (b *SimulationAPIBackend) EstimateGasBundle(ctx context.Context, args EstimateGasBundleArgs, overrides *ethapi.StateOverride) ([]uint64, error) {
txs := args.Transactions

if b.stateDb == nil {
return nil, fmt.Errorf("statedb is empty")
}

if b.currentBlock == nil {
return nil, fmt.Errorf("current block is empty")
}

var (
stateDB = b.stateDb.Copy()
parent = b.currentBlock.Header()
chainConfig = b.eth.BlockChain().Config()
gasCap = b.eth.Config().RPCGasCap

opts = &gasestimator.Options{
Config: chainConfig,
Chain: b.eth.BlockChain(),
Header: parent,
State: stateDB,
IsNotCopyStateDB: true,
ErrorRatio: estimateGasErrorRatio,
}
)
if err := overrides.Apply(stateDB); err != nil {
log.Error("Failed to apply overrides to state db", "err", err)
return nil, err
}

var gasEstimatedBundles []uint64

for _, tx := range txs {
// Run the gas estimation andwrap any revertals into a custom return
call, err := tx.ToMessage(gasCap, parent.BaseFee)
if err != nil {
return nil, err
}
estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap)
if err != nil {
if len(revert) > 0 {
return nil, newRevertError(revert)
}
return nil, err
}

gasEstimatedBundles = append(gasEstimatedBundles, estimate)
}

return gasEstimatedBundles, nil
}
3 changes: 3 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,9 @@ func (s *Ethereum) SyncMode() downloader.SyncMode {
mode, _ := s.handler.chainSync.modeAndLocalHead()
return mode
}
func (s *Ethereum) Config() *ethconfig.Config {
return s.config
}

// Protocols returns all the currently configured
// network protocols to start.
Expand Down
16 changes: 12 additions & 4 deletions eth/gasestimator/gasestimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ type Options struct {
Chain core.ChainContext // Chain context to access past block hashes
Header *types.Header // Header defining the block context to execute in
State *state.StateDB // Pre-state on top of which to estimate the gas

ErrorRatio float64 // Allowed overestimation ratio for faster estimation termination
// This flag whether determines we should make the copy of the opts.State or not.
// In the case of bundling transactions, we don't need to make a new copy of opts.State
IsNotCopyStateDB bool
ErrorRatio float64 // Allowed overestimation ratio for faster estimation termination
}

// Estimate returns the lowest possible gas limit that allows the transaction to
Expand Down Expand Up @@ -206,13 +208,19 @@ func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit ui
// call invocation.
func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) {
// Assemble the call and the call context
var dirtyState *state.StateDB
if opts.IsNotCopyStateDB {
dirtyState = opts.State
} else {
dirtyState = opts.State.Copy()
}
var (
msgContext = core.NewEVMTxContext(call)
evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil)

dirtyState = opts.State.Copy()
evm = vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})
evm = vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})
)

// Monitor the outer context and interrupt the EVM upon cancellation. To avoid
// a dangling goroutine until the outer estimation finishes, create an internal
// context for the lifetime of this method call.
Expand Down

0 comments on commit 0670753

Please sign in to comment.