@@ -21,58 +21,50 @@ extension Web3 {
21
21
/// Ethereum scope shortcut
22
22
private var eth : web3 . Eth { web3Provider. eth }
23
23
24
- /// Block to start getting history
25
- var block : String
24
+ /// Block to start getting history backward
25
+ var block : BlockNumber
26
26
27
- /// Count of blocks to calculate statistics
27
+ /// Count of blocks to include in dataset
28
28
var blockCount : BigUInt
29
29
30
- /// Count of transactions to filter block for tip calculation
30
+ /// Percentiles
31
+ ///
32
+ /// This property set values by which dataset would be sliced.
33
+ ///
34
+ /// If you set it to `[25.0, 50.0, 75.0]` on any prediction property read you'll get
35
+ /// `[71456911562, 92735433497, 105739785122]` which means that first item in array is more
36
+ /// than 25% of the whole dataset, second one more than 50% of the dataset and third one
37
+ /// more than 75% of the dataset.
38
+ ///
39
+ /// Another example: If you set it [100.0] you'll get the very highest value of a dataset e.g. max Tip amount.
31
40
var percentiles : [ Double ]
32
41
42
+ // TODO: Disabled until 3.0 version, coz will be enabled from 3.0.0.
43
+ // var forceDropCache = false
44
+
33
45
/// Oracle initializer
34
46
/// - Parameters:
35
47
/// - provider: Web3 Ethereum provider
36
48
/// - block: Number of block from which counts starts backward
37
49
/// - blockCount: Count of block to calculate statistics
38
- /// - percentiles: Percentiles of fees which will split in fees history
39
- public init ( _ provider: web3 , block: String = " latest " , blockCount: BigUInt = 20 , percentiles: [ Double ] = [ 25 , 50 , 75 ] ) {
50
+ /// - percentiles: Percentiles of fees to which result of predictions will be split in
51
+ public init ( _ provider: web3 , block: BlockNumber = . latest, blockCount: BigUInt = 20 , percentiles: [ Double ] = [ 25 , 50 , 75 ] ) {
40
52
self . web3Provider = provider
41
53
self . block = block
42
54
self . blockCount = blockCount
43
55
self . percentiles = percentiles
44
56
}
45
57
46
- // private func calcBaseFee(for block: Block?) -> BigUInt {
47
- // guard let block = block else { return 0 }
48
- // return Web3.calcBaseFee(block) ?? 0
49
- // }
50
-
51
- private func calculateStatistic( for statistic: Statistic , data: [ BigUInt ] ) throws -> BigUInt {
52
- let noAnomalyArray = data. cropAnomalyValues ( )
53
-
54
- // FIXME: Set appropriate error thrown.
55
- guard let unwrappedArray = noAnomalyArray, !unwrappedArray. isEmpty else { throw Web3Error . unknownError }
56
-
57
- switch statistic {
58
- // Force unwrapping is ok, since array checked for epmtiness above
59
- // swiftlint:disable force_unwrapping
60
- case . minimum: return unwrappedArray. min ( ) !
61
- case . mean: return unwrappedArray. mean ( ) !
62
- case . median: return unwrappedArray. mean ( ) !
63
- case . maximum:
64
- // Checking that suggestedBaseFee is not lower than it will be in the next block
65
- // because in the maximum statistic we should guarantee that transaction would be included in it.
66
- // return max(calcBaseFee(for: latestBlock), unwrappedArray.max()!)
67
- return unwrappedArray. max ( ) !
68
- }
69
- // swiftlint:enable force_unwrapping
70
- }
71
58
59
+ /// Returning one dimensional array from two dimensional array
60
+ ///
61
+ /// We've got `[[min],[middle],[max]]` 2 dimensional array
62
+ /// we're getting `[min, middle, max].count == self.percentiles.count`,
63
+ /// where each value are mean from the input percentile arrays
64
+ ///
65
+ /// - Parameter array: `[[min], [middle], [max]]` 2 dimensional array
66
+ /// - Returns: `[min, middle, max].count == self.percentiles.count`
72
67
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 arrays
76
68
array. compactMap { percentileArray -> [ BigUInt ] ? in
77
69
guard !percentileArray. isEmpty else { return nil }
78
70
// swiftlint:disable force_unwrapping
@@ -82,6 +74,9 @@ extension Web3 {
82
74
. flatMap { $0 }
83
75
}
84
76
77
+ /// Method calculates percentiles array based on `self.percetniles` value
78
+ /// - Parameter data: Integer data from which percentiles should be calculated
79
+ /// - Returns: Array of values which is in positions in dataset to given percentiles
85
80
private func calculatePercentiles( for data: [ BigUInt ] ) -> [ BigUInt ] {
86
81
percentiles. compactMap { percentile in
87
82
data. percentile ( of: percentile)
@@ -92,9 +87,9 @@ extension Web3 {
92
87
// This is some kind of cache.
93
88
// It stores about 9 seconds, than it rewrites it with newer data.
94
89
// 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! }
90
+ // guard feeHistory == nil, forceDropCache, feeHistory!.timestamp.distance(to: Date()) > cacheTimeout else { return feeHistory! }
96
91
97
- return try eth. feeHistory ( blockCount: blockCount, block: block, percentiles: percentiles)
92
+ return try eth. feeHistory ( blockCount: blockCount, block: block. hexValue , percentiles: percentiles)
98
93
}
99
94
100
95
/// Suggesting tip values
@@ -126,8 +121,14 @@ extension Web3 {
126
121
return calculatePercentiles ( for: feeHistory!. baseFeePerGas)
127
122
}
128
123
129
- private func suggestGasFeeLegacy( _ statistic: Statistic ) throws -> BigUInt {
130
- let latestBlockNumber = try eth. getBlockNumber ( )
124
+ private func suggestGasFeeLegacy( ) throws -> [ BigUInt ] {
125
+ var latestBlockNumber : BigUInt = 0
126
+ switch block {
127
+ case . latest: latestBlockNumber = try eth. getBlockNumber ( )
128
+ case let . exact( number) : latestBlockNumber = number
129
+ }
130
+
131
+ guard latestBlockNumber != 0 else { return [ ] }
131
132
132
133
// TODO: Make me work with cache
133
134
let lastNthBlockGasPrice = try ( latestBlockNumber - blockCount ... latestBlockNumber)
@@ -140,7 +141,7 @@ extension Web3 {
140
141
}
141
142
. map { $0. gasPrice }
142
143
143
- return try calculateStatistic ( for: statistic , data : lastNthBlockGasPrice)
144
+ return calculatePercentiles ( for: lastNthBlockGasPrice)
144
145
}
145
146
}
146
147
}
@@ -149,62 +150,48 @@ public extension Web3.Oracle {
149
150
// MARK: - Base Fee
150
151
/// Softed baseFee amount
151
152
///
152
- /// Normalized means that most high and most low value were droped from calculation.
153
- ///
154
- /// - Returns: Suggested base fee amount according to statistic, nil if failed to perdict
153
+ /// - Returns: `[percentile_1, percentile_2, percentile_3, ...].count == self.percentile.count`
154
+ /// empty array if failed to perdict. By default there's 3 percentile.
155
155
var baseFeePercentiles : [ BigUInt ] {
156
156
guard let value = try ? suggestBaseFee ( ) else { return [ ] }
157
157
return value
158
158
}
159
159
160
160
// MARK: - Tip
161
- /// Maximum tip amount based on last block tips
161
+ /// Tip amount
162
162
///
163
- /// Normalized means that most high and most low value were droped from calculation.
164
- ///
165
- /// Method calculates the suggested tip based on the most recent block that contains more than transactionsCount transactions
166
- ///
167
- /// - Parameter statistic: Statistic to apply for tip calculation
168
- /// - Returns: Suggested tip amount according to statistic, nil if failed to perdict
163
+ /// - Returns: `[percentile_1, percentile_2, percentile_3, ...].count == self.percentile.count`
164
+ /// empty array if failed to perdict. By default there's 3 percentile.
169
165
var tipFeePercentiles : [ BigUInt ] {
170
166
guard let value = try ? suggestTipValue ( ) else { return [ ] }
171
167
return value
172
168
}
173
169
174
170
// MARK: - Summary fees
175
- /// Method to get summary fees
176
- /// - Parameters:
177
- /// - baseFee: Statistic to apply for baseFee
178
- /// - tip: Statistic to apply for tip
179
- /// - Returns: Tuple where `baseFee` — base fee, `tip` — tip, nil if failed to predict
171
+ /// Summary fees amount
172
+ ///
173
+ /// - Returns: `[percentile_1, percentile_2, percentile_3, ...].count == self.percentile.count`
174
+ /// nil if failed to perdict. By default there's 3 percentile.
180
175
var bothFeesPercentiles : ( baseFee: [ BigUInt ] , tip: [ BigUInt ] ) ? {
181
- guard let baseFee = try ? suggestBaseFee ( ) else { return nil }
182
- guard let tip = try ? suggestTipValue ( ) else { return nil }
183
-
184
- return ( baseFee: baseFee, tip: tip)
176
+ var baseFeeArr : [ BigUInt ] = [ ]
177
+ var tipArr : [ BigUInt ] = [ ]
178
+ if let baseFee = try ? suggestBaseFee ( ) {
179
+ baseFeeArr = baseFee
180
+ }
181
+ if let tip = try ? suggestTipValue ( ) {
182
+ tipArr = tip
183
+ }
184
+ return ( baseFee: baseFeeArr, tip: tipArr)
185
185
}
186
186
187
187
// MARK: - Legacy GasPrice
188
- /// Method to get legacy gas price
189
- /// - Parameter statistic: Statistic to apply for gas price
190
- /// - Returns: Suggested gas price amount according to statistic, nil if failed to predict
191
- // func predictGasPriceLegacy() -> BigUInt? {
192
- // guard let value = try? suggestGasFeeLegacy() else { return nil}
193
- // return value
194
- // }
195
- }
196
-
197
- public extension Web3 . Oracle {
198
- // TODO: Make me struct and encapsulate math within to make me extendable
199
- enum Statistic {
200
- /// Mininum statistic
201
- case minimum
202
- /// Mean statistic
203
- case mean
204
- /// Median statistic
205
- case median
206
- /// Maximum statistic
207
- case maximum
188
+ /// Legacy gasPrice amount
189
+ ///
190
+ /// - Returns: `[percentile_1, percentile_2, percentile_3, ...].count == self.percentile.count`
191
+ /// empty array if failed to perdict. By default there's 3 percentile.
192
+ var gasPriceLegacyPercentiles : [ BigUInt ] {
193
+ guard let value = try ? suggestGasFeeLegacy ( ) else { return [ ] }
194
+ return value
208
195
}
209
196
}
210
197
@@ -235,3 +222,24 @@ extension Web3.Oracle.FeeHistory: Decodable {
235
222
self . reward = try values. decodeHex ( [ [ BigUInt ] ] . self, forKey: . reward)
236
223
}
237
224
}
225
+
226
+
227
+ public extension Web3 {
228
+ /// Enum for convenient type safe work with block number
229
+ enum BlockNumber {
230
+ /// Latest block of a chain
231
+ case latest
232
+ /// Exact block number
233
+ case exact( BigUInt )
234
+
235
+ /// Block number as a string
236
+ ///
237
+ /// Could be `hexString` either `latest`
238
+ internal var hexValue : String {
239
+ switch self {
240
+ case . latest: return " latest "
241
+ case let . exact( number) : return String ( number, radix: 16 ) . addHexPrefix ( )
242
+ }
243
+ }
244
+ }
245
+ }
0 commit comments