Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit a4ec32b

Browse files
authored
Merge pull request #34 from smartcontractkit/rpc-headers
use DialOptions, set default timeout, use custom RPC headers
2 parents 80dc793 + bbabbbf commit a4ec32b

8 files changed

Lines changed: 128 additions & 17 deletions

File tree

client.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"crypto/ecdsa"
66
verr "errors"
77
"fmt"
8+
"github.com/ethereum/go-ethereum/rpc"
89
"math/big"
10+
"net/http"
911
"path/filepath"
1012
"strconv"
1113
"strings"
@@ -133,7 +135,7 @@ func NewClientWithConfig(cfg *Config) (*Client, error) {
133135
if len(cfg.Network.URLs) == 0 {
134136
return nil, fmt.Errorf("at least one url should be present in config in 'secret_urls = []'")
135137
}
136-
tr, err := NewTracer(cfg.Network.URLs[0], cs, &abiFinder, cfg, contractAddressToNameMap, addrs)
138+
tr, err := NewTracer(cs, &abiFinder, cfg, contractAddressToNameMap, addrs)
137139
if err != nil {
138140
return nil, errors.Wrap(err, ErrCreateTracer)
139141
}
@@ -215,6 +217,10 @@ func ValidateConfig(cfg *Config) error {
215217
return fmt.Errorf("KeyFileSource is set to 'file' but the path to the key file is not set")
216218
}
217219

220+
if cfg.Network.DialTimeout == nil {
221+
cfg.Network.DialTimeout = &Duration{D: DefaultDialTimeout}
222+
}
223+
218224
return nil
219225
}
220226

@@ -240,11 +246,19 @@ func NewClientRaw(
240246
if len(cfg.Network.URLs) > 1 {
241247
L.Warn().Msg("Multiple RPC URLs provided, only the first one will be used")
242248
}
243-
244-
client, err := ethclient.Dial(cfg.Network.URLs[0])
249+
ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration())
250+
defer cancel()
251+
rpcClient, err := rpc.DialOptions(ctx,
252+
cfg.FirstNetworkURL(),
253+
rpc.WithHeaders(cfg.RPCHeaders),
254+
rpc.WithHTTPClient(&http.Client{
255+
Transport: NewLoggingTransport(),
256+
}),
257+
)
245258
if err != nil {
246-
return nil, fmt.Errorf("failed to connect to '%s' due to: %w", cfg.Network.URLs[0], err)
259+
return nil, fmt.Errorf("failed to connect RPC client to '%s' due to: %w", cfg.FirstNetworkURL(), err)
247260
}
261+
client := ethclient.NewClient(rpcClient)
248262

