@@ -5,14 +5,17 @@ import (
55 "crypto/ecdsa"
66 "fmt"
77 "math/big"
8+ "net/http"
89 "path/filepath"
910 "reflect"
1011 "strings"
1112 "time"
1213
1314 "github.com/avast/retry-go"
1415 "github.com/ethereum/go-ethereum"
16+ "github.com/ethereum/go-ethereum/ethclient"
1517 "github.com/ethereum/go-ethereum/ethclient/simulated"
18+ "github.com/ethereum/go-ethereum/rpc"
1619
1720 "github.com/ethereum/go-ethereum/accounts/abi"
1821 "github.com/ethereum/go-ethereum/accounts/abi/bind"
@@ -137,23 +140,26 @@ func NewClientWithConfig(cfg *Config) (*Client, error) {
137140 }
138141
139142 abiFinder := NewABIFinder (contractAddressToNameMap , cs )
140- if len (cfg .Network .URLs ) == 0 {
141- return nil , fmt .Errorf ("at least one url should be present in config in 'secret_urls = []'" )
142- }
143- tr , err := NewTracer (cs , & abiFinder , cfg , contractAddressToNameMap , addrs )
144- if err != nil {
145- return nil , errors .Wrap (err , ErrCreateTracer )
143+
144+ var opts []ClientOpt
145+
146+ // even if the ethclient that was passed supports tracing, we still need the RPC URL, because we cannot get from
147+ // the instance of ethclient, since it doesn't expose any such method
148+ if (cfg .ethclient != nil && shouldIntialiseTracer (cfg .ethclient , cfg ) && len (cfg .Network .URLs ) > 0 ) || cfg .ethclient == nil {
149+ tr , err := NewTracer (cs , & abiFinder , cfg , contractAddressToNameMap , addrs )
150+ if err != nil {
151+ return nil , errors .Wrap (err , ErrCreateTracer )
152+ }
153+ opts = append (opts , WithTracer (tr ))
146154 }
147155
156+ opts = append (opts , WithContractStore (cs ), WithNonceManager (nm ), WithContractMap (contractAddressToNameMap ), WithABIFinder (& abiFinder ))
157+
148158 return NewClientRaw (
149159 cfg ,
150160 addrs ,
151161 pkeys ,
152- WithContractStore (cs ),
153- WithNonceManager (nm ),
154- WithTracer (tr ),
155- WithContractMap (contractAddressToNameMap ),
156- WithABIFinder (& abiFinder ),
162+ opts ... ,
157163 )
158164}
159165
@@ -177,39 +183,46 @@ func NewClientRaw(
177183 return nil , errors .New (ErrReadOnlyWithPrivateKeys )
178184 }
179185
180- // TODO we should execute this only if we haven't passed an instance of Client
181- // or... we should externalize client creation to a separate function
182- // and by default create a new instance of Client using the URL from the config
183- // althouth that would change the API a bit
184-
185- // if len(cfg.Network.URLs) == 0 {
186- // return nil, errors.New("no RPC URL provided")
187- // }
188-
189- // if len(cfg.Network.URLs) > 1 {
190- // L.Warn().Msg("Multiple RPC URLs provided, only the first one will be used")
191- // }
192-
193- // ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration())
194- // defer cancel()
195- // rpcClient, err := rpc.DialOptions(ctx,
196- // cfg.FirstNetworkURL(),
197- // rpc.WithHeaders(cfg.RPCHeaders),
198- // rpc.WithHTTPClient(&http.Client{
199- // Transport: NewLoggingTransport(),
200- // }),
201- // )
202- // if err != nil {
203- // return nil, fmt.Errorf("failed to connect RPC client to '%s' due to: %w", cfg.FirstNetworkURL(), err)
204- // }
205- // client := ethclient.NewClient(rpcClient)
186+ var firstUrl string
187+ var client simulated.Client
188+ if cfg .ethclient == nil {
189+ L .Info ().Msg ("Creating new ethereum client" )
190+ if len (cfg .Network .URLs ) == 0 {
191+ return nil , errors .New ("no RPC URL provided" )
192+ }
193+
194+ if len (cfg .Network .URLs ) > 1 {
195+ L .Warn ().Msg ("Multiple RPC URLs provided, only the first one will be used" )
196+ }
197+
198+ ctx , cancel := context .WithTimeout (context .Background (), cfg .Network .DialTimeout .Duration ())
199+ defer cancel ()
200+ rpcClient , err := rpc .DialOptions (ctx ,
201+ cfg .MustFirstNetworkURL (),
202+ rpc .WithHeaders (cfg .RPCHeaders ),
203+ rpc .WithHTTPClient (& http.Client {
204+ Transport : NewLoggingTransport (),
205+ }),
206+ )
207+ if err != nil {
208+ return nil , fmt .Errorf ("failed to connect RPC client to '%s' due to: %w" , cfg .MustFirstNetworkURL (), err )
209+ }
210+ client = ethclient .NewClient (rpcClient )
211+ firstUrl = cfg .MustFirstNetworkURL ()
212+ } else {
213+ L .Info ().
214+ Str ("Type" , reflect .TypeOf (cfg .ethclient ).String ()).
215+ Msg ("Using provided ethereum client" )
216+ client = cfg .ethclient
217+ }
206218
207219 ctx , cancelFunc := context .WithCancel (context .Background ())
208220 c := & Client {
221+ Client : client ,
209222 Cfg : cfg ,
210223 Addresses : addrs ,
211224 PrivateKeys : pkeys ,
212- URL : cfg . FirstNetworkURL () ,
225+ URL : firstUrl ,
213226 ChainID : mustSafeInt64 (cfg .Network .ChainID ),
214227 Context : ctx ,
215228 CancelFunc : cancelFunc ,
@@ -225,7 +238,7 @@ func NewClientRaw(
225238 return nil , errors .Wrap (err , "failed to get chain ID" )
226239 }
227240 cfg .Network .ChainID = chainId .Uint64 ()
228- c .ChainID = int64 (cfg .Network .ChainID )
241+ c .ChainID = mustSafeInt64 (cfg .Network .ChainID )
229242 }
230243
231244 var err error
@@ -287,7 +300,7 @@ func NewClientRaw(
287300 L .Info ().
288301 Str ("NetworkName" , cfg .Network .Name ).
289302 Interface ("Addresses" , addrs ).
290- Str ("RPC" , cfg . FirstNetworkURL () ).
303+ Str ("RPC" , firstUrl ).
291304 Uint64 ("ChainID" , cfg .Network .ChainID ).
292305 Int64 ("Ephemeral keys" , * cfg .EphemeralAddrs ).
293306 Msg ("Created new client" )
@@ -324,7 +337,9 @@ func NewClientRaw(
324337 }
325338 }
326339
327- if c .Cfg .TracingLevel != TracingLevel_None && c .Tracer == nil {
340+ // we cannot use the tracer with simulated backend, because it doesn't expose a method to get rpcClient (even though it has one)
341+ // and Tracer needs rpcClient to call debug_traceTransaction
342+ if shouldIntialiseTracer (c .Client , cfg ) && c .Cfg .TracingLevel != TracingLevel_None && c .Tracer == nil {
328343 if c .ContractStore == nil {
329344 cs , err := NewContractStore (filepath .Join (cfg .ConfigDir , cfg .ABIDir ), filepath .Join (cfg .ConfigDir , cfg .BINDir ), cfg .GethWrappersDirs )
330345 if err != nil {
@@ -537,12 +552,6 @@ func WithTracer(t *Tracer) ClientOpt {
537552 }
538553}
539554
540- func WithEthClient (ethClient simulated.Client ) ClientOpt {
541- return func (c * Client ) {
542- c .Client = ethClient
543- }
544- }
545-
546555/* CallOpts function options */
547556
548557// CallOpt is a functional option for bind.CallOpts
@@ -1401,3 +1410,11 @@ func (m *Client) mergeLogMeta(pe *DecodedTransactionLog, l types.Log) {
14011410 pe .TXIndex = l .TxIndex
14021411 pe .Removed = l .Removed
14031412}
1413+
1414+ func shouldIntialiseTracer (client simulated.Client , cfg * Config ) bool {
1415+ return len (cfg .Network .URLs ) > 0 && supportsTracing (client )
1416+ }
1417+
1418+ func supportsTracing (client simulated.Client ) bool {
1419+ return strings .Contains (reflect .TypeOf (client ).String (), "ethclient.Client" )
1420+ }
0 commit comments