Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: estimate gas bundle #5

Merged
merged 1 commit into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading