Skip to content

Commit

Permalink
Refactor transaction builder (Layr-Labs#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-shim authored Dec 1, 2023
1 parent f3c16d1 commit 588e0b2
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 12 deletions.
1 change: 1 addition & 0 deletions common/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ type EthClient interface {
TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error)
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
EstimateGasPriceAndLimitAndSendTx(ctx context.Context, tx *types.Transaction, tag string, value *big.Int) (*types.Receipt, error)
UpdateGas(ctx context.Context, tx *types.Transaction, value *big.Int) (*types.Transaction, error)
EnsureTransactionEvaled(ctx context.Context, tx *types.Transaction, tag string) (*types.Receipt, error)
}
46 changes: 34 additions & 12 deletions common/geth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,18 @@ func (c *EthClient) GetNoSendTransactOpts() *bind.TransactOpts {
return c.NoSendTransactOpts
}

// EstimateGasPriceAndLimitAndSendTx sends and returns an otherwise identical txn
// to the one provided but with updated gas prices sampled from the existing network
// conditions and an accurate gasLimit
// UpdateGas returns an otherwise identical txn to the one provided but with updated
// gas prices sampled from the existing network conditions and an accurate gasLimit.
// Note that this method does not publish the transaction.
//
// Note: tx must be a to a contract, not an EOA
//
// Slightly modified from: https://github.com/ethereum-optimism/optimism/blob/ec266098641820c50c39c31048aa4e953bece464/batch-submitter/drivers/sequencer/driver.go#L314
func (c *EthClient) EstimateGasPriceAndLimitAndSendTx(
func (c *EthClient) UpdateGas(
ctx context.Context,
tx *types.Transaction,
tag string,
value *big.Int,
) (*types.Receipt, error) {
) (*types.Transaction, error) {
gasTipCap, err := c.SuggestGasTipCap(ctx)
if err != nil {
// If the transaction failed because the backend does not support
Expand All @@ -135,7 +134,7 @@ func (c *EthClient) EstimateGasPriceAndLimitAndSendTx(
if err != nil {
return nil, err
}
gasFeeCap := new(big.Int).Add(header.BaseFee, gasTipCap)
gasFeeCap := getGasFeeCap(gasTipCap, header.BaseFee)

// The estimated gas limits performed by RawTransact fail semi-regularly
// with out of gas exceptions. To remedy this we extract the internal calls
Expand All @@ -154,10 +153,8 @@ func (c *EthClient) EstimateGasPriceAndLimitAndSendTx(
return nil, err
}

opts, err := bind.NewKeyedTransactorWithChainID(c.privateKey, tx.ChainId())
if err != nil {
return nil, fmt.Errorf("EstimateGasPriceAndLimitAndSendTx: cannot create transactOpts: %w", err)
}
opts := new(bind.TransactOpts)
*opts = *c.NoSendTransactOpts
opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
opts.GasTipCap = gasTipCap
Expand All @@ -174,7 +171,25 @@ func (c *EthClient) EstimateGasPriceAndLimitAndSendTx(
c.Contracts[*tx.To()] = contract
}

tx, err = contract.RawTransact(opts, tx.Data())
return contract.RawTransact(opts, tx.Data())
}

// EstimateGasPriceAndLimitAndSendTx sends and returns an otherwise identical txn
// to the one provided but with updated gas prices sampled from the existing network
// conditions and an accurate gasLimit
//
// Note: tx must be a to a contract, not an EOA
func (c *EthClient) EstimateGasPriceAndLimitAndSendTx(
ctx context.Context,
tx *types.Transaction,
tag string,
value *big.Int,
) (*types.Receipt, error) {
tx, err := c.UpdateGas(ctx, tx, value)
if err != nil {
return nil, fmt.Errorf("EstimateGasPriceAndLimitAndSendTx: failed to update gas for txn (%s): %w", tag, err)
}
err = c.SendTransaction(ctx, tx)
if err != nil {
return nil, fmt.Errorf("EstimateGasPriceAndLimitAndSendTx: failed to send txn (%s): %w", tag, err)
}
Expand Down Expand Up @@ -204,6 +219,13 @@ func (c *EthClient) EnsureTransactionEvaled(ctx context.Context, tx *types.Trans
return receipt, nil
}

// getGasFeeCap returns the gas fee cap for a transaction, calculated as:
// gasFeeCap = 2 * baseFee + gasTipCap
// Rationale: https://www.blocknative.com/blog/eip-1559-fees
func getGasFeeCap(gasTipCap *big.Int, baseFee *big.Int) *big.Int {
return new(big.Int).Add(new(big.Int).Mul(baseFee, big.NewInt(2)), gasTipCap)
}

func addGasBuffer(gasLimit uint64) uint64 {
return 6 * gasLimit / 5 // add 20% buffer to gas limit
}
6 changes: 6 additions & 0 deletions common/mock/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ func (mock *MockEthClient) TransactionReceipt(ctx context.Context, txHash common
return result, args.Error(1)
}

func (mock *MockEthClient) UpdateGas(ctx context.Context, tx *types.Transaction, value *big.Int) (*types.Transaction, error) {
args := mock.Called()
result := args.Get(0)
return result.(*types.Transaction), args.Error(1)
}

func (mock *MockEthClient) EstimateGasPriceAndLimitAndSendTx(ctx context.Context, tx *types.Transaction, tag string, value *big.Int) (*types.Receipt, error) {
args := mock.Called()
var result *types.Receipt
Expand Down

0 comments on commit 588e0b2

Please sign in to comment.