Skip to content
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
2 changes: 2 additions & 0 deletions pkg/mev/backrun_public_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/ecdsa"
"encoding/json"
"fmt"
"math/big"
"net/http"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -44,6 +45,7 @@ func (b BackrunPublicClient) SendBackrunBundle(
_ uint64,
pendingTxHashes []common.Hash,
_ []string,
_ *big.Int,
txs ...*types.Transaction,
) (SendBundleResponse, error) {
req := SendRequest{
Expand Down
2 changes: 1 addition & 1 deletion pkg/mev/backrun_public_sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestNewBackrunPublicClient(t *testing.T) {

pendingTXhash := common.HexToHash("0x79d48b1a25d7af0d815997d2ce3a127560080971c5ea98ca5a32424f604e09fb")
resp, err := senderClient.SendBackrunBundle(context.Background(), nil,
blockNumber, blockNumber, []common.Hash{pendingTXhash}, []string{}, tx)
blockNumber, blockNumber, []common.Hash{pendingTXhash}, []string{}, nil, tx)
t.Log("resp", resp)
t.Log("err", err)
}
5 changes: 5 additions & 0 deletions pkg/mev/bloxroute_backrunme_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"math/big"
"net/http"
"strings"

Expand Down Expand Up @@ -35,6 +36,8 @@ type backrunmeRequestParams struct {
Timestamp *uint64 `json:"timestamp,omitempty"`
MinTimestamp *uint64 `json:"min_timestamp,omitempty"`
MaxTimestamp *uint64 `json:"max_timestamp,omitempty"`
// CoinbaseProfit is the wei paid to the BackRunMe contract (a JSON number).
CoinbaseProfit *big.Int `json:"coinbase_profit,omitempty"`
}

type backrunmeResponse struct {
Expand Down Expand Up @@ -130,6 +133,7 @@ func (s *BloxrouteBackrunmeSender) SendBackrunBundle(
maxBlockNumber uint64,
pendingTxHashes []common.Hash,
targetBuilders []string,
coinbaseProfit *big.Int,
txs ...*types.Transaction,
) (SendBundleResponse, error) {
// Validate inputs
Expand Down Expand Up @@ -165,6 +169,7 @@ func (s *BloxrouteBackrunmeSender) SendBackrunBundle(
TransactionHash: pendingTxHashes[0].Hex(),
Transaction: transactions,
BlockNumber: fmt.Sprintf("0x%x", blockNumber),
CoinbaseProfit: coinbaseProfit,
}

// Build request
Expand Down
2 changes: 1 addition & 1 deletion pkg/mev/bloxroute_backrunme_sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestBloxrouteBackrunmeSender_SendBackrunBundle(t *testing.T) {

pendingTXhash := common.HexToHash("0x79d48b1a25d7af0d815997d2ce3a127560080971c5ea98ca5a32424f604e09fb")

resp, err := sender.SendBackrunBundle(context.Background(), nil, 1, 1, []common.Hash{pendingTXhash}, []string{}, tx)
resp, err := sender.SendBackrunBundle(context.Background(), nil, 1, 1, []common.Hash{pendingTXhash}, []string{}, nil, tx)
require.NoError(t, err)
t.Log("resp", resp.Result.BundleHash)
}
2 changes: 2 additions & 0 deletions pkg/mev/merkle_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/ecdsa"
"encoding/json"
"fmt"
"math/big"
"net/http"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -44,6 +45,7 @@ func (b MerkleClient) SendBackrunBundle(
_ uint64,
pendingTxHashes []common.Hash,
_ []string,
_ *big.Int,
txs ...*types.Transaction,
) (SendBundleResponse, error) {
req := SendRequest{
Expand Down
2 changes: 2 additions & 0 deletions pkg/mev/mevshare_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package mev
import (
"context"
"crypto/ecdsa"
"math/big"

"github.com/duoxehyon/mev-share-go/rpc"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -39,6 +40,7 @@ func (m FlashbotMevShareSender) SendBackrunBundle(
maxBlockNumber uint64,
pendingTxHashes []common.Hash,
targetBuilders []string,
_ *big.Int,
txs ...*types.Transaction,
) (SendBundleResponse, error) {
if m.client == nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/mev/mevshare_sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestSendBackrunBundle(t *testing.T) {

// Send bundle
res, err := rpcClient.SendBackrunBundle(context.Background(), nil,
blockNumber+1, blockNumber+1, []common.Hash{pendingTxHash}, nil, tx)
blockNumber+1, blockNumber+1, []common.Hash{pendingTxHash}, nil, nil, tx)
assert.Nil(t, err)

t.Log(res, "result")
Expand Down
4 changes: 4 additions & 0 deletions pkg/mev/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"strings"
"time"
Expand Down Expand Up @@ -91,6 +92,9 @@ type IBackrunSender interface {
maxBlockNumber uint64,
pendingTxHashes []common.Hash,
targetBuilders []string,
// coinbaseProfit is the wei amount paid to the BackRunMe contract, reported to
// bloxroute via submit_arb_only_bundle. nil for non-bloxroute senders (ignored).
coinbaseProfit *big.Int,
tx ...*types.Transaction,
) (SendBundleResponse, error)
// MevSimulateBundle only use for backrun simulate with pending tx hash
Expand Down
91 changes: 91 additions & 0 deletions pkg/types/bloxroute_backrun_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package types

import (
"errors"
"fmt"
"strconv"

"github.com/ethereum/go-ethereum/common"
)

var ErrInvalidBloxrouteBackrunConfig = errors.New("invalid bloxroute backrun config")

// BloxrouteBackrunConfig is the per-tx BackRunMe profit-split config from bloxroute's
// arbOnlyMEV stream. On the wire from bloxroute it arrives as a 5-element string array
// [contractSplit, targetRewardAddress, targetSplit, blxrRewardAddress, searcherSplit];
// mempool-explorer parses it once via ParseBloxrouteBackrunConfig and attaches the typed
// value to Message.BloxrouteBackrunConfig, so downstream consumers read it directly (nil
// when the tx is not a bloxroute backrun).
type BloxrouteBackrunConfig struct {
ContractSplit float64 `json:"contract_split"`
TargetRewardAddress string `json:"target_reward_address"`
TargetSplit float64 `json:"target_split"`
BlxrRewardAddress string `json:"blxr_reward_address"`
SearcherSplit float64 `json:"searcher_split"`
}

// ParseBloxrouteBackrunConfig parses the 5-element backrunConfig array from the arbOnlyMEV
// stream into a validated BloxrouteBackrunConfig. Element order is
// [contractSplit, targetRewardAddress, targetSplit, blxrRewardAddress, searcherSplit].
func ParseBloxrouteBackrunConfig(parts []string) (*BloxrouteBackrunConfig, error) {
if len(parts) != 5 {
return nil, fmt.Errorf("%w: expected 5 elements, got %d", ErrInvalidBloxrouteBackrunConfig, len(parts))
}

contractSplit, err := strconv.ParseFloat(parts[0], 64)
if err != nil {
return nil, fmt.Errorf("%w: contract split: %w", ErrInvalidBloxrouteBackrunConfig, err)
}
targetSplit, err := strconv.ParseFloat(parts[2], 64)
if err != nil {
return nil, fmt.Errorf("%w: target split: %w", ErrInvalidBloxrouteBackrunConfig, err)
}
searcherSplit, err := strconv.ParseFloat(parts[4], 64)
if err != nil {
return nil, fmt.Errorf("%w: searcher split: %w", ErrInvalidBloxrouteBackrunConfig, err)
}

cfg := BloxrouteBackrunConfig{
ContractSplit: contractSplit,
TargetRewardAddress: parts[1],
TargetSplit: targetSplit,
BlxrRewardAddress: parts[3],
SearcherSplit: searcherSplit,
}
if err := cfg.Validate(); err != nil {
return nil, err
}

return &cfg, nil
}

// Validate checks the split percentages are non-negative, sum to <= 100, and the reward
// addresses are valid non-zero addresses.
func (c BloxrouteBackrunConfig) Validate() error {
if c.ContractSplit < 0 || c.TargetSplit < 0 || c.SearcherSplit < 0 {
return fmt.Errorf("%w: splits must be non-negative", ErrInvalidBloxrouteBackrunConfig)
}
if _, err := c.BlxrSplit(); err != nil {
return err
}
if !isValidNonZeroAddress(c.TargetRewardAddress) {
return fmt.Errorf("%w: invalid target reward address", ErrInvalidBloxrouteBackrunConfig)
}
if !isValidNonZeroAddress(c.BlxrRewardAddress) {
return fmt.Errorf("%w: invalid blxr reward address", ErrInvalidBloxrouteBackrunConfig)
}
return nil
}

// BlxrSplit returns bloxroute's reward share: 100 - contract - target - searcher.
func (c BloxrouteBackrunConfig) BlxrSplit() (float64, error) {
blxrSplit := 100 - c.ContractSplit - c.TargetSplit - c.SearcherSplit
if blxrSplit < 0 {
return 0, fmt.Errorf("%w: split sum exceeds 100", ErrInvalidBloxrouteBackrunConfig)
}
return blxrSplit, nil
}

func isValidNonZeroAddress(addr string) bool {
return common.IsHexAddress(addr) && common.HexToAddress(addr) != (common.Address{})
}
53 changes: 53 additions & 0 deletions pkg/types/bloxroute_backrun_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package types_test

import (
"errors"
"testing"

"github.com/KyberNetwork/tradinglib/pkg/types"
"github.com/stretchr/testify/require"
)

func TestParseBloxrouteBackrunConfig(t *testing.T) {
const (
targetAddress = "0x435D8cB64ba4B68f6A28405D3C19E7C169B01917"
blxrAddress = "0xF1Ce037Cc1d02c046e800C3265D2Efa91940864E"
)

cfg, err := types.ParseBloxrouteBackrunConfig([]string{"50", targetAddress, "15", blxrAddress, "20"})
require.NoError(t, err)
require.Equal(t, 50.0, cfg.ContractSplit)
require.Equal(t, targetAddress, cfg.TargetRewardAddress)
require.Equal(t, 15.0, cfg.TargetSplit)
require.Equal(t, blxrAddress, cfg.BlxrRewardAddress)
require.Equal(t, 20.0, cfg.SearcherSplit)

blxr, err := cfg.BlxrSplit()
require.NoError(t, err)
require.Equal(t, 15.0, blxr) // 100 - 50 - 15 - 20
}

func TestParseBloxrouteBackrunConfigErrors(t *testing.T) {
const (
targetAddress = "0x435D8cB64ba4B68f6A28405D3C19E7C169B01917"
blxrAddress = "0xF1Ce037Cc1d02c046e800C3265D2Efa91940864E"
)

tests := []struct {
name string
parts []string
}{
{"wrong length", []string{"50", targetAddress, "15", blxrAddress}},
{"non-numeric split", []string{"x", targetAddress, "15", blxrAddress, "20"}},
{"sum exceeds 100", []string{"60", targetAddress, "30", blxrAddress, "20"}},
{"zero target address", []string{"50", "0x0000000000000000000000000000000000000000", "15", blxrAddress, "20"}},
{"invalid blxr address", []string{"50", targetAddress, "15", "not-an-address", "20"}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := types.ParseBloxrouteBackrunConfig(tc.parts)
require.Error(t, err)
require.True(t, errors.Is(err, types.ErrInvalidBloxrouteBackrunConfig))
})
}
}
3 changes: 3 additions & 0 deletions pkg/types/pendingtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type Message struct {
ExtraData string `json:"extra_data"`
RawTx string `json:"raw_tx"`
BlockNumber uint64 `json:"block_number"` // valid in block
// BloxrouteBackrunConfig is the per-tx BackRunMe profit-split config, set only for
// bloxroute arbOnlyMEV-sourced txs (nil otherwise).
BloxrouteBackrunConfig *BloxrouteBackrunConfig `json:"bloxroute_backrun_config,omitempty"`
}

type Prestate struct {
Expand Down
Loading