249263
chainId, err := client.ChainID(context.Background())
250264
if err != nil {
@@ -255,16 +269,16 @@ func NewClientRaw(
255269
if err != nil {
256270
return nil, err
257271
}
258-
ctx, cancel := context.WithCancel(context.Background())
272+
ctx, cancelFunc := context.WithCancel(context.Background())
259273
c := &Client{
260274
Cfg: cfg,
261275
Client: client,
262276
Addresses: addrs,
263277
PrivateKeys: pkeys,
264-
URL: cfg.Network.URLs[0],
278+
URL: cfg.FirstNetworkURL(),
265279
ChainID: int64(cID),
266280
Context: ctx,
267-
CancelFunc: cancel,
281+
CancelFunc: cancelFunc,
268282
}
269283
for _, o := range opts {
270284
o(c)
@@ -320,7 +334,7 @@ func NewClientRaw(
320334
L.Info().
321335
Str("NetworkName", cfg.Network.Name).
322336
Interface("Addresses", addrs).
323-
Str("RPC", cfg.Network.URLs[0]).
337+
Str("RPC", cfg.FirstNetworkURL()).
324338
Str("ChainID", cfg.Network.ChainID).
325339
Int64("Ephemeral keys", *cfg.EphemeralAddrs).
326340
Msg("Created new client")
@@ -364,7 +378,7 @@ func NewClientRaw(
364378
abiFinder := NewABIFinder(c.ContractAddressToNameMap, c.ContractStore)
365379
c.ABIFinder = &abiFinder
366380
}
367-
tr, err := NewTracer(cfg.Network.URLs[0], c.ContractStore, c.ABIFinder, cfg, c.ContractAddressToNameMap, addrs)
381+
tr, err := NewTracer(c.ContractStore, c.ABIFinder, cfg, c.ContractAddressToNameMap, addrs)
368382
if err != nil {
369383
return nil, errors.Wrap(err, ErrCreateTracer)
370384
}

client_main_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func newClientWithContractMapFromEnv(t *testing.T) *seth.Client {
122122
c.ContractAddressToNameMap = contractMap
123123

124124
// now let's recreate the Tracer, so that it has the same contract map
125-
tracer, err := seth.NewTracer(c.Cfg.Network.URLs[0], c.ContractStore, c.ABIFinder, c.Cfg, contractMap, c.Addresses)
125+
tracer, err := seth.NewTracer(c.ContractStore, c.ABIFinder, c.Cfg, contractMap, c.Addresses)
126126
require.NoError(t, err, "failed to create tracer")
127127

128128
c.Tracer = tracer
@@ -154,7 +154,7 @@ func NewDebugContractSetup() (
154154
contractMap := seth.NewEmptyContractMap()
155155

156156
abiFinder := seth.NewABIFinder(contractMap, cs)
157-
tracer, err := seth.NewTracer(cfg.Network.URLs[0], cs, &abiFinder, cfg, contractMap, addrs)
157+
tracer, err := seth.NewTracer(cs, &abiFinder, cfg, contractMap, addrs)
158158
if err != nil {
159159
return nil, nil, common.Address{}, common.Address{}, nil, err
160160
}

cmd/seth.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package seth
33
import (
44
"context"
55
"fmt"
6+
"github.com/ethereum/go-ethereum/rpc"
67
"math/big"
78
"os"
89
"path/filepath"
@@ -354,10 +355,16 @@ func RunCLI(args []string) error {
354355
return fmt.Errorf("default network not defined in the TOML file")
355356
}
356357

357-
client, err := ethclient.Dial(cfg.Network.URLs[0])
358+
if cfg.Network.DialTimeout == nil {
359+
cfg.Network.DialTimeout = &seth.Duration{D: seth.DefaultDialTimeout}
360+
}
361+
ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration())
362+
defer cancel()
363+
rpcClient, err := rpc.DialOptions(ctx, cfg.FirstNetworkURL(), rpc.WithHeaders(cfg.RPCHeaders))
358364
if err != nil {
359-
return fmt.Errorf("failed to connect to '%s' due to: %w", cfg.Network.URLs[0], err)
365+
return fmt.Errorf("failed to connect RPC client to '%s' due to: %w", cfg.FirstNetworkURL(), err)
360366
}
367+
client := ethclient.NewClient(rpcClient)
361368
defer client.Close()
362369

363370
if cfg.Network.Name == seth.DefaultNetworkName {
@@ -372,6 +379,9 @@ func RunCLI(args []string) error {
372379
zero := int64(0)
373380
cfg.EphemeralAddrs = &zero
374381
cfg.TracingLevel = seth.TracingLevel_All
382+
if cfg.Network.DialTimeout == nil {
383+
cfg.Network.DialTimeout = &seth.Duration{D: seth.DefaultDialTimeout}
384+
}
375385

376386
client, err := seth.NewClientWithConfig(cfg)
377387
if err != nil {

config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"crypto/ecdsa"
55
"encoding/base64"
66
"fmt"
7+
"net/http"
78
"os"
89
"path/filepath"
910
"strings"
@@ -36,6 +37,7 @@ const (
3637
ONE_PASS_VAULT_ENV_VAR = "SETH_ONE_PASS_VAULT"
3738

3839
DefaultNetworkName = "Default"
40+
DefaultDialTimeout = 1 * time.Minute
3941
)
4042

4143
type KeyFileSource string
@@ -49,6 +51,7 @@ type Config struct {
4951
// internal fields
5052
RevertedTransactionsFile string
5153
ephemeral bool
54+
RPCHeaders http.Header
5255

5356
// external fields
5457
KeyFileSource KeyFileSource `toml:"keyfile_source"`
@@ -87,6 +90,7 @@ type Network struct {
8790
GasTipCap int64 `toml:"gas_tip_cap"`
8891
GasLimit uint64 `toml:"gas_limit"`
8992
TxnTimeout *Duration `toml:"transaction_timeout"`
93+
DialTimeout *Duration `toml:"dial_timeout"`
9094
TransferGasFee int64 `toml:"transfer_gas_fee"`
9195
PrivateKeys []string `toml:"private_keys_secret"`
9296
GasPriceEstimationEnabled bool `toml:"gas_price_estimation_enabled"`
@@ -162,10 +166,18 @@ func ReadConfig() (*Config, error) {
162166
} else {
163167
cfg.Network.PrivateKeys = append(cfg.Network.PrivateKeys, rootPrivateKey)
164168
}
169+
if cfg.Network.DialTimeout == nil {
170+
cfg.Network.DialTimeout = &Duration{D: DefaultDialTimeout}
171+
}
165172
L.Trace().Interface("Config", cfg).Msg("Parsed seth config")
166173
return cfg, nil
167174
}
168175

176+
// FirstNetworkURL returns first network URL
177+
func (c *Config) FirstNetworkURL() string {
178+
return c.Network.URLs[0]
179+
}
180+
169181
// ParseKeys parses private keys from the config
170182
func (c *Config) ParseKeys() ([]common.Address, []*ecdsa.PrivateKey, error) {
171183
addresses := make([]common.Address, 0)

examples_wasp/client_main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func NewDebugContractSetup() (
5555
contractMap := seth.NewEmptyContractMap()
5656

5757
abiFinder := seth.NewABIFinder(contractMap, cs)
58-
tracer, err := seth.NewTracer(cfg.Network.URLs[0], cs, &abiFinder, cfg, contractMap, addrs)
58+
tracer, err := seth.NewTracer(cs, &abiFinder, cfg, contractMap, addrs)
5959
if err != nil {
6060
return nil, nil, common.Address{}, common.Address{}, nil, err
6161
}

http_logging_transport.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package seth
2+
3+
import (
4+
"crypto/tls"
5+
"fmt"
6+
"net/http"
7+
"net/http/httputil"
8+
"os"
9+
"time"
10+
)
11+
12+
// LoggingTransport is a custom transport to log requests and responses
13+
type LoggingTransport struct {
14+
Transport http.RoundTripper
15+
}
16+
17+
// RoundTrip implements the RoundTripper interface
18+
func (t *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
19+
start := time.Now()
20+
21+
reqDump, err := httputil.DumpRequestOut(req, true)
22+
if err != nil {
23+
L.Error().Err(err).Msg("Error dumping request")
24+
} else {
25+
fmt.Printf("Request:\n%s\n", string(reqDump))
26+
}
27+
28+
transport := t.Transport
29+
if transport == nil {
30+
transport = http.DefaultTransport
31+
}
32+
33+
resp, err := transport.RoundTrip(req)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
respDump, err := httputil.DumpResponse(resp, true)
39+
if err != nil {
40+
L.Error().Err(err).Msg("Error dumping response")
41+
} else {
42+
fmt.Printf("Response:\n%s\n", string(respDump))
43+
}
44+
45+
fmt.Printf("Request took %s\n", time.Since(start))
46+
return resp, nil
47+
}
48+
49+
// NewLoggingTransport creates a new logging transport for GAP or default transport
50+
// controlled by SETH_LOG_LEVEL
51+
func NewLoggingTransport() http.RoundTripper {
52+
if os.Getenv(LogLevelEnvVar) == "debug" {
53+
return &LoggingTransport{
54+
// TODO: GAP, add proper certificates
55+
Transport: &http.Transport{
56+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
57+
},
58+
}
59+
} else {
60+
return &http.Transport{
61+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
62+
}
63+
}
64+
}

seth.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ key_sync_retries = 10
6060

6161
[[networks]]
6262
name = "Anvil"
63+
dial_timeout="1m"
6364
transaction_timeout = "30s"
6465
urls_secret = ["ws://localhost:8545"]
6566
transfer_gas_fee = 21_000
@@ -73,6 +74,7 @@ gas_tip_cap = 1_000_000_000
7374

7475
[[networks]]
7576
name = "Geth"
77+
dial_timeout="1m"
7678
transaction_timeout = "30s"
7779
urls_secret = ["ws://localhost:8546"]
7880
transfer_gas_fee = 21_000
@@ -86,6 +88,7 @@ gas_tip_cap = 3_000_000_000
8688

8789
[[networks]]
8890
name = "Default"
91+
dial_timeout="1m"
8992
transaction_timeout = "30s"
9093
# enable EIP-1559 transactions, because Seth will disable them if they are not supported
9194
eip_1559_dynamic_fees = true
@@ -102,6 +105,7 @@ gas_tip_cap = 50_000_000_000 #50 gwei
102105

103106
[[networks]]
104107
name = "Fuji"
108+
dial_timeout="1m"
105109
transaction_timeout = "30s"
106110
eip_1559_dynamic_fees = true
107111

@@ -124,6 +128,7 @@ eip_1559_dynamic_fees = true
124128

125129
[[networks]]
126130
name = "Sepolia"
131+
dial_timeout="1m"
127132
transaction_timeout = "30s"
128133
eip_1559_dynamic_fees = true
129134

@@ -147,6 +152,7 @@ gas_tip_cap = 5_000_000_000
147152

148153
[[networks]]
149154
name = "Mumbai"
155+
dial_timeout="1m"
150156
transaction_timeout = "30s"
151157
eip_1559_dynamic_fees = true
152158

@@ -173,6 +179,7 @@ gas_tip_cap = 30460480806
173179

174180
[[networks]]
175181
name = "zkEVM"
182+
dial_timeout="1m"
176183
transaction_timeout = "30s"
177184
eip_1559_dynamic_fees = false
178185

@@ -196,6 +203,7 @@ gas_tip_cap = 1_800_000_000
196203

197204
[[networks]]
198205
name = "ARBITRUM_SEPOLIA"
206+
dial_timeout="1m"
199207
transaction_timeout = "10m"
200208
transfer_gas_fee = 50_000
201209
# gas_limit = 15_000_000

tracing.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package seth
22

33
import (
4+
"context"
45
"fmt"
56
"strconv"
67
"strings"
@@ -92,10 +93,12 @@ type Call struct {
9293
Calls []Call `json:"calls"`
9394
}
9495

95-
func NewTracer(url string, cs *ContractStore, abiFinder *ABIFinder, cfg *Config, contractAddressToNameMap ContractMap, addresses []common.Address) (*Tracer, error) {
96-
c, err := rpc.Dial(url)
96+
func NewTracer(cs *ContractStore, abiFinder *ABIFinder, cfg *Config, contractAddressToNameMap ContractMap, addresses []common.Address) (*Tracer, error) {
97+
ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration())
98+
defer cancel()
99+
c, err := rpc.DialOptions(ctx, cfg.FirstNetworkURL(), rpc.WithHeaders(cfg.RPCHeaders))
97100
if err != nil {
98-
return nil, fmt.Errorf("failed to connect to '%s' due to: %w", url, err)
101+
return nil, fmt.Errorf("failed to connect to '%s' due to: %w", cfg.FirstNetworkURL(), err)
99102
}
100103
return &Tracer{
101104
Cfg: cfg,

0 commit comments

Comments
 (0)