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 bundle gas #35

Merged
merged 5 commits into from
Apr 9, 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
23 changes: 2 additions & 21 deletions pkg/eth/simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"math/big"

"github.com/KyberNetwork/tradinglib/pkg/mev"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
Expand All @@ -28,33 +29,13 @@ func (s *Simulator) EstimateGasWithOverrides(
) (uint64, error) {
var hex hexutil.Uint64
err := s.c.CallContext(
ctx, &hex, "eth_estimateGas", toCallArg(msg),
ctx, &hex, "eth_estimateGas", mev.ToCallArg(msg),
toBlockNumArg(blockNumber), overrides,
)

return uint64(hex), err
}

func toCallArg(msg ethereum.CallMsg) interface{} {
arg := map[string]interface{}{
"from": msg.From,
"to": msg.To,
}
if len(msg.Data) > 0 {
arg["input"] = hexutil.Bytes(msg.Data)
}
if msg.Value != nil {
arg["value"] = (*hexutil.Big)(msg.Value)
}
if msg.Gas != 0 {
arg["gas"] = hexutil.Uint64(msg.Gas)
}
if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
}
return arg
}

func toBlockNumArg(number *big.Int) string {
if number == nil {
return "latest"
Expand Down
10 changes: 10 additions & 0 deletions pkg/mev/blxr_bundle_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (
"net/http"
"strconv"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/flashbots/mev-share-node/mevshare"
)

Expand All @@ -32,6 +34,14 @@ type BloxrouteClient struct {
enabledBuilders []BlxrBuilder
}

func (s *BloxrouteClient) EstimateBundleGas(
_ context.Context,
_ []ethereum.CallMsg,
_ *map[common.Address]gethclient.OverrideAccount,
) ([]uint64, error) {
return nil, nil
}

func (s *BloxrouteClient) MevSimulateBundle(
_ uint64,
_ common.Hash,
Expand Down
19 changes: 16 additions & 3 deletions pkg/mev/bundle_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import (
"net/http"

"github.com/duoxehyon/mev-share-go/rpc"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/flashbots/mev-share-node/mevshare"
)

Expand All @@ -26,7 +28,8 @@ type Client struct {
cancelBySendBundle bool
senderType BundleSenderType
// mevShareClient is the client for mev-share flashbots node
mevShareClient rpc.MevAPIClient
mevShareClient rpc.MevAPIClient
gasBundleEstimator IGasBundleEstimator
}

// NewClient set the flashbotKey to nil will skip adding the signature header.
Expand All @@ -36,7 +39,8 @@ func NewClient(
flashbotKey *ecdsa.PrivateKey,
cancelBySendBundle bool,
senderType BundleSenderType,
) *Client {
gasBundleEstimator IGasBundleEstimator,
) (*Client, error) {
var mevShareClient rpc.MevAPIClient
if flashbotKey != nil {
mevShareClient = rpc.NewClient(endpoint, flashbotKey)
Expand All @@ -49,7 +53,8 @@ func NewClient(
cancelBySendBundle: cancelBySendBundle,
senderType: senderType,
mevShareClient: mevShareClient,
}
gasBundleEstimator: gasBundleEstimator,
}, nil
}

func (s *Client) GetSenderType() BundleSenderType {
Expand Down Expand Up @@ -124,6 +129,14 @@ func (s *Client) flashbotBackrunSendBundle(
return res, err
}

func (s *Client) EstimateBundleGas(
ctx context.Context,
messages []ethereum.CallMsg,
overrides *map[common.Address]gethclient.OverrideAccount,
) ([]uint64, error) {
return s.gasBundleEstimator.EstimateBundleGas(ctx, messages, overrides)
}

func (s *Client) MevSimulateBundle(
blockNumber uint64,
pendingTxHash common.Hash,
Expand Down
26 changes: 20 additions & 6 deletions pkg/mev/bundle_sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ func TestSendBundle(t *testing.T) {
t.Log("new tx", signedTx.Hash().String())

uuid := uuid.NewString()
sender := mev.NewClient(client, endpoint, privateKey, false, mev.BundleSenderTypeFlashbot)
ethClient, err = ethclient.Dial(endpoint)
require.NoError(t, err)
gasBundleEstimator := mev.NewGasBundleEstimator(ethClient.Client())
sender, err := mev.NewClient(client, endpoint, privateKey, false, mev.BundleSenderTypeFlashbot, gasBundleEstimator)
require.NoError(t, err)

resp, err := sender.SendBundle(ctx, &uuid, blockNumber+12, signedTx)
require.NoError(t, err) // sepolia: code: [-32000], message: [internal server error]
Expand Down Expand Up @@ -95,7 +99,12 @@ func TestCancelBeaver(t *testing.T) {
bundleUUID = uuid.New().String()
)

sender := mev.NewClient(client, endpoint, nil, true, mev.BundleSenderTypeBeaver)
ethClient, err := ethclient.Dial(endpoint)
require.NoError(t, err)
gasBundleEstimator := mev.NewGasBundleEstimator(ethClient.Client())

sender, err := mev.NewClient(client, endpoint, nil, true, mev.BundleSenderTypeBeaver, gasBundleEstimator)
require.NoError(t, err)

require.NoError(t, sender.CancelBundle(ctx, bundleUUID))
}
Expand Down Expand Up @@ -129,10 +138,15 @@ func Test_SimulateBundle(t *testing.T) {
txs = append(txs, &tx)
}

var (
simulationEndpoint = "http://localhost:8545"
client = mev.NewClient(http.DefaultClient, simulationEndpoint, nil, false, mev.BundleSenderTypeFlashbot)
)
simulationEndpoint := "http://localhost:8545"
ethClient, err := ethclient.Dial(simulationEndpoint)
require.NoError(t, err)
gasBundleEstimator := mev.NewGasBundleEstimator(ethClient.Client())

client, err := mev.NewClient(http.DefaultClient,
simulationEndpoint, nil, false,
mev.BundleSenderTypeFlashbot, gasBundleEstimator)
require.NoError(t, err)

simulationResponse, err := client.SimulateBundle(context.Background(), uint64(blockNumber), txs...)
require.NoError(t, err)
Expand Down
50 changes: 50 additions & 0 deletions pkg/mev/gas_bundle_estimator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package mev

import (
"context"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/rpc"
)

type GasBundleEstimator struct {
client *rpc.Client
}

func NewGasBundleEstimator(client *rpc.Client) GasBundleEstimator {
return GasBundleEstimator{
client: client,
}
}

func (g GasBundleEstimator) EstimateBundleGas(
_ context.Context,
messages []ethereum.CallMsg,
overrides *map[common.Address]gethclient.OverrideAccount,
) ([]uint64, error) {
bundles := make([]interface{}, 0, len(messages))
for _, msg := range messages {
bundles = append(bundles, ToCallArg(msg))
}

var gasEstimateCost []hexutil.Uint64

err := g.client.Call(
&gasEstimateCost, ETHEstimateGasBundleMethod,
map[string]interface{}{
"transactions": bundles,
}, "latest", overrides,
)
if err != nil {
return nil, err
}
result := make([]uint64, 0, len(gasEstimateCost))

for _, gasEstimate := range gasEstimateCost {
result = append(result, uint64(gasEstimate))
}
return result, nil
}
42 changes: 42 additions & 0 deletions pkg/mev/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import (
"io"
"net/http"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/flashbots/mev-share-node/mevshare"
)

Expand All @@ -33,6 +36,7 @@ const (
ETHSendBundleMethod = "eth_sendBundle"
EthCallBundleMethod = "eth_callBundle"
ETHCancelBundleMethod = "eth_cancelBundle"
ETHEstimateGasBundleMethod = "eth_estimateGasBundle"
MevSendBundleMethod = "mev_sendBundle"
MaxBlockFromTarget = 3
)
Expand All @@ -55,13 +59,26 @@ type IBundleSender interface {
ctx context.Context, bundleUUID string,
) error
SimulateBundle(ctx context.Context, blockNumber uint64, txs ...*types.Transaction) (SendBundleResponse, error)
EstimateBundleGas(
ctx context.Context,
messages []ethereum.CallMsg,
overrides *map[common.Address]gethclient.OverrideAccount,
) ([]uint64, error)
MevSimulateBundle(
blockNumber uint64,
pendingTxHash common.Hash,
tx *types.Transaction) (*mevshare.SimMevBundleResponse, error)
GetSenderType() BundleSenderType
}

type IGasBundleEstimator interface {
EstimateBundleGas(
ctx context.Context,
messages []ethereum.CallMsg,
overrides *map[common.Address]gethclient.OverrideAccount,
) ([]uint64, error)
}

var (
_ IBundleSender = &Client{}
_ IBundleSender = &BloxrouteClient{}
Expand Down Expand Up @@ -174,3 +191,28 @@ type TitanCancelBundleResponse struct {
Result int `json:"result,omitempty"`
Error SendBundleError `json:"error,omitempty"`
}

func ToCallArg(msg ethereum.CallMsg) interface{} {
arg := map[string]interface{}{
"from": msg.From,
"to": msg.To,
}
if len(msg.Data) > 0 {
arg["input"] = hexutil.Bytes(msg.Data)
}
if msg.Value != nil {
arg["value"] = (*hexutil.Big)(msg.Value)
}
if msg.Gas != 0 {
arg["gas"] = hexutil.Uint64(msg.Gas)
}
if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
}
return arg
}

func GetFrom(tx *types.Transaction) (common.Address, error) {
from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx)
return from, err
}
Loading