Skip to content

Commit 891897a

Browse files
committed
PoC of using Simulated Backend with Seth
1 parent 456673e commit 891897a

File tree

5 files changed

+675
-35
lines changed

5 files changed

+675
-35
lines changed

seth/client.go

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@ import (
55
"crypto/ecdsa"
66
"fmt"
77
"math/big"
8-
"net/http"
98
"path/filepath"
109
"reflect"
1110
"strings"
1211
"time"
1312

1413
"github.com/avast/retry-go"
1514
"github.com/ethereum/go-ethereum"
16-
"github.com/ethereum/go-ethereum/rpc"
15+
"github.com/ethereum/go-ethereum/ethclient/simulated"
1716

1817
"github.com/ethereum/go-ethereum/accounts/abi"
1918
"github.com/ethereum/go-ethereum/accounts/abi/bind"
2019
"github.com/ethereum/go-ethereum/common"
2120
"github.com/ethereum/go-ethereum/core/types"
22-
"github.com/ethereum/go-ethereum/ethclient"
2321
"github.com/pkg/errors"
2422
"github.com/rs/zerolog"
2523
"golang.org/x/sync/errgroup"
@@ -64,7 +62,7 @@ var (
6462
// Client is a vanilla go-ethereum client with enhanced debug logging
6563
type Client struct {
6664
Cfg *Config
67-
Client *ethclient.Client
65+
Client simulated.Client
6866
Addresses []common.Address
6967
PrivateKeys []*ecdsa.PrivateKey
7068
ChainID int64
@@ -175,53 +173,63 @@ func NewClientRaw(
175173
pkeys []*ecdsa.PrivateKey,
176174
opts ...ClientOpt,
177175
) (*Client, error) {
178-
if len(cfg.Network.URLs) == 0 {
179-
return nil, errors.New("no RPC URL provided")
180-
}
181-
if len(cfg.Network.URLs) > 1 {
182-
L.Warn().Msg("Multiple RPC URLs provided, only the first one will be used")
183-
}
184-
185176
if cfg.ReadOnly && (len(addrs) > 0 || len(pkeys) > 0) {
186177
return nil, errors.New(ErrReadOnlyWithPrivateKeys)
187178
}
188179

189-
ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration())
190-
defer cancel()
191-
rpcClient, err := rpc.DialOptions(ctx,
192-
cfg.FirstNetworkURL(),
193-
rpc.WithHeaders(cfg.RPCHeaders),
194-
rpc.WithHTTPClient(&http.Client{
195-
Transport: NewLoggingTransport(),
196-
}),
197-
)
198-
if err != nil {
199-
return nil, fmt.Errorf("failed to connect RPC client to '%s' due to: %w", cfg.FirstNetworkURL(), err)
200-
}
201-
client := ethclient.NewClient(rpcClient)
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)
202206

203-
if cfg.Network.ChainID == 0 {
204-
chainId, err := client.ChainID(context.Background())
205-
if err != nil {
206-
return nil, errors.Wrap(err, "failed to get chain ID")
207-
}
208-
cfg.Network.ChainID = chainId.Uint64()
209-
}
210207
ctx, cancelFunc := context.WithCancel(context.Background())
211208
c := &Client{
212209
Cfg: cfg,
213-
Client: client,
214210
Addresses: addrs,
215211
PrivateKeys: pkeys,
216212
URL: cfg.FirstNetworkURL(),
217213
ChainID: mustSafeInt64(cfg.Network.ChainID),
218214
Context: ctx,
219215
CancelFunc: cancelFunc,
220216
}
217+
221218
for _, o := range opts {
222219
o(c)
223220
}
224221

222+
if cfg.Network.ChainID == 0 {
223+
chainId, err := c.Client.ChainID(context.Background())
224+
if err != nil {
225+
return nil, errors.Wrap(err, "failed to get chain ID")
226+
}
227+
cfg.Network.ChainID = chainId.Uint64()
228+
c.ChainID = int64(cfg.Network.ChainID)
229+
}
230+
231+
var err error
232+
225233
if c.ContractAddressToNameMap.addressMap == nil {
226234
c.ContractAddressToNameMap = NewEmptyContractMap()
227235
if !cfg.IsSimulatedNetwork() {
@@ -407,7 +415,7 @@ func (m *Client) TransferETHFromKey(ctx context.Context, fromKeyNum int, to stri
407415
ctx, chainCancel := context.WithTimeout(ctx, m.Cfg.Network.TxnTimeout.Duration())
408416
defer chainCancel()
409417

410-
chainID, err := m.Client.NetworkID(ctx)
418+
chainID, err := m.Client.ChainID(ctx)
411419
if err != nil {
412420
return errors.Wrap(err, "failed to get network ID")
413421
}
@@ -529,6 +537,12 @@ func WithTracer(t *Tracer) ClientOpt {
529537
}
530538
}
531539

540+
func WithEthClient(ethClient simulated.Client) ClientOpt {
541+
return func(c *Client) {
542+
c.Client = ethClient
543+
}
544+
}
545+
532546
/* CallOpts function options */
533547

534548
// CallOpt is a functional option for bind.CallOpts
@@ -828,6 +842,8 @@ This issue is caused by one of two things:
828842
return &bind.TransactOpts{Context: ctx}, NonceStatus{}, GasEstimations{}
829843
}
830844

845+
fmt.Println("sender", opts.From.Hex())
846+
831847
if ctx != nil {
832848
opts.Context = ctx
833849
}

seth/client_simulated_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package seth_test
2+
3+
import (
4+
"context"
5+
"math/big"
6+
"testing"
7+
"time"
8+
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/ethereum/go-ethereum/core/types"
11+
"github.com/ethereum/go-ethereum/ethclient/simulated"
12+
"github.com/pkg/errors"
13+
"github.com/smartcontractkit/chainlink-testing-framework/seth"
14+
link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link"
15+
"github.com/stretchr/testify/require"
16+
)
17+
18+
func newClientWithEthClient(ethClient simulated.Client) (*seth.Client, error) {
19+
cfg, err := seth.ReadConfig()
20+
if err != nil {
21+
return nil, err
22+
}
23+
cs, err := seth.NewContractStore("./contracts/abi", "./contracts/bin", nil)
24+
if err != nil {
25+
return nil, err
26+
}
27+
addrs, pkeys, err := cfg.ParseKeys()
28+
if err != nil {
29+
return nil, err
30+
}
31+
contractMap := seth.NewEmptyContractMap()
32+
33+
abiFinder := seth.NewABIFinder(contractMap, cs)
34+
tracer, err := seth.NewTracer(cs, &abiFinder, cfg, contractMap, addrs)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
nm, err := seth.NewNonceManager(cfg, addrs, pkeys)
40+
if err != nil {
41+
return nil, errors.Wrap(err, seth.ErrCreateNonceManager)
42+
}
43+
44+
c, err := seth.NewClientRaw(cfg, addrs, pkeys, seth.WithContractStore(cs), seth.WithTracer(tracer), seth.WithNonceManager(nm), seth.WithEthClient(ethClient))
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
return c, nil
50+
}
51+
52+
func Test_SimulatedBackend(t *testing.T) {
53+
alloc := map[common.Address]types.Account{
54+
common.HexToAddress("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"): {
55+
Balance: big.NewInt(1000000000000000000), // 1 Ether
56+
},
57+
}
58+
backend := simulated.NewBackend(alloc)
59+
client, err := newClientWithEthClient(backend.Client())
60+
require.NoError(t, err)
61+
62+
ctx, cancelFn := context.WithCancel(context.Background())
63+
defer cancelFn()
64+
65+
ticker := time.NewTicker(100 * time.Millisecond)
66+
go func() {
67+
for {
68+
select {
69+
case <-ticker.C:
70+
backend.Commit()
71+
case <-ctx.Done():
72+
backend.Close()
73+
return
74+
}
75+
}
76+
}()
77+
78+
linkAbi, err := link_token.LinkTokenMetaData.GetAbi()
79+
require.NoError(t, err, "failed to get LINK ABI")
80+
81+
_, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *linkAbi, common.FromHex(link_token.LinkTokenMetaData.Bin))
82+
require.NoError(t, err, "failed to deploy LINK contract")
83+
}

seth/go.mod

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,21 @@ require (
1919
)
2020

2121
require (
22+
github.com/DataDog/zstd v1.4.5 // indirect
2223
github.com/Microsoft/go-winio v0.6.2 // indirect
2324
github.com/StackExchange/wmi v1.2.1 // indirect
25+
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
2426
github.com/benbjohnson/clock v1.3.5 // indirect
27+
github.com/beorn7/perks v1.0.1 // indirect
2528
github.com/bits-and-blooms/bitset v1.13.0 // indirect
2629
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
30+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
31+
github.com/cockroachdb/errors v1.11.3 // indirect
32+
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
33+
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
34+
github.com/cockroachdb/pebble v1.1.2 // indirect
35+
github.com/cockroachdb/redact v1.1.5 // indirect
36+
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
2737
github.com/consensys/bavard v0.1.13 // indirect
2838
github.com/consensys/gnark-crypto v0.12.1 // indirect
2939
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
@@ -35,22 +45,56 @@ require (
3545
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
3646
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
3747
github.com/fsnotify/fsnotify v1.6.0 // indirect
48+
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
49+
github.com/getsentry/sentry-go v0.27.0 // indirect
3850
github.com/go-ole/go-ole v1.3.0 // indirect
51+
github.com/gofrs/flock v0.8.1 // indirect
52+
github.com/gogo/protobuf v1.3.2 // indirect
53+
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
54+
github.com/golang/protobuf v1.5.4 // indirect
55+
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
3956
github.com/google/uuid v1.6.0 // indirect
4057
github.com/gorilla/websocket v1.5.0 // indirect
58+
github.com/hashicorp/go-bexpr v0.1.10 // indirect
59+
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 // indirect
60+
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
61+
github.com/huin/goupnp v1.3.0 // indirect
62+
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
63+
github.com/klauspost/compress v1.16.0 // indirect
64+
github.com/kr/pretty v0.3.1 // indirect
65+
github.com/kr/text v0.2.0 // indirect
4166
github.com/mattn/go-colorable v0.1.13 // indirect
4267
github.com/mattn/go-isatty v0.0.20 // indirect
68+
github.com/mattn/go-runewidth v0.0.13 // indirect
69+
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
70+
github.com/mitchellh/mapstructure v1.4.1 // indirect
71+
github.com/mitchellh/pointerstructure v1.2.0 // indirect
4372
github.com/mmcloughlin/addchain v0.4.0 // indirect
73+
github.com/olekukonko/tablewriter v0.0.5 // indirect
4474
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
75+
github.com/prometheus/client_golang v1.12.0 // indirect
76+
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect
77+
github.com/prometheus/common v0.32.1 // indirect
78+
github.com/prometheus/procfs v0.7.3 // indirect
79+
github.com/rivo/uniseg v0.2.0 // indirect
80+
github.com/rogpeppe/go-internal v1.9.0 // indirect
81+
github.com/rs/cors v1.7.0 // indirect
4582
github.com/russross/blackfriday/v2 v2.1.0 // indirect
4683
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
84+
github.com/status-im/keycard-go v0.2.0 // indirect
4785
github.com/supranational/blst v0.3.13 // indirect
86+
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
4887
github.com/tklauser/go-sysconf v0.3.12 // indirect
4988
github.com/tklauser/numcpus v0.6.1 // indirect
89+
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
5090
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
5191
golang.org/x/crypto v0.25.0 // indirect
5292
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
5393
golang.org/x/sys v0.22.0 // indirect
94+
golang.org/x/text v0.16.0 // indirect
95+
golang.org/x/time v0.5.0 // indirect
96+
google.golang.org/protobuf v1.34.2 // indirect
97+
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
5498
gopkg.in/yaml.v3 v3.0.1 // indirect
5599
rsc.io/tmplfunc v0.0.3 // indirect
56100
)

0 commit comments

Comments
 (0)