From 93ef61bfad8fb1bb633a7ffa20ca0b6da22f524f Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Wed, 7 Feb 2024 09:06:24 +0000 Subject: [PATCH] Add method to include Bundles --- suave/builder/api/api.go | 11 ++++ suave/builder/api/api_client.go | 4 ++ suave/builder/api/api_server.go | 9 +++ suave/builder/api/api_test.go | 4 ++ suave/builder/builder.go | 96 ++++++++++++++++++++++++++++++++ suave/builder/session_manager.go | 9 +++ 6 files changed, 133 insertions(+) diff --git a/suave/builder/api/api.go b/suave/builder/api/api.go index b11d237ba2..f144e65f18 100644 --- a/suave/builder/api/api.go +++ b/suave/builder/api/api.go @@ -2,11 +2,22 @@ package api import ( "context" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) +type Bundle struct { + BlockNumber *big.Int `json:"blockNumber,omitempty"` // if BlockNumber is set it must match DecryptionCondition! + MaxBlock *big.Int `json:"maxBlock,omitempty"` + Txs types.Transactions `json:"txs"` + RevertingHashes []common.Hash `json:"revertingHashes,omitempty"` + RefundPercent *int `json:"percent,omitempty"` +} + type API interface { NewSession(ctx context.Context) (string, error) AddTransaction(ctx context.Context, sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error) + AddBundle(ctx context.Context, sessionId string, bundle Bundle) error } diff --git a/suave/builder/api/api_client.go b/suave/builder/api/api_client.go index 3baefde531..e7ca8246b4 100644 --- a/suave/builder/api/api_client.go +++ b/suave/builder/api/api_client.go @@ -40,3 +40,7 @@ func (a *APIClient) AddTransaction(ctx context.Context, sessionId string, tx *ty err := a.rpc.CallContext(ctx, &receipt, "suavex_addTransaction", sessionId, tx) return receipt, err } + +func (a *APIClient) AddBundle(ctx context.Context, sessionId string, bundle Bundle) error { + return a.rpc.CallContext(ctx, nil, "suavex_addBundle", sessionId, bundle) +} diff --git a/suave/builder/api/api_server.go b/suave/builder/api/api_server.go index 4b8336ed06..e78f435e14 100644 --- a/suave/builder/api/api_server.go +++ b/suave/builder/api/api_server.go @@ -10,6 +10,7 @@ import ( type SessionManager interface { NewSession(context.Context) (string, error) AddTransaction(sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error) + AddBundle(sessionId string, bundle Bundle) error } func NewServer(s SessionManager) *Server { @@ -31,6 +32,10 @@ func (s *Server) AddTransaction(ctx context.Context, sessionId string, tx *types return s.sessionMngr.AddTransaction(sessionId, tx) } +func (s *Server) AddBundle(ctx context.Context, sessionId string, bundle Bundle) error { + return s.sessionMngr.AddBundle(sessionId, bundle) +} + type MockServer struct { } @@ -41,3 +46,7 @@ func (s *MockServer) NewSession(ctx context.Context) (string, error) { func (s *MockServer) AddTransaction(ctx context.Context, sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error) { return &types.SimulateTransactionResult{}, nil } + +func (s *MockServer) AddBundle(ctx context.Context, sessionId string, bundle Bundle) error { + return nil +} diff --git a/suave/builder/api/api_test.go b/suave/builder/api/api_test.go index f9d795033c..30a9dabb39 100644 --- a/suave/builder/api/api_test.go +++ b/suave/builder/api/api_test.go @@ -37,3 +37,7 @@ func (nullSessionManager) NewSession(ctx context.Context) (string, error) { func (nullSessionManager) AddTransaction(sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error) { return &types.SimulateTransactionResult{Logs: []*types.SimulatedLog{}}, nil } + +func (nullSessionManager) AddBundle(sessionId string, bundle Bundle) error { + return nil +} diff --git a/suave/builder/builder.go b/suave/builder/builder.go index 0d5c30ce9e..b471f7a949 100644 --- a/suave/builder/builder.go +++ b/suave/builder/builder.go @@ -1,12 +1,16 @@ package builder import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/suave/builder/api" ) type builder struct { @@ -16,6 +20,7 @@ type builder struct { state *state.StateDB gasPool *core.GasPool gasUsed *uint64 + signer types.Signer } type builderConfig struct { @@ -34,9 +39,100 @@ func newBuilder(config *builderConfig) *builder { state: config.preState.Copy(), gasPool: &gp, gasUsed: &gasUsed, + signer: types.MakeSigner(config.config, config.header.Number, config.header.Time), } } +func (b *builder) takeSnapshot() func() { + indx := len(b.txns) + snap := b.state.Snapshot() + + return func() { + b.txns = b.txns[:indx] + b.receipts = b.receipts[:indx] + b.state.RevertToSnapshot(snap) + } +} + +func (b *builder) AddBundle(bundle api.Bundle) error { + revertFn := b.takeSnapshot() + + // create ephemeral addr and private key for payment txn + ephemeralPrivKey, err := crypto.GenerateKey() + if err != nil { + return err + } + ephemeralAddr := crypto.PubkeyToAddress(ephemeralPrivKey.PublicKey) + + // Assume static 28000 gas transfers for both mev-share and proposer payments + refundTransferCost := new(big.Int).Mul(big.NewInt(28000), b.config.header.BaseFee) + + // apply bundle + profitPreBundle := b.state.GetBalance(b.config.header.Coinbase) + if err := b.AddTransactions(bundle.Txs); err != nil { + revertFn() + return err + } + + profitPostBundle := b.state.GetBalance(b.config.header.Coinbase) + + // calc & refund user if bundle has multiple txns and wants refund + if len(bundle.Txs) > 1 && bundle.RefundPercent != nil { + // Note: PoC logic, this could be gamed by not sending any eth to coinbase + refundPrct := *bundle.RefundPercent + if refundPrct == 0 { + // default refund + refundPrct = 10 + } + bundleProfit := new(big.Int).Sub(profitPostBundle, profitPreBundle) + refundAmt := new(big.Int).Div(bundleProfit, big.NewInt(int64(refundPrct))) + // subtract payment txn transfer costs + refundAmt = new(big.Int).Sub(refundAmt, refundTransferCost) + + currNonce := b.state.GetNonce(ephemeralAddr) + + // HACK to include payment txn + // multi refund block untested + userTx := bundle.Txs[0] // NOTE : assumes first txn is refund recipient + refundAddr, err := types.Sender(types.LatestSignerForChainID(userTx.ChainId()), userTx) + if err != nil { + return err + } + paymentTx, err := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: currNonce, + To: &refundAddr, + Value: refundAmt, + Gas: 28000, + GasPrice: b.config.header.BaseFee, + }), b.signer, ephemeralPrivKey) + + if err != nil { + return err + } + + // commit payment txn + if _, err := b.AddTransaction(paymentTx); err != nil { + revertFn() + return err + } + } + + return nil +} + +func (b *builder) AddTransactions(txns types.Transactions) error { + revertFn := b.takeSnapshot() + + for _, txn := range txns { + if _, err := b.AddTransaction(txn); err != nil { + revertFn() + return err + } + } + + return nil +} + func (b *builder) AddTransaction(txn *types.Transaction) (*types.SimulateTransactionResult, error) { dummyAuthor := common.Address{} diff --git a/suave/builder/session_manager.go b/suave/builder/session_manager.go index 905241a5bd..2a2ddda062 100644 --- a/suave/builder/session_manager.go +++ b/suave/builder/session_manager.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/suave/builder/api" "github.com/google/uuid" ) @@ -162,6 +163,14 @@ func (s *SessionManager) AddTransaction(sessionId string, tx *types.Transaction) return builder.AddTransaction(tx) } +func (s *SessionManager) AddBundle(sessionId string, bundle api.Bundle) error { + builder, err := s.getSession(sessionId) + if err != nil { + return err + } + return builder.AddBundle(bundle) +} + // CalcBaseFee calculates the basefee of the header. func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { // If the current block is the first EIP-1559 block, return the InitialBaseFee.