@@ -16,6 +16,8 @@ extension web3.Eth {
16
16
/// Web3 provider by which accessing to the blockchain
17
17
private let web3Provider : web3
18
18
19
+ private var feeHistory : FeeHistory ?
20
+
19
21
/// Ethereum scope shortcut
20
22
private var eth : web3 . Eth { web3Provider. eth }
21
23
@@ -34,7 +36,7 @@ extension web3.Eth {
34
36
/// - block: Number of block from which counts starts
35
37
/// - blockCount: Count of block to calculate statistics
36
38
/// - percentiles: Percentiles of fees which will split in fees history
37
- public init ( _ provider: web3 , block: String = " latest " , blockCount: BigUInt = 20 , percentiles: [ Double ] = [ 25 , 50 , 75 ] ) {
39
+ public init ( _ provider: web3 , block: String = " latest " , blockCount: BigUInt = 20 , percentiles: [ Double ] = [ 10 , 50 , 90 ] ) {
38
40
self . web3Provider = provider
39
41
self . block = block
40
42
self . blockCount = blockCount
@@ -57,7 +59,7 @@ extension web3.Eth {
57
59
// swiftlint:disable force_unwrapping
58
60
case . minimum: return unwrappedArray. min ( ) !
59
61
case . mean: return unwrappedArray. mean ( ) !
60
- case . median: return unwrappedArray. median ( ) !
62
+ case . median: return unwrappedArray. mean ( ) !
61
63
case . maximum:
62
64
// Checking that suggestedBaseFee is not lower than it will be in the next block
63
65
// because in the maximum statistic we should guarantee that transaction would be included in it.
@@ -67,47 +69,50 @@ extension web3.Eth {
67
69
// swiftlint:enable force_unwrapping
68
70
}
69
71
70
- // private func suggestGasValues( statistic: Statistic) throws -> FeeHistory {
71
- //
72
- // }
73
-
74
- private func suggestTipValue( _ statistic: Statistic ) throws -> BigUInt {
75
-
76
- let latestBlockNumber = try eth. getBlockNumber ( )
72
+ private func soft( twoDimentsion array: [ [ BigUInt ] ] ) -> [ BigUInt ] {
73
+ /// We've got `[[min],[middle],[max]]` 2 dimensional array
74
+ /// we're getting `[min, middle, max].count == self.percentiles.count`,
75
+ /// where each value are mean from the input percentile array
76
+ array. compactMap { percentileArray -> [ BigUInt ] ? in
77
+ guard !percentileArray. isEmpty else { return nil }
78
+ // swiftlint:disable force_unwrapping
79
+ return [ percentileArray. mean ( ) !]
80
+ // swiftlint:enable force_unwrapping
81
+ }
82
+ . flatMap { $0 }
83
+ }
77
84
78
- var block : Block
85
+ private func calculatePercentiles( for data: [ BigUInt ] ) -> [ BigUInt ] {
86
+ percentiles. compactMap { percentile in
87
+ data. percentile ( of: percentile)
88
+ }
89
+ }
79
90
80
- // TODO: Make me work with cache
81
- repeat {
82
- block = try eth. getBlockByNumber ( latestBlockNumber, fullTransactions: true )
83
- } while block. transactions. count < 20
91
+ private func suggestGasValues( ) throws -> FeeHistory {
92
+ // This is some kind of cache.
93
+ // It stores about 9 seconds, than it rewrites it with newer data.
94
+ // TODO: Disabled until 3.0 version, coz `distance` available from iOS 13.
95
+ // guard feeHistory != nil, feeHistory!.timestamp.distance(to: Date()) < cacheTimeout else { return feeHistory! }
84
96
85
- // Storing last block to calculate baseFee of the next block
86
- // latestBlock = block
97
+ return try eth . feeHistory ( blockCount : blockCount , block: block , percentiles : percentiles )
98
+ }
87
99
88
- let transactionsTips = block. transactions
89
- . compactMap { t -> EthereumTransaction ? in
90
- guard case let . transaction( transaction) = t else { return nil }
91
- return transaction
100
+ /// Suggesting tip values
101
+ /// - Returns: `[percentile 1, percentile 2, percentile 3].count == self.percentile.count`
102
+ /// by default there's 3 percentile.
103
+ private func suggestTipValue( ) throws -> [ BigUInt ] {
104
+ /// reaarange `[[min, middle, max]]` to `[[min], [middle], [max]]`
105
+ let rearrengedArray = try suggestGasValues ( ) . reward
106
+ . map { internalArray -> [ BigUInt ] in
107
+ var newArray = [ BigUInt] ( )
108
+ internalArray. enumerated ( ) . forEach { newArray [ $0] = $1 }
109
+ return newArray
92
110
}
93
- // TODO: Add filter for transaction types
94
- . map { $0. maxPriorityFeePerGas }
95
-
96
- return try calculateStatistic ( for: statistic, data: transactionsTips)
111
+ return soft ( twoDimentsion: rearrengedArray)
97
112
}
98
113
99
- private func suggestBaseFee( _ statistic: Statistic ) throws -> BigUInt {
100
- let latestBlockNumber = try eth. getBlockNumber ( )
101
-
102
- // Assigning last block to object var to predict baseFee of the next block
103
- // latestBlock = try eth.getBlockByNumber(latestBlockNumber)
104
- // TODO: Make me work with cache
105
- let lastNthBlocksBaseFees = try ( latestBlockNumber - blockCount ... latestBlockNumber)
106
- . map { try eth. getBlockByNumber ( $0) }
107
- . filter { !$0. transactions. isEmpty }
108
- . compactMap { $0. baseFeePerGas }
109
-
110
- return try calculateStatistic ( for: statistic, data: lastNthBlocksBaseFees)
114
+ private func suggestBaseFee( ) throws -> [ BigUInt ] {
115
+ calculatePercentiles ( for: try suggestGasValues ( ) . baseFeePerGas)
111
116
}
112
117
113
118
private func suggestGasFeeLegacy( _ statistic: Statistic ) throws -> BigUInt {
@@ -141,8 +146,8 @@ public extension web3.Eth.Oracle {
141
146
///
142
147
/// - Parameter statistic: Statistic to apply for base fee calculation
143
148
/// - Returns: Suggested base fee amount according to statistic, nil if failed to perdict
144
- func predictBaseFee( _ statistic : Statistic ) -> BigUInt ? {
145
- guard let value = try ? suggestBaseFee ( statistic ) else { return nil }
149
+ func predictBaseFee( ) -> [ BigUInt ] {
150
+ guard let value = try ? suggestBaseFee ( ) else { return [ ] }
146
151
return value
147
152
}
148
153
@@ -155,8 +160,8 @@ public extension web3.Eth.Oracle {
155
160
///
156
161
/// - Parameter statistic: Statistic to apply for tip calculation
157
162
/// - Returns: Suggested tip amount according to statistic, nil if failed to perdict
158
- func predictTip( _ statistic : Statistic ) -> BigUInt ? {
159
- guard let value = try ? suggestTipValue ( statistic ) else { return nil }
163
+ func predictTip( ) -> [ BigUInt ] {
164
+ guard let value = try ? suggestTipValue ( ) else { return [ ] }
160
165
return value
161
166
}
162
167
@@ -166,9 +171,9 @@ public extension web3.Eth.Oracle {
166
171
/// - baseFee: Statistic to apply for baseFee
167
172
/// - tip: Statistic to apply for tip
168
173
/// - Returns: Tuple where `baseFee` — base fee, `tip` — tip, nil if failed to predict
169
- func predictBothFees( baseFee : Statistic , tip : Statistic ) -> ( baseFee: BigUInt , tip: BigUInt ) ? {
170
- guard let baseFee = try ? suggestBaseFee ( baseFee ) else { return nil }
171
- guard let tip = try ? suggestTipValue ( tip ) else { return nil }
174
+ func predictBothFees( ) -> ( baseFee: [ BigUInt ] , tip: [ BigUInt ] ) ? {
175
+ guard let baseFee = try ? suggestBaseFee ( ) else { return nil }
176
+ guard let tip = try ? suggestTipValue ( ) else { return nil }
172
177
173
178
return ( baseFee: baseFee, tip: tip)
174
179
}
@@ -177,10 +182,10 @@ public extension web3.Eth.Oracle {
177
182
/// Method to get legacy gas price
178
183
/// - Parameter statistic: Statistic to apply for gas price
179
184
/// - Returns: Suggested gas price amount according to statistic, nil if failed to predict
180
- func predictGasPriceLegacy( _ statistic : Statistic ) -> BigUInt ? {
181
- guard let value = try ? suggestGasFeeLegacy ( statistic ) else { return nil }
182
- return value
183
- }
185
+ // func predictGasPriceLegacy() -> BigUInt? {
186
+ // guard let value = try? suggestGasFeeLegacy() else { return nil}
187
+ // return value
188
+ // }
184
189
}
185
190
186
191
public extension web3 . Eth . Oracle {
@@ -199,6 +204,7 @@ public extension web3.Eth.Oracle {
199
204
200
205
public extension web3 . Eth . Oracle {
201
206
struct FeeHistory {
207
+ let timestamp = Date ( )
202
208
let baseFeePerGas : [ BigUInt ]
203
209
let gasUsedRatio : [ Double ]
204
210
let oldestBlock : BigUInt
@@ -244,14 +250,14 @@ extension Array where Element: BinaryInteger {
244
250
return BigUInt ( self . reduce ( 0 , + ) ) / BigUInt( self . count)
245
251
}
246
252
247
- func median ( ) -> BigUInt ? {
253
+ func percentile ( of value : Double ) -> BigUInt ? {
248
254
guard !self . isEmpty else { return nil }
249
255
250
256
let sorted_data = self . sorted ( )
251
- if self . count % 2 == 1 {
252
- return BigUInt ( sorted_data [ Int ( floor ( Double ( self . count) / 2 ) ) ] )
253
- } else {
254
- return BigUInt ( sorted_data [ self . count / 2 ] + sorted_data[ ( self . count / 2 ) - 1 ] / 2 )
255
- }
257
+ // if self.count % 2 == 1 {
258
+ return BigUInt ( sorted_data [ Int ( floor ( Double ( self . count) / value / 10 ) ) ] )
259
+ // } else {
260
+ // return BigUInt(sorted_data[self.count / Int(value) / 10 ] + sorted_data[(self.count / Int(value) / 10) - 1] /( value) / 10 )
261
+ // }
256
262
}
257
263
}
0 commit comments