Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TT-1546] add back chain id #1133

Merged
merged 3 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion seth/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test_cli:

.PHONY: test_others
test_others:
SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 -race `go list ./... | grep -v examples` -run "TestContractMap|TestGasEstimator|TestRPCHealthCheck|TestUtil|TestContract|TestConfig"
SETH_CONFIG_PATH="seth.toml" SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 -race `go list ./... | grep -v examples` -run "TestContractMap|TestGasEstimator|TestRPCHealthCheck|TestUtil|TestContract|TestConfig"

# this one is without -race flag, because zerolog is not thread safe and fails the run
.PHONY: test_gas_bumping
Expand Down
18 changes: 17 additions & 1 deletion seth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,11 @@ This config uses what we consider reasonable defaults, such as:
You can also use a `ClientBuilder` to build a config programmatically. Here's an extensive example:

```go
client, err := builder.
client, err := NewClientBuilder().
// network
WithNetworkName("my network").
// if empty we will ask the RPC node for the chain ID
WithNetworkChainId(1337).
WithRpcUrl("ws://localhost:8546").
WithPrivateKeys([]string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}).
WithRpcDialTimeout(10*time.Second).
Expand Down Expand Up @@ -241,6 +243,20 @@ By default, it uses the same values as simplified configuration, but you can ove
that we thought to be most useful, it's not a 1:1 mapping of all fields in the `Config` struct. Therefore, if you need to set some more advanced options, you should create the `Config` struct directly,
use TOML config or manually set the fields on the `Config` struct returned by the builder.

It' also possible to use the builder to create a new config from an existing one:

```go
client, err := NewClientBuilderWithConfig(&existingConfig).
UseNetworkWithChainId(1337).
WithEIP1559DynamicFees(false).
Build()

