Skip to content
This repository has been archived by the owner on Apr 15, 2024. It is now read-only.

feat: support eip1559 when speeding up transactions #644

Merged
merged 2 commits into from
Dec 5, 2023
Merged
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
114 changes: 103 additions & 11 deletions relayer/relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strconv"
"time"

"github.com/ethereum/go-ethereum/params"

"github.com/ethereum/go-ethereum/ethclient"

"github.com/ipfs/go-datastore"
Expand Down Expand Up @@ -408,19 +410,52 @@ func (r *Relayer) waitForTransactionAndRetryIfNeeded(ctx context.Context, ethCli
_, err := r.EVMClient.WaitForTransaction(ctx, ethClient, newTx, r.RetryTimeout)
if err != nil {
if stderrors.Is(err, context.DeadlineExceeded) {
newGasPrice, err := ethClient.SuggestGasPrice(ctx)
if err != nil {
return err
}
if newGasPrice.Uint64() <= newTx.GasPrice().Uint64() {
// no need to resend the transaction if the suggested gas price is lower than the original one
continue
var rawTx *coregethtypes.Transaction
if tx.GasPrice() != nil {
rawTx, err = createSpeededUpLegacyTransaction(ctx, ethClient, newTx)
if err != nil {
return err
}
if rawTx.GasPrice().Cmp(newTx.GasPrice()) <= 0 {
// no need to resend the transaction if the suggested gas price is lower than the original one
continue
}
} else if tx.GasTipCap() != nil && tx.GasFeeCap() != nil {
rawTx, err = createSpeededUpDynamicTransaction(ctx, ethClient, newTx)
if err != nil {
return err
}
if rawTx.GasFeeCap().Cmp(newTx.GasFeeCap()) <= 0 {
// no need to resend the transaction if the suggested gas price is lower than the original one
continue
}
} else {
// Only query for basefee if gasPrice not specified
if head, errHead := ethClient.HeaderByNumber(ctx, nil); errHead != nil {
return errHead
} else if head.BaseFee != nil {
rawTx, err = createSpeededUpDynamicTransaction(ctx, ethClient, newTx)
if err != nil {
return err
}
if rawTx.GasFeeCap().Cmp(newTx.GasFeeCap()) <= 0 {
// no need to resend the transaction if the suggested gas price is lower than the original one
continue
}
} else {
// Chain is not London ready -> use legacy transaction
rawTx, err = createSpeededUpLegacyTransaction(ctx, ethClient, newTx)
if err != nil {
return err
}
if rawTx.GasPrice().Cmp(newTx.GasPrice()) <= 0 {
// no need to resend the transaction if the suggested gas price is lower than the original one
continue
}
}
}
Comment on lines +413 to 456
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think its necessarily worth changing, but it might be a good exercise to try and refactor this. Submitting a transaction is always really frustrating and some elaborate logic is unavoidable, I also haven't tried to so it might not be possible. However, my gut instinct is if there are this many if, if else, else, and continue statements there usually a way to rewrite it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea, but I think that's the best we could do because we have multiple chain types so we need to speed up the transaction according to its type. This implementation is inspired from the geth implementation.

legacyTx := toLegacyTransaction(newTx)
legacyTx.GasPrice = newGasPrice
newTx = coregethtypes.NewTx(legacyTx)
r.logger.Debug("transaction still not included. updating the gas price", "retry_number", i)
err = ethClient.SendTransaction(ctx, newTx)
err = ethClient.SendTransaction(ctx, rawTx)
r.logger.Info("submitted speed up transaction", "hash", newTx.Hash().Hex(), "new_gas_price", newTx.GasPrice().Uint64())
if err != nil {
r.logger.Debug("response of sending speed up transaction", "resp", err.Error())
rach-id marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -435,6 +470,45 @@ func (r *Relayer) waitForTransactionAndRetryIfNeeded(ctx context.Context, ethCli
return ErrTransactionStillPending
}

// createSpeededUpDynamicTransaction update the EIP1559 dynamic transaction with the current gas price.
func createSpeededUpDynamicTransaction(ctx context.Context, ethClient *ethclient.Client, newTx *coregethtypes.Transaction) (*coregethtypes.Transaction, error) {
// Estimate TipCap
gasTipCap, err := ethClient.SuggestGasTipCap(ctx)
if err != nil {
return nil, err
}
lastKnownHeader, err := ethClient.HeaderByNumber(ctx, nil)
if err != nil {
return nil, err
}
// Estimate FeeCap
gasFeeCap := new(big.Int).Add(
gasTipCap,
// the DefaultElasticityMultiplier is used to define the wiggle room for the gas
// in EIP1559
new(big.Int).Mul(lastKnownHeader.BaseFee, big.NewInt(params.DefaultElasticityMultiplier)),
)
if gasFeeCap.Cmp(gasTipCap) < 0 {
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
}

dynamicTransaction := toDynamicTransaction(newTx)
dynamicTransaction.GasTipCap = gasTipCap
dynamicTransaction.GasFeeCap = gasFeeCap
return coregethtypes.NewTx(dynamicTransaction), nil
}

// createSpeededUpLegacyTransaction update the legacy transaction with the new gas price.
func createSpeededUpLegacyTransaction(ctx context.Context, ethClient *ethclient.Client, newTx *coregethtypes.Transaction) (tx *coregethtypes.Transaction, err error) {
newGasPrice, err := ethClient.SuggestGasPrice(ctx)
if err != nil {
return nil, err
}
legacyTx := toLegacyTransaction(newTx)
legacyTx.GasPrice = newGasPrice
return coregethtypes.NewTx(legacyTx), nil
}

func toLegacyTransaction(tx *coregethtypes.Transaction) *coregethtypes.LegacyTx {
v, r, s := tx.RawSignatureValues()
return &coregethtypes.LegacyTx{
Expand All @@ -450,6 +524,24 @@ func toLegacyTransaction(tx *coregethtypes.Transaction) *coregethtypes.LegacyTx
}
}

func toDynamicTransaction(tx *coregethtypes.Transaction) *coregethtypes.DynamicFeeTx {
v, r, s := tx.RawSignatureValues()
return &coregethtypes.DynamicFeeTx{
ChainID: tx.ChainId(),
Nonce: tx.Nonce(),
GasTipCap: tx.GasTipCap(),
GasFeeCap: tx.GasFeeCap(),
Gas: tx.Gas(),
To: tx.To(),
Value: tx.Value(),
Data: tx.Data(),
AccessList: tx.AccessList(),
V: v,
R: r,
S: s,
}
}

// matchAttestationConfirmSigs matches and sorts the confirm signatures with the valset
// members as expected by the Blobstream contract.
// Also, it leaves the non provided signatures as nil in the `sigs` slice:
Expand Down
Loading