-
Notifications
You must be signed in to change notification settings - Fork 32
/
chain_impl.go
190 lines (160 loc) · 5.66 KB
/
chain_impl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package chain
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/params"
"github.com/pkg/errors"
"github.com/synapsecns/sanguine/ethergo/chain/chainwatcher"
"github.com/synapsecns/sanguine/ethergo/chain/client"
"github.com/synapsecns/sanguine/ethergo/chain/gas"
"github.com/synapsecns/sanguine/ethergo/chain/watcher"
"math/big"
"sync"
)
var _ Chain = &baseChain{}
// baseChain is an extensible struct that implements chain. This is used for interacting with an underlying
// evm rpc.
type baseChain struct {
// ctx: this is used on all non-client methods
//nolint: containedctx
ctx context.Context
// EVMClient is the rpc client used to access the chain
client.MeteredEVMClient
// cfg is the configuration for the chain
cfg *client.Config
// structMux is the mutex
structMux sync.RWMutex
// chainConfig is the chain config
chainConfig *params.ChainConfig
// height watcher keeps track of the current chain tip height
heightWatcher chainwatcher.BlockHeightWatcher
// contractWatcher watches for events on the contract
contractWatcher chainwatcher.ContractWatcher
// chainID is the cached chain id
chainID uint
// cancel is used to close the session
cancel context.CancelFunc
// rpcAddress used by the chain
rpcAddress string
// estimator stores the gas estimator
// Deprecated: use setter
estimator gas.PriceEstimator
// setter is used to set gas on a tx
setter gas.Setter
}
func (b *baseChain) GasSetter() gas.Setter {
return b.setter
}
// Name is the name of the chain.
// Deprecated: do not use.
func (b *baseChain) Name() string {
return ""
}
// Config gets the config for the chain.
func (b *baseChain) Config() *client.Config {
return b.cfg
}
// RPCAddress gets the rpc address the chain is currently connected to.
func (b *baseChain) RPCAddress() string {
return b.rpcAddress
}
// ChainConfig gets the chain config if it's available.
func (b *baseChain) ChainConfig() *params.ChainConfig {
b.structMux.RLock()
defer b.structMux.RUnlock()
return b.chainConfig
}
// Estimator gets the gas estimator.
func (b *baseChain) Estimator() gas.PriceEstimator {
return b.estimator
}
// GetHeightWatcher gets a block height watcher.
func (b *baseChain) GetHeightWatcher() chainwatcher.BlockHeightWatcher {
return b.heightWatcher
}
// SetChainConfig sets the chain config manually. This is used mostly for testing.
func (b *baseChain) SetChainConfig(config *params.ChainConfig) {
b.structMux.Lock()
defer b.structMux.Unlock()
b.chainConfig = config
}
// NewFromMeteredClient creaates a new client from a metered evm client.
func NewFromMeteredClient(ctx context.Context, config *client.Config, meteredClient client.MeteredEVMClient) (chain Chain, err error) {
b := baseChain{}
b.ctx, b.cancel = context.WithCancel(ctx)
b.cfg = config
if len(config.RPCUrl) > 0 {
b.rpcAddress = config.RPCUrl[0]
}
logger.Debugf("creating eth client %s", config.RPCUrl)
// TODO, correctly set chain id here, only affects metrics
b.MeteredEVMClient = meteredClient
b.chainID = uint(config.ChainID)
b.chainConfig = client.ConfigFromID(b.GetBigChainID())
// initialize monitor/estimator
b.heightWatcher = watcher.NewBlockHeightWatcher(ctx, uint64(b.chainID), b.MeteredEVMClient)
b.contractWatcher = watcher.NewContractWatcher(ctx, &b, b.heightWatcher, b.cfg.RequiredConfirmations)
b.estimator = gas.NewGasPriceEstimator(ctx, &b)
b.setter = gas.NewGasSetter(ctx, &b)
return &b, nil
}
// NewFromURL creates a new client from a url.
//
//nolint:ireturn
func NewFromURL(ctx context.Context, url string) (Chain, error) {
tmpClient, err := client.NewClient(ctx, url)
if err != nil {
return nil, fmt.Errorf("could not create new client at %s: %w", url, err)
}
chainID, err := tmpClient.ChainID(ctx)
if err != nil {
return nil, fmt.Errorf("could not get chain id: %w", err)
}
meteredClient := client.NewMeteredClient(tmpClient, chainID, url, nil)
return NewFromMeteredClient(ctx, &client.Config{
RPCUrl: []string{url},
ChainID: int(chainID.Int64()),
}, meteredClient)
}
// NewFromClient gets a chain from client.
func NewFromClient(ctx context.Context, config *client.Config, evmClient client.EVMClient) (chain Chain, err error) {
rpcURL := ""
if len(config.RPCUrl) > 0 {
rpcURL = config.RPCUrl[0]
}
meteredClient := client.NewMeteredClient(evmClient, big.NewInt(0), rpcURL, config.LimiterConfig)
return NewFromMeteredClient(ctx, config, meteredClient)
}
// New creates a new rpc client for querying an evm-based rpc server and attempts to connect to the chain.
func New(ctx context.Context, config *client.Config) (evmClient Chain, err error) {
if len(config.RPCUrl) == 0 {
return nil, errors.New("could not get ws url")
}
// RPCUrl gets the rpc url from the client
baseClient, err := client.NewClientFromChainID(ctx, config.RPCUrl[0], big.NewInt(int64(config.ChainID)))
if err != nil {
return nil, fmt.Errorf("could not create pool client: %w", err)
}
return NewFromClient(ctx, config, baseClient)
}
// ChainName gets the chain name from the config.
func (b *baseChain) ChainName() string {
return b.Name()
}
// GetChainID gets the cached chain id.
func (b *baseChain) GetChainID() uint {
return b.chainID
}
// GetBigChainID gets the chain id as a big int.
func (b *baseChain) GetBigChainID() *big.Int {
return big.NewInt(int64(b.chainID))
}
// ListenOnContract registers an event listener on a contract address
// the events emitted by the a contractAddress after confirmations (defined in the config).
func (b *baseChain) ListenOnContract(ctx context.Context, contractAddress string, eventLog chan interface{}) error {
err := b.contractWatcher.ListenOnContract(ctx, contractAddress, eventLog)
if err != nil {
return fmt.Errorf("could not listen on contract: %w", err)
}
return nil
}