if err != nil {
log.Fatal(err)
}
```
This can be useful if you already have a config, but want to modify it slightly. It can also be useful if you read TOML config with multiple `Networks` and you want to specify which one you want to use.

### Supported env vars

Some crucial data is stored in env vars, create `.envrc` and use `source .envrc`, or use `direnv`
Expand Down
19 changes: 8 additions & 11 deletions seth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"math/big"
"net/http"
"path/filepath"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -246,14 +245,12 @@ func NewClientRaw(
}
client := ethclient.NewClient(rpcClient)

chainId, err := client.ChainID(context.Background())
if err != nil {
return nil, errors.Wrap(err, "failed to get chain ID")
}
cfg.Network.ChainID = chainId.String()
cID, err := strconv.Atoi(cfg.Network.ChainID)
if err != nil {
return nil, err
if cfg.Network.ChainID == 0 {
chainId, err := client.ChainID(context.Background())
if err != nil {
return nil, errors.Wrap(err, "failed to get chain ID")
}
cfg.Network.ChainID = chainId.Uint64()
}
ctx, cancelFunc := context.WithCancel(context.Background())
c := &Client{
Expand All @@ -262,7 +259,7 @@ func NewClientRaw(
Addresses: addrs,
PrivateKeys: pkeys,
URL: cfg.FirstNetworkURL(),
ChainID: int64(cID),
ChainID: int64(cfg.Network.ChainID),
Context: ctx,
CancelFunc: cancelFunc,
}
Expand Down Expand Up @@ -321,7 +318,7 @@ func NewClientRaw(
Str("NetworkName", cfg.Network.Name).
Interface("Addresses", addrs).
Str("RPC", cfg.FirstNetworkURL()).
Str("ChainID", cfg.Network.ChainID).
Uint64("ChainID", cfg.Network.ChainID).
Int64("Ephemeral keys", *cfg.EphemeralAddrs).
Msg("Created new client")

Expand Down
123 changes: 109 additions & 14 deletions seth/client_builder.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package seth

import (
"fmt"
"time"

"github.com/pkg/errors"
)

type ClientBuilder struct {
config *Config
errors []error
}

// NewClientBuilder creates a new ClientBuilder with reasonable default values. You only need to pass private key(s) and RPC URL to build a usable config.
Expand Down Expand Up @@ -44,10 +48,21 @@ func NewClientBuilder() *ClientBuilder {
}
}

// NewClientBuilderWithConfig creates a new ClientBuilder with a provided config. If it doesn't have the network set, remember to set it with `UseNetworkWithName(name string)`
// or `WithSelectedNetworkWithChainId(chainId uint64)`, before calling any of the methods that modify the Network.
func NewClientBuilderWithConfig(config *Config) *ClientBuilder {
return &ClientBuilder{
config: config,
}
}

// 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()
if !c.checkIfNetworkIsSet() {
return c
}

c.config.Network.URLs = []string{url}
// defensive programming
if len(c.config.Networks) == 0 {
Expand All @@ -58,10 +73,40 @@ func (c *ClientBuilder) WithRpcUrl(url string) *ClientBuilder {
return c
}

// UseNetworkWithName sets the network to use by name. If the network with the provided name is not found in the `Networks` slice, config will fail on build.
// There is no default value.
func (c *ClientBuilder) UseNetworkWithName(name string) *ClientBuilder {
for _, network := range c.config.Networks {
if network.Name == name {
c.config.Network = network
return c
}
}

c.errors = append(c.errors, fmt.Errorf("network with name '%s' not found", name))
return c
}

// UseNetworkWithChainId sets the network to use by chain ID. If the network with the provided chain ID is not found in the `Networks` slice, config will fail on build.
// There is no default value.
func (c *ClientBuilder) UseNetworkWithChainId(chainId uint64) *ClientBuilder {
for _, network := range c.config.Networks {
if network.ChainID == chainId {
c.config.Network = network
return c
}
}

c.errors = append(c.errors, fmt.Errorf("network with chainId '%d' not found", chainId))
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()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.PrivateKeys = pks
// defensive programming
if len(c.config.Networks) == 0 {
Expand All @@ -75,7 +120,9 @@ func (c *ClientBuilder) WithPrivateKeys(pks []string) *ClientBuilder {
// WithNetworkName sets the network name, useful mostly for debugging and logging.
// Default value is "default".
func (c *ClientBuilder) WithNetworkName(name string) *ClientBuilder {
c.assertNetworkIsSet()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.Name = name
// defensive programming
if len(c.config.Networks) == 0 {
Expand All @@ -86,13 +133,31 @@ func (c *ClientBuilder) WithNetworkName(name string) *ClientBuilder {
return c
}

// WithNetworkChainId sets the network chainID. If no value is set, we will ask the RPC node for the chainID.
// There is no default value.
func (c *ClientBuilder) WithNetworkChainId(chainId uint64) *ClientBuilder {
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.ChainID = chainId
// defensive programming
if len(c.config.Networks) == 0 {
c.config.Networks = append(c.config.Networks, c.config.Network)
} else if net := c.config.findNetworkByName(c.config.Network.Name); net != nil {
net.ChainID = chainId
}
return c
}

// WithGasPriceEstimations enables or disables gas price estimations, sets the number of blocks to use for estimation or transaction priority.
// Even with estimations enabled you should still either set legacy gas price with `WithLegacyGasPrice()` or EIP-1559 dynamic fees with `WithDynamicGasPrices()`
// ss they will be used as fallback values, if the estimations fail.
// 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()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.GasPriceEstimationEnabled = enabled
c.config.Network.GasPriceEstimationBlocks = estimationBlocks
c.config.Network.GasPriceEstimationTxPriority = txPriority
Expand All @@ -110,7 +175,9 @@ func (c *ClientBuilder) WithGasPriceEstimations(enabled bool, estimationBlocks u
// 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()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.EIP1559DynamicFees = enabled
// defensive programming
if len(c.config.Networks) == 0 {
Expand All @@ -124,7 +191,9 @@ func (c *ClientBuilder) WithEIP1559DynamicFees(enabled bool) *ClientBuilder {
// 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()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.GasPrice = gasPrice
// defensive programming
if len(c.config.Networks) == 0 {
Expand All @@ -138,7 +207,9 @@ func (c *ClientBuilder) WithLegacyGasPrice(gasPrice int64) *ClientBuilder {
// 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()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.GasFeeCap = gasFeeCap
c.config.Network.GasTipCap = gasTipCap
// defensive programming
Expand All @@ -154,7 +225,9 @@ func (c *ClientBuilder) WithDynamicGasPrices(gasFeeCap, gasTipCap int64) *Client
// 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(transferGasFee int64) *ClientBuilder {
c.assertNetworkIsSet()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.TransferGasFee = transferGasFee
// defensive programming
if len(c.config.Networks) == 0 {
Expand All @@ -167,7 +240,7 @@ func (c *ClientBuilder) WithTransferGasFee(transferGasFee int64) *ClientBuilder

// WithGasBumping sets the number of retries for gas bumping and max gas price. You can also provide a custom bumping strategy. If the transaction is not mined within this number of retries, it will be considered failed.
// If the gas price is bumped to a value higher than max gas price, no more gas bumping will be attempted and previous gas price will be used by all subsequent attempts. If set to 0 max price is not checked.
// Default value is 10 retries, no max gas price and a default bumping strategy (with gas increase % based on gas_price_estimation_tx_priority)
// Default value is 0 retries. If you want to use default bumping strategy (where gas increase % based on gas_price_estimation_tx_priority), pass `nil` as the customBumpingStrategy.
func (c *ClientBuilder) WithGasBumping(retries uint, maxGasPrice int64, customBumpingStrategy GasBumpStrategyFn) *ClientBuilder {
c.config.GasBump = &GasBumpConfig{
Retries: retries,
Expand All @@ -180,7 +253,9 @@ 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()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.TxnTimeout = MustMakeDuration(timeout)
// defensive programming
if len(c.config.Networks) == 0 {
Expand All @@ -194,7 +269,9 @@ func (c *ClientBuilder) WithTransactionTimeout(timeout time.Duration) *ClientBui
// 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()
if !c.checkIfNetworkIsSet() {
return c
}
c.config.Network.DialTimeout = MustMakeDuration(timeout)
// defensive programming
if len(c.config.Networks) == 0 {
Expand Down Expand Up @@ -260,11 +337,29 @@ func (c *ClientBuilder) WithNonceManager(rateLimitSec int, retries uint, timeout

// Build creates a new Client from the builder.
func (c *ClientBuilder) Build() (*Client, error) {
return NewClientWithConfig(c.config)
config, err := c.BuildConfig()
if err != nil {
return nil, err
}
return NewClientWithConfig(config)
}

// BuildConfig returns the config from the builder.
func (c *ClientBuilder) BuildConfig() (*Config, error) {
if len(c.errors) > 0 {
var concatenatedErrors string
for _, err := range c.errors {
concatenatedErrors = fmt.Sprintf("%s\n%s", concatenatedErrors, err.Error())
}
return nil, fmt.Errorf("errors occurred during building the config:%s", concatenatedErrors)
}
return c.config, nil
}

func (c *ClientBuilder) assertNetworkIsSet() {
func (c *ClientBuilder) checkIfNetworkIsSet() bool {
if c.config.Network == nil {
panic("Network is required to use this method, but it was nil")
c.errors = append(c.errors, errors.New("at least one method that required network to be set was called, but network is nil"))
return false
}
return true
}
2 changes: 1 addition & 1 deletion seth/cmd/seth.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func RunCLI(args []string) error {
if err != nil {
return errors.Wrap(err, "failed to get chain ID")
}
cfg.Network.ChainID = chainId.String()
cfg.Network.ChainID = chainId.Uint64()
}
}

Expand Down
4 changes: 1 addition & 3 deletions seth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type NonceManagerCfg struct {
type Network struct {
Name string `toml:"name"`
URLs []string `toml:"urls_secret"`
ChainID uint64 `toml:"chain_id"`
EIP1559DynamicFees bool `toml:"eip_1559_dynamic_fees"`
GasPrice int64 `toml:"gas_price"`
GasFeeCap int64 `toml:"gas_fee_cap"`
Expand All @@ -111,9 +112,6 @@ type Network struct {
GasPriceEstimationEnabled bool `toml:"gas_price_estimation_enabled"`
GasPriceEstimationBlocks uint64 `toml:"gas_price_estimation_blocks"`
GasPriceEstimationTxPriority string `toml:"gas_price_estimation_tx_priority"`

// derivative vars
ChainID string
}

// DefaultClient returns a Client with reasonable default config with the specified RPC URL and private keys. You should pass at least 1 private key.
Expand Down
Loading
Loading