Add method to include Bundles

This commit is contained in:
Ferran Borreguero 2024-02-07 09:06:24 +00:00
parent 9b2bee565d
commit 93ef61bfad
6 changed files with 133 additions and 0 deletions

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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{}

View file

@ -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.