@@ -31,8 +31,10 @@ import (
31
31
32
32
const sampleNumber = 3 // Number of transactions sampled in a block
33
33
34
- var DefaultMaxPrice = big .NewInt (500 * params .GWei )
35
- var DefaultIgnorePrice = big .NewInt (2 * params .Wei )
34
+ var (
35
+ DefaultMaxPrice = big .NewInt (500 * params .GWei )
36
+ DefaultIgnorePrice = big .NewInt (2 * params .Wei )
37
+ )
36
38
37
39
type Config struct {
38
40
Blocks int
@@ -103,8 +105,13 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
103
105
}
104
106
}
105
107
106
- // SuggestPrice returns the recommended gas price.
107
- func (gpo * Oracle ) SuggestPrice (ctx context.Context ) (* big.Int , error ) {
108
+ // SuggestTipCap returns a tip cap so that newly created transaction can have a
109
+ // very high chance to be included in the following blocks.
110
+ //
111
+ // Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be
112
+ // necessary to add the basefee to the returned number to fall back to the legacy
113
+ // behavior.
114
+ func (gpo * Oracle ) SuggestTipCap (ctx context.Context ) (* big.Int , error ) {
108
115
head , _ := gpo .backend .HeaderByNumber (ctx , rpc .LatestBlockNumber )
109
116
headHash := head .Hash ()
110
117
@@ -113,7 +120,7 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
113
120
lastHead , lastPrice := gpo .lastHead , gpo .lastPrice
114
121
gpo .cacheLock .RUnlock ()
115
122
if headHash == lastHead {
116
- return lastPrice , nil
123
+ return new (big. Int ). Set ( lastPrice ) , nil
117
124
}
118
125
gpo .fetchLock .Lock ()
119
126
defer gpo .fetchLock .Unlock ()
@@ -123,17 +130,17 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
123
130
lastHead , lastPrice = gpo .lastHead , gpo .lastPrice
124
131
gpo .cacheLock .RUnlock ()
125
132
if headHash == lastHead {
126
- return lastPrice , nil
133
+ return new (big. Int ). Set ( lastPrice ) , nil
127
134
}
128
135
var (
129
136
sent , exp int
130
137
number = head .Number .Uint64 ()
131
- result = make (chan getBlockPricesResult , gpo .checkBlocks )
138
+ result = make (chan results , gpo .checkBlocks )
132
139
quit = make (chan struct {})
133
- txPrices []* big.Int
140
+ results []* big.Int
134
141
)
135
142
for sent < gpo .checkBlocks && number > 0 {
136
- go gpo .getBlockPrices (ctx , types .MakeSigner (gpo .backend .ChainConfig (), big .NewInt (int64 (number ))), number , sampleNumber , gpo .ignorePrice , result , quit )
143
+ go gpo .getBlockValues (ctx , types .MakeSigner (gpo .backend .ChainConfig (), big .NewInt (int64 (number ))), number , sampleNumber , gpo .ignorePrice , result , quit )
137
144
sent ++
138
145
exp ++
139
146
number --
@@ -142,93 +149,116 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
142
149
res := <- result
143
150
if res .err != nil {
144
151
close (quit )
145
- return lastPrice , res .err
152
+ return new (big. Int ). Set ( lastPrice ) , res .err
146
153
}
147
154
exp --
148
155
// Nothing returned. There are two special cases here:
149
156
// - The block is empty
150
157
// - All the transactions included are sent by the miner itself.
151
158
// In these cases, use the latest calculated price for samping.
152
- if len (res .prices ) == 0 {
153
- res .prices = []* big.Int {lastPrice }
159
+ if len (res .values ) == 0 {
160
+ res .values = []* big.Int {lastPrice }
154
161
}
155
162
// Besides, in order to collect enough data for sampling, if nothing
156
163
// meaningful returned, try to query more blocks. But the maximum
157
164
// is 2*checkBlocks.
158
- if len (res .prices ) == 1 && len (txPrices )+ 1 + exp < gpo .checkBlocks * 2 && number > 0 {
159
- go gpo .getBlockPrices (ctx , types .MakeSigner (gpo .backend .ChainConfig (), big .NewInt (int64 (number ))), number , sampleNumber , gpo .ignorePrice , result , quit )
165
+ if len (res .values ) == 1 && len (results )+ 1 + exp < gpo .checkBlocks * 2 && number > 0 {
166
+ go gpo .getBlockValues (ctx , types .MakeSigner (gpo .backend .ChainConfig (), big .NewInt (int64 (number ))), number , sampleNumber , gpo .ignorePrice , result , quit )
160
167
sent ++
161
168
exp ++
162
169
number --
163
170
}
164
- txPrices = append (txPrices , res .prices ... )
171
+ results = append (results , res .values ... )
165
172
}
166
173
price := lastPrice
167
- if len (txPrices ) > 0 {
168
- sort .Sort (bigIntArray (txPrices ))
169
- price = txPrices [(len (txPrices )- 1 )* gpo .percentile / 100 ]
174
+ if len (results ) > 0 {
175
+ sort .Sort (bigIntArray (results ))
176
+ price = results [(len (results )- 1 )* gpo .percentile / 100 ]
170
177
}
171
178
if price .Cmp (gpo .maxPrice ) > 0 {
172
179
price = new (big.Int ).Set (gpo .maxPrice )
173
180
}
174
181
175
- // Check gas price min.
176
- minGasPrice := common .GetMinGasPrice (head .Number )
177
- if price .Cmp (minGasPrice ) < 0 {
178
- price = new (big.Int ).Set (minGasPrice )
182
+ // Check min gas price for non-eip1559 block
183
+ if head .BaseFee == nil {
184
+ minGasPrice := common .GetMinGasPrice (head .Number )
185
+ if price .Cmp (minGasPrice ) < 0 {
186
+ price = new (big.Int ).Set (minGasPrice )
187
+ }
179
188
}
180
189
181
190
gpo .cacheLock .Lock ()
182
191
gpo .lastHead = headHash
183
192
gpo .lastPrice = price
184
193
gpo .cacheLock .Unlock ()
185
- return price , nil
194
+
195
+ return new (big.Int ).Set (price ), nil
186
196
}
187
197
188
- type getBlockPricesResult struct {
189
- prices []* big.Int
198
+ type results struct {
199
+ values []* big.Int
190
200
err error
191
201
}
192
202
193
- type transactionsByGasPrice []* types.Transaction
203
+ type txSorter struct {
204
+ txs []* types.Transaction
205
+ baseFee * big.Int
206
+ }
207
+
208
+ func newSorter (txs []* types.Transaction , baseFee * big.Int ) * txSorter {
209
+ return & txSorter {
210
+ txs : txs ,
211
+ baseFee : baseFee ,
212
+ }
213
+ }
194
214
195
- func (t transactionsByGasPrice ) Len () int { return len (t ) }
196
- func (t transactionsByGasPrice ) Swap (i , j int ) { t [i ], t [j ] = t [j ], t [i ] }
197
- func (t transactionsByGasPrice ) Less (i , j int ) bool { return t [i ].FeeCapCmp (t [j ]) < 0 }
215
+ func (s * txSorter ) Len () int { return len (s .txs ) }
216
+ func (s * txSorter ) Swap (i , j int ) {
217
+ s .txs [i ], s .txs [j ] = s .txs [j ], s .txs [i ]
218
+ }
219
+ func (s * txSorter ) Less (i , j int ) bool {
220
+ // It's okay to discard the error because a tx would never be
221
+ // accepted into a block with an invalid effective tip.
222
+ tip1 , _ := s .txs [i ].EffectiveTip (s .baseFee )
223
+ tip2 , _ := s .txs [j ].EffectiveTip (s .baseFee )
224
+ return tip1 .Cmp (tip2 ) < 0
225
+ }
198
226
199
227
// getBlockPrices calculates the lowest transaction gas price in a given block
200
228
// and sends it to the result channel. If the block is empty or all transactions
201
229
// are sent by the miner itself(it doesn't make any sense to include this kind of
202
230
// transaction prices for sampling), nil gasprice is returned.
203
- func (gpo * Oracle ) getBlockPrices (ctx context.Context , signer types.Signer , blockNum uint64 , limit int , ignoreUnder * big.Int , result chan getBlockPricesResult , quit chan struct {}) {
231
+ func (gpo * Oracle ) getBlockValues (ctx context.Context , signer types.Signer , blockNum uint64 , limit int , ignoreUnder * big.Int , result chan results , quit chan struct {}) {
204
232
block , err := gpo .backend .BlockByNumber (ctx , rpc .BlockNumber (blockNum ))
205
233
if block == nil {
206
234
select {
207
- case result <- getBlockPricesResult {nil , err }:
235
+ case result <- results {nil , err }:
208
236
case <- quit :
209
237
}
210
238
return
211
239
}
212
- blockTxs := block .Transactions ()
213
- txs := make ([]* types.Transaction , len (blockTxs ))
214
- copy (txs , blockTxs )
215
- sort .Sort (transactionsByGasPrice (txs ))
240
+ // Sort the transaction by effective tip in ascending sort.
241
+ txs := make ([]* types.Transaction , len (block .Transactions ()))
242
+ copy (txs , block .Transactions ())
243
+ sorter := newSorter (txs , block .BaseFee ())
244
+ sort .Sort (sorter )
216
245
217
246
var prices []* big.Int
218
- for _ , tx := range txs {
219
- if ignoreUnder != nil && tx .GasPrice ().Cmp (ignoreUnder ) == - 1 {
247
+ for _ , tx := range sorter .txs {
248
+ tip , _ := tx .EffectiveTip (block .BaseFee ())
249
+ if ignoreUnder != nil && tip .Cmp (ignoreUnder ) == - 1 {
220
250
continue
221
251
}
222
252
sender , err := types .Sender (signer , tx )
223
253
if err == nil && sender != block .Coinbase () {
224
- prices = append (prices , tx . GasPrice () )
254
+ prices = append (prices , tip )
225
255
if len (prices ) >= limit {
226
256
break
227
257
}
228
258
}
229
259
}
230
260
select {
231
- case result <- getBlockPricesResult {prices , nil }:
261
+ case result <- results {prices , nil }:
232
262
case <- quit :
233
263
}
234
264
}
0 commit comments