Skip to content

Commit

Permalink
[TT-1619] more robust builder and better logging (#1126)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tofel authored Sep 6, 2024
1 parent d6bb3c8 commit 5e0b15b
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 49 deletions.
5 changes: 2 additions & 3 deletions seth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ func (m *Client) TransferETHFromKey(ctx context.Context, fromKeyNum int, to stri

// WaitMined the same as bind.WaitMined, awaits transaction receipt until timeout
func (m *Client) WaitMined(ctx context.Context, l zerolog.Logger, b bind.DeployBackend, tx *types.Transaction) (*types.Receipt, error) {
l.Info().
Msg("Waiting for transaction to be mined")
queryTicker := time.NewTicker(time.Second)
defer queryTicker.Stop()
ctx, cancel := context.WithTimeout(ctx, m.Cfg.Network.TxnTimeout.Duration())
Expand All @@ -492,17 +494,14 @@ func (m *Client) WaitMined(ctx context.Context, l zerolog.Logger, b bind.DeployB
if err == nil {
l.Info().
Int64("BlockNumber", receipt.BlockNumber.Int64()).
Str("TX", tx.Hash().String()).
Msg("Transaction receipt found")
return receipt, nil
} else if errors.Is(err, ethereum.NotFound) {
l.Debug().
Str("Timeout", m.Cfg.Network.TxnTimeout.String()).
Str("TX", tx.Hash().String()).
Msg("Awaiting transaction")
} else {
l.Debug().
Str("TX", tx.Hash().String()).
Msgf("Failed to get receipt due to: %s", err)
}
select {
Expand Down
66 changes: 41 additions & 25 deletions seth/client_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,38 +47,41 @@ func NewClientBuilder() *ClientBuilder {
// WithRpcUrl sets the RPC URL for the config.
// Default value is an empty string (which is an incorrect value).
func (c *ClientBuilder) WithRpcUrl(url string) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.URLs = []string{url}
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].URLs = []string{url}
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.URLs = []string{url}
}
return c
}

// WithPrivateKeys sets the private keys for the config. At least one is required to build a valid config.
// Default value is an empty slice (which is an incorrect value).
func (c *ClientBuilder) WithPrivateKeys(pks []string) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.PrivateKeys = pks
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].PrivateKeys = pks
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.PrivateKeys = pks
}
return c
}

// WithNetworkName sets the network name, useful mostly for debugging and logging.
// Default value is "default".
func (c *ClientBuilder) WithNetworkName(name string) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.Name = name
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].Name = name
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.Name = name
}
return c
}
Expand All @@ -89,70 +92,75 @@ func (c *ClientBuilder) WithNetworkName(name string) *ClientBuilder {
// Following priorities are supported: "slow", "standard" and "fast"
// Default values are true for enabled, 200 blocks for estimation and "standard" for priority.
func (c *ClientBuilder) WithGasPriceEstimations(enabled bool, estimationBlocks uint64, txPriority string) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.GasPriceEstimationEnabled = enabled
c.config.Network.GasPriceEstimationBlocks = estimationBlocks
c.config.Network.GasPriceEstimationTxPriority = txPriority
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].GasPriceEstimationEnabled = enabled
c.config.Networks[0].GasPriceEstimationBlocks = estimationBlocks
c.config.Networks[0].GasPriceEstimationTxPriority = txPriority
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.GasPriceEstimationEnabled = enabled
net.GasPriceEstimationBlocks = estimationBlocks
net.GasPriceEstimationTxPriority = txPriority
}
return c
}

// WithEIP1559DynamicFees enables or disables EIP-1559 dynamic fees. If enabled, you should set gas fee cap and gas tip cap with `WithDynamicGasPrices()`
// Default value is true.
func (c *ClientBuilder) WithEIP1559DynamicFees(enabled bool) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.EIP1559DynamicFees = enabled
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].EIP1559DynamicFees = enabled
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.EIP1559DynamicFees = enabled
}
return c
}

// WithLegacyGasPrice sets the gas price for legacy transactions that will be used only if EIP-1559 dynamic fees are disabled.
// Default value is 1 gwei.
func (c *ClientBuilder) WithLegacyGasPrice(gasPrice int64) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.GasPrice = gasPrice
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].GasPrice = gasPrice
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.GasPrice = gasPrice
}
return c
}

