-
Notifications
You must be signed in to change notification settings - Fork 2
/
gaspriceestimator.go
113 lines (94 loc) · 2.48 KB
/
gaspriceestimator.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
package gasestimator
import (
"context"
"fmt"
"log"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethclient"
)
// GasPriceEstimator is the gas price estimator, it returns cached gas price to allow a timely
// execution of a transaction.
type GasPriceEstimator struct {
gasPrice *big.Int
gasPricer ethereum.GasPricer
updateInterval time.Duration
rwMutex sync.RWMutex
wg sync.WaitGroup
closeOnce sync.Once
closed chan struct{}
}
// NewGasPriceEstimator creates an instance of GasPriceEstimator
func NewGasPriceEstimator(rawRPCURL string) (*GasPriceEstimator, error) {
cl, err := ethclient.Dial(rawRPCURL)
if err != nil {
return nil, fmt.Errorf("gasestimator: ethclient.Dial: %v", err)
}
gasPrice, err := cl.SuggestGasPrice(context.Background())
if err != nil {
return nil, fmt.Errorf("gasestimator: SuggestGasPrice: %v", err)
}
return newGasPriceEstimator(gasPrice, cl, 4*time.Second), nil
}
func newGasPriceEstimator(initGasPrice *big.Int, gasPricer ethereum.GasPricer, updateInterval time.Duration) *GasPriceEstimator {
estimator := &GasPriceEstimator{
gasPrice: initGasPrice,
gasPricer: gasPricer,
updateInterval: updateInterval,
closed: make(chan struct{}),
}
estimator.runAsync()
return estimator
}
// Close implements io.Closer interface.
func (e *GasPriceEstimator) Close() (err error) {
e.closeOnce.Do(func() {
close(e.closed)
e.wg.Wait()
})
return
}
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
// execution of a transaction.
func (e *GasPriceEstimator) SuggestGasPrice() (gasPrice *big.Int) {
e.rwMutex.RLock()
gasPrice = new(big.Int).Set(e.gasPrice)
e.rwMutex.RUnlock()
return
}
func (e *GasPriceEstimator) runAsync() {
ctx, cancel := context.WithCancel(context.Background())
e.cancelOnClose(cancel)
e.wg.Add(1)
go func() {
defer e.wg.Done()
for {
select {
case <-ctx.Done():
return
case <-time.After(e.updateInterval):
}
newGasPrice, err := e.gasPricer.SuggestGasPrice(ctx)
if err != nil {
log.Printf("gasestimator: SuggestGasPrice: %v", err)
continue
}
curGasPrice := e.SuggestGasPrice()
if curGasPrice.Cmp(newGasPrice) != 0 {
e.rwMutex.Lock()
e.gasPrice = newGasPrice
e.rwMutex.Unlock()
}
}
}()
}
func (e *GasPriceEstimator) cancelOnClose(cancel context.CancelFunc) {
e.wg.Add(1)
go func() {
defer e.wg.Done()
defer cancel()
<-e.closed
}()
}