// WithDynamicGasPrices sets the gas fee cap and gas tip cap for EIP-1559 dynamic fees. These values will be used only if EIP-1559 dynamic fees are enabled.
// Default values are 150 gwei for gas fee cap and 50 gwei for gas tip cap.
func (c *ClientBuilder) WithDynamicGasPrices(gasFeeCap, gasTipCap int64) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.GasFeeCap = gasFeeCap
c.config.Network.GasTipCap = gasTipCap
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].GasFeeCap = gasFeeCap
c.config.Networks[0].GasTipCap = gasTipCap
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.GasFeeCap = gasFeeCap
net.GasTipCap = gasTipCap
}
return c
}

// WithTransferGasFee sets the gas fee for transfer transactions. This value is used, when sending funds to ephemeral keys or returning funds to root private key.
// Default value is 21_000 wei.
func (c *ClientBuilder) WithTransferGasFee(gasFee int64) *ClientBuilder {
c.config.Network.TransferGasFee = gasFee
func (c *ClientBuilder) WithTransferGasFee(transferGasFee int64) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.TransferGasFee = transferGasFee
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].TransferGasFee = gasFee
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.TransferGasFee = transferGasFee
}
return c
}
Expand All @@ -172,25 +180,27 @@ func (c *ClientBuilder) WithGasBumping(retries uint, maxGasPrice int64, customBu
// WithTransactionTimeout sets the timeout for transactions. If the transaction is not mined within this time, it will be considered failed.
// Default value is 5 minutes.
func (c *ClientBuilder) WithTransactionTimeout(timeout time.Duration) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.TxnTimeout = MustMakeDuration(timeout)
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].TxnTimeout = MustMakeDuration(timeout)
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.TxnTimeout = MustMakeDuration(timeout)
}
return c
}

// WithRpcDialTimeout sets the timeout for dialing the RPC server. If the connection is not established within this time, it will be considered failed.
// Default value is 1 minute.
func (c *ClientBuilder) WithRpcDialTimeout(timeout time.Duration) *ClientBuilder {
c.assertNetworkIsSet()
c.config.Network.DialTimeout = MustMakeDuration(timeout)
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else {
c.config.Networks[0].DialTimeout = MustMakeDuration(timeout)
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.DialTimeout = MustMakeDuration(timeout)
}
return c
}
Expand Down Expand Up @@ -252,3 +262,9 @@ func (c *ClientBuilder) WithNonceManager(rateLimitSec int, retries uint, timeout
func (c *ClientBuilder) Build() (*Client, error) {
return NewClientWithConfig(c.config)
}

func (c *ClientBuilder) assertNetworkIsSet() {
if c.config.Network == nil {
panic("Network is required to use this method, but it was nil")
}
}
10 changes: 10 additions & 0 deletions seth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,13 @@ func (c *Config) hasOutput(output string) bool {

return false
}

func (c *Config) findNetworkByName(name string) *Network {
for _, n := range c.Networks {
if strings.EqualFold(n.Name, name) {
return n
}
}

return nil
}
76 changes: 55 additions & 21 deletions seth/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,34 +155,48 @@ var prepareReplacementTransaction = func(client *Client, tx *types.Transaction)

switch tx.Type() {
case types.LegacyTxType:
gasPrice := client.Cfg.GasBump.StrategyFn(tx.GasPrice())
if err := checkMaxPrice(gasPrice, maxGasPrice); err != nil {
newGasPrice := client.Cfg.GasBump.StrategyFn(tx.GasPrice())
if err := checkMaxPrice(newGasPrice, maxGasPrice); err != nil {
return nil, err
}
L.Debug().Interface("Old gas price", tx.GasPrice()).Interface("New gas price", gasPrice).Msg("Bumping gas price for legacy transaction")
gasPriceDiff := big.NewInt(0).Sub(newGasPrice, tx.GasPrice())
L.Debug().
Str("Old gas price", fmt.Sprintf("%s wei /%s ether", tx.GasPrice(), WeiToEther(tx.GasPrice()).Text('f', -1))).
Str("New gas price", fmt.Sprintf("%s wei /%s ether", newGasPrice, WeiToEther(newGasPrice).Text('f', -1))).
Str("Diff", fmt.Sprintf("%s wei /%s ether", gasPriceDiff, WeiToEther(gasPriceDiff).Text('f', -1))).
Msg("Bumping gas price for Legacy transaction")
txData := &types.LegacyTx{
Nonce: tx.Nonce(),
To: tx.To(),
Value: tx.Value(),
Gas: tx.Gas(),
GasPrice: gasPrice,
GasPrice: newGasPrice,
Data: tx.Data(),
}
replacementTx, err = types.SignNewTx(privateKey, signer, txData)
case types.DynamicFeeTxType:
gasFeeCap := client.Cfg.GasBump.StrategyFn(tx.GasFeeCap())
gasTipCap := client.Cfg.GasBump.StrategyFn(tx.GasTipCap())
if err := checkMaxPrice(big.NewInt(0).Add(gasFeeCap, gasTipCap), maxGasPrice); err != nil {
newGasFeeCap := client.Cfg.GasBump.StrategyFn(tx.GasFeeCap())
newGasTipCap := client.Cfg.GasBump.StrategyFn(tx.GasTipCap())
if err := checkMaxPrice(big.NewInt(0).Add(newGasFeeCap, newGasTipCap), maxGasPrice); err != nil {
return nil, err
}
L.Debug().Interface("Old gas fee cap", tx.GasFeeCap()).Interface("New gas fee cap", gasFeeCap).Interface("Old gas tip cap", tx.GasTipCap()).Interface("New gas tip cap", gasTipCap).Msg("Bumping gas fee cap and tip cap for EIP-1559 transaction")
gasFeeCapDiff := big.NewInt(0).Sub(newGasFeeCap, tx.GasFeeCap())
gasTipCapDiff := big.NewInt(0).Sub(newGasTipCap, tx.GasTipCap())
L.Debug().
Str("Old gas fee cap", fmt.Sprintf("%s wei /%s ether", tx.GasFeeCap(), WeiToEther(tx.GasFeeCap()).Text('f', -1))).
Str("New gas fee cap", fmt.Sprintf("%s wei /%s ether", newGasFeeCap, WeiToEther(newGasFeeCap).Text('f', -1))).
Str("Gas fee cap diff", fmt.Sprintf("%s wei /%s ether", gasFeeCapDiff, WeiToEther(gasFeeCapDiff).Text('f', -1))).
Str("Old gas tip cap", fmt.Sprintf("%s wei /%s ether", tx.GasTipCap(), WeiToEther(tx.GasTipCap()).Text('f', -1))).
Str("New gas tip cap", fmt.Sprintf("%s wei /%s ether", newGasTipCap, WeiToEther(newGasTipCap).Text('f', -1))).
Str("Gas fee tip diff", fmt.Sprintf("%s wei /%s ether", gasTipCapDiff, WeiToEther(gasTipCapDiff).Text('f', -1))).
Msg("Bumping gas fee cap and tip cap for EIP-1559 transaction")
txData := &types.DynamicFeeTx{
Nonce: tx.Nonce(),
To: tx.To(),
Value: tx.Value(),
Gas: tx.Gas(),
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
GasFeeCap: newGasFeeCap,
GasTipCap: newGasTipCap,
Data: tx.Data(),
}

Expand All @@ -191,33 +205,53 @@ var prepareReplacementTransaction = func(client *Client, tx *types.Transaction)
if tx.To() == nil {
return nil, fmt.Errorf("blob tx with nil recipient is not supported")
}
gasFeeCap := client.Cfg.GasBump.StrategyFn(tx.GasFeeCap())
gasTipCap := client.Cfg.GasBump.StrategyFn(tx.GasTipCap())
blobFeeCap := client.Cfg.GasBump.StrategyFn(tx.BlobGasFeeCap())
if err := checkMaxPrice(big.NewInt(0).Add(gasFeeCap, big.NewInt(0).Add(gasTipCap, blobFeeCap)), maxGasPrice); err != nil {
newGasFeeCap := client.Cfg.GasBump.StrategyFn(tx.GasFeeCap())
newGasTipCap := client.Cfg.GasBump.StrategyFn(tx.GasTipCap())
newBlobFeeCap := client.Cfg.GasBump.StrategyFn(tx.BlobGasFeeCap())
if err := checkMaxPrice(big.NewInt(0).Add(newGasFeeCap, big.NewInt(0).Add(newGasTipCap, newBlobFeeCap)), maxGasPrice); err != nil {
return nil, err
}

L.Debug().Interface("Old gas fee cap", tx.GasFeeCap()).Interface("Old max fee per blob", tx.BlobGasFeeCap()).Interface("New max fee per blob", blobFeeCap).Interface("New gas fee cap", gasFeeCap).Interface("Old gas tip cap", tx.GasTipCap()).Interface("New gas tip cap", gasTipCap).Msg("Bumping gas fee cap and tip cap for Blob transaction")
gasFeeCapDiff := big.NewInt(0).Sub(newGasFeeCap, tx.GasFeeCap())
gasTipCapDiff := big.NewInt(0).Sub(newGasTipCap, tx.GasTipCap())
gasBlobFeeCapDiff := big.NewInt(0).Sub(newBlobFeeCap, tx.BlobGasFeeCap())

L.Debug().
Str("Old gas fee cap", fmt.Sprintf("%s wei /%s ether", tx.GasFeeCap(), WeiToEther(tx.GasFeeCap()).Text('f', -1))).
Str("New gas fee cap", fmt.Sprintf("%s wei /%s ether", newGasFeeCap, WeiToEther(newGasFeeCap).Text('f', -1))).
Str("Gas fee cap diff", fmt.Sprintf("%s wei /%s ether", gasFeeCapDiff, WeiToEther(gasFeeCapDiff).Text('f', -1))).
Str("Old gas tip cap", fmt.Sprintf("%s wei /%s ether", tx.GasTipCap(), WeiToEther(tx.GasTipCap()).Text('f', -1))).
Str("New gas tip cap", fmt.Sprintf("%s wei /%s ether", newGasTipCap, WeiToEther(newGasTipCap).Text('f', -1))).
Str("Gas fee tip diff", fmt.Sprintf("%s wei /%s ether", gasTipCapDiff, WeiToEther(gasTipCapDiff).Text('f', -1))).
Str("Old gas blob cap", fmt.Sprintf("%s wei /%s ether", tx.BlobGasFeeCap(), WeiToEther(tx.BlobGasFeeCap()).Text('f', -1))).
Str("New gas blob cap", fmt.Sprintf("%s wei /%s ether", newBlobFeeCap, WeiToEther(newBlobFeeCap).Text('f', -1))).
Str("Gas fee blob diff", fmt.Sprintf("%s wei /%s ether", gasBlobFeeCapDiff, WeiToEther(gasBlobFeeCapDiff).Text('f', -1))).
Msg("Bumping gas fee cap and tip cap for Blob transaction")

txData := &types.BlobTx{
Nonce: tx.Nonce(),
To: *tx.To(),
Value: uint256.NewInt(tx.Value().Uint64()),
Gas: tx.Gas(),
GasFeeCap: uint256.NewInt(gasFeeCap.Uint64()),
GasTipCap: uint256.NewInt(gasTipCap.Uint64()),
BlobFeeCap: uint256.NewInt(blobFeeCap.Uint64()),
GasFeeCap: uint256.NewInt(newGasFeeCap.Uint64()),
GasTipCap: uint256.NewInt(newGasTipCap.Uint64()),
BlobFeeCap: uint256.NewInt(newBlobFeeCap.Uint64()),
BlobHashes: tx.BlobHashes(),
Data: tx.Data(),
}

replacementTx, err = types.SignNewTx(privateKey, signer, txData)
case types.AccessListTxType:
gasPrice := client.Cfg.GasBump.StrategyFn(tx.GasPrice())
if err := checkMaxPrice(gasPrice, maxGasPrice); err != nil {
newGasPrice := client.Cfg.GasBump.StrategyFn(tx.GasPrice())
if err := checkMaxPrice(newGasPrice, maxGasPrice); err != nil {
return nil, err
}
L.Debug().Interface("Old gas price", tx.GasPrice()).Interface("New gas price", gasPrice).Msg("Bumping gas price for access list transaction")
gasPriceDiff := big.NewInt(0).Sub(newGasPrice, tx.GasPrice())
L.Debug().
Str("Old gas price", fmt.Sprintf("%s wei /%s ether", tx.GasPrice(), WeiToEther(tx.GasPrice()).Text('f', -1))).
Str("New gas price", fmt.Sprintf("%s wei /%s ether", newGasPrice, WeiToEther(newGasPrice).Text('f', -1))).
Str("Diff", fmt.Sprintf("%s wei /%s ether", gasPriceDiff, WeiToEther(gasPriceDiff).Text('f', -1))).
Msg("Bumping gas price for Access List transaction")

txData := &types.AccessListTx{
Nonce: tx.Nonce(),
Expand Down

0 comments on commit 5e0b15b

Please sign in to comment.