Skip to content

Commit 0975347

Browse files
committed
Merge branch 'develop' into experimental/EIP-1559-combined
* develop: Fix EthereumTransaction decoding bug. Fix appropriate test result for such Add test to check whether all transactions in block decodes well. Fix percentiles array extension out of range index bug. Fixin typos and revert unnecessary code changes. Add comments to GasOracle & common methods - Add `BlockNumber` enum to enable strong typing in block number management. - Full test coverage of GasOracle public methods. - Clean GasOracle class. Code cleaning & unnecessary changes revert. Move back decode([Any].Type) methods Fixups - Update percentile arrays method. - fix JSONRPCmethod validation bug - Rewrite GasOracle - Implement generic decodeHex methods for T: DecodableFromHex, [T], [[T]]. - Make RPC API `feeHistory` call internal. - Reimplement `tipFeePercentiles` to return array of percentiles of last tips. - Reimplement `baseFeePercentiles` to return array of percentiles of last baseFee. - Rename `bothFeesPercentiles` to return tuple of arrays of percentiles of last both tips and baseFee. - `JSONRPCresponse` now can decode `Web3.Oracle.FeeHistory` - `JSONRPCparams` now can encode `[Double]` - Add `OracleTests` - `testPretictBaseFee` - `testPredictTip` Rewrite GasOracle - Now it works with feeHistory JRON RPC Ethereum call - Now all method returns percentiles, which user set on object init. Move AnyCodingKeys to appropriate place Delete unused hexDecode methods for KeyedDecodingContainer. Add 2 dimension array decodeHex method. - Rename decodeHex method to swift name convention. - Add FeeHistory struct initializer. Implement feeHistory usage for Gas cost prediction - Add method `decodeHex` for `Arrays<T> where T: DecodableFromHex`. - Add feeHistory request. - Add public feeHistory API call - Move GasOracle from `Web3` scope to `web3.Eth` scope (watch out case of first "w") - Change Oracle struct to work with feeHistory API call. - Refactor `JSONRPCmethod` to make it more explicit. - Refactor `InfuraWebsockerMethod` with a styling. Add tests Gas Oracle Fixes by PR review Fixed by PR review. Fixes by PR review. Add legacy gas price prediction method (pre EIP-1559) Revert "Revert "Implement Oracle class"" # Conflicts: # Sources/web3swift/Convenience/Decodable+Extensions.swift # Sources/web3swift/Web3/Web3+Structures.swift
2 parents 6eda73a + 629174c commit 0975347

File tree

10 files changed

+716
-122
lines changed

10 files changed

+716
-122
lines changed

Sources/web3swift/Convenience/Array+Extension.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//
66

77
import Foundation
8+
import BigInt
89

910
extension Array {
1011
public func split(intoChunksOf chunkSize: Int) -> [[Element]] {
@@ -14,3 +15,41 @@ extension Array {
1415
}
1516
}
1617
}
18+
19+
extension Array where Element: Comparable {
20+
/// Sorts array and drops most and least values.
21+
/// - Returns: Sorted array without most and least values, nil if `array.count` <= 2
22+
func cropAnomalyValues() -> Self? {
23+
var sortedArray = self.sorted()
24+
// Array should at least counts two to pass that formations.
25+
guard sortedArray.count > 1 else { return nil }
26+
sortedArray.removeLast()
27+
sortedArray.removeFirst()
28+
return sortedArray
29+
}
30+
}
31+
32+
extension Array where Element: BinaryInteger {
33+
// TODO: Make me generic
34+
/// Calculates mean value of a dataset
35+
/// - Returns: Mean value of a dataset, nil if dataset is empty
36+
func mean() -> BigUInt? {
37+
guard !self.isEmpty else { return nil }
38+
return BigUInt(self.reduce(0, +)) / BigUInt(self.count)
39+
}
40+
41+
42+
/// Calculates percentile of dataset on which get called.
43+
/// - Parameter value: Percentile value.
44+
/// - Returns: Item from dataset that is belongs to given percentile, nil if dataset is empty.
45+
func percentile(of value: Double) -> Element? {
46+
guard !self.isEmpty else { return nil }
47+
48+
let normalizedValue = value / 100 * Double(self.count)
49+
let index = Int(ceil(normalizedValue))
50+
51+
let sorted_data = self.sorted()
52+
guard index < self.count else { return sorted_data[sorted_data.count - 1] }
53+
return sorted_data[index]
54+
}
55+
}

Sources/web3swift/Convenience/Decodable+Extensions.swift

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ extension KeyedDecodingContainer {
3535
/// for the given key.
3636
/// - throws: `DecodingError.valueNotFound` if `self` has a null entry for
3737
/// the given key.
38+
@available(*, deprecated, message: "Use decodeHex insetad")
3839
public func decode(_ type: [Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [Any] {
3940
var values = try nestedUnkeyedContainer(forKey: key)
4041
return try values.decode(type)
@@ -52,6 +53,7 @@ extension KeyedDecodingContainer {
5253
/// for the given key.
5354
/// - throws: `DecodingError.valueNotFound` if `self` has a null entry for
5455
/// the given key.
56+
@available(*, deprecated, message: "Use decodeHex() insetad")
5557
public func decode(_ type: [String: Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [String: Any] {
5658
let values = try nestedContainer(keyedBy: AnyCodingKey.self, forKey: key)
5759
return try values.decode(type)
@@ -70,6 +72,7 @@ extension KeyedDecodingContainer {
7072
/// the value is a null value.
7173
/// - throws: `DecodingError.typeMismatch` if the encountered encoded value
7274
/// is not convertible to the requested type.
75+
@available(*, deprecated, message: "In next version Will be replaced by decodeHexIfPresent() insetad")
7376
public func decodeIfPresent(_ type: [Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [Any]? {
7477
guard contains(key),
7578
try decodeNil(forKey: key) == false else { return nil }
@@ -89,39 +92,92 @@ extension KeyedDecodingContainer {
8992
/// the value is a null value.
9093
/// - throws: `DecodingError.typeMismatch` if the encountered encoded value
9194
/// is not convertible to the requested type.
95+
@available(*, deprecated, message: "In next version Will be replaced by decodeHexIfPresent() insetad")
9296
public func decodeIfPresent(_ type: [String: Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [String: Any]? {
9397
guard contains(key),
9498
try decodeNil(forKey: key) == false else { return nil }
9599
return try decode(type, forKey: key)
96100
}
97101

98-
/// Decodes a value of the given key from Hex to <Type>
102+
/// Decodes a value of the given key from Hex to `DecodableFromHex`
99103
///
100-
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `UInt.Type`, `Date.Type`
104+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`
101105
///
102106
/// - Parameter type: Generic type `T` wich conforms to `DecodableFromHex` protocol
103107
/// - Parameter key: The key that the decoded value is associated with.
104-
/// - Returns: A decoded value of type `DecodableFromHex`
105-
/// - throws: `Web3Error.dataError` if value associated with key are unable
106-
/// to be initialized as `DecodableFromHex`.
107-
public func decodeHex<T: DecodableFromHex>(to type: T.Type, key: KeyedDecodingContainer<K>.Key) throws -> T {
108-
let string = try self.decode(String.self, forKey: key)
109-
guard let number = T(fromHex: string) else { throw Web3Error.dataError }
108+
/// - Returns: A decoded value of type `BigUInt`
109+
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `DecodableFromHex`.
110+
public func decodeHex<T: DecodableFromHex>(_ type: T.Type, forKey: KeyedDecodingContainer<K>.Key) throws -> T {
111+
let hexString = try self.decode(String.self, forKey: forKey)
112+
guard let number = T(fromHex: hexString) else { throw Web3Error.dataError }
110113
return number
111114
}
112115

113-
/// Decodes a value of the given key from Hex to <Type>
116+
/// Decodes a value of the given key from Hex to `[DecodableFromHex]`
114117
///
115-
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `UInt.Type`, `Date.Type`
118+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`
119+
///
120+
/// - Parameter type: Array of a generic type `T` wich conforms to `DecodableFromHex` protocol
121+
/// - Parameter key: The key that the decoded value is associated with.
122+
/// - Returns: A decoded value of type `BigUInt`
123+
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `[[DecodableFromHex]]`.
124+
public func decodeHex<T: DecodableFromHex>(_ type: [T].Type, forKey: KeyedDecodingContainer<K>.Key) throws -> [T] {
125+
var container = try nestedUnkeyedContainer(forKey: forKey)
126+
guard let array = try? container.decodeHex(type) else { throw Web3Error.dataError }
127+
return array
128+
}
129+
130+
/// Decodes a value of the given key from Hex to `[DecodableFromHex]`
131+
///
132+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `EthereumAddress`
133+
///
134+
/// - Parameter type: Array of a generic type `T` wich conforms to `DecodableFromHex` protocol
135+
/// - Parameter key: The key that the decoded value is associated with.
136+
/// - Returns: A decoded value of type `BigUInt`
137+
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `[[DecodableFromHex]]`.
138+
public func decodeHex<T: DecodableFromHex>(_ type: [[T]].Type, forKey: KeyedDecodingContainer<K>.Key) throws -> [[T]] {
139+
var container = try nestedUnkeyedContainer(forKey: forKey)
140+
guard let array = try? container.decodeHex(type) else { throw Web3Error.dataError }
141+
return array
142+
}
143+
}
144+
145+
public extension UnkeyedDecodingContainer {
146+
/// Decodes a unkeyed value from hex to `[DecodableFromHex]`
147+
///
148+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `EthereumAddress`
149+
///
150+
/// - Parameter type: Generic type `T` wich conforms to `DecodableFromHex` protocol
151+
/// - Parameter key: The key that the decoded value is associated with.
152+
/// - Returns: A decoded value of type `BigUInt`
153+
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `[DecodableFromHex]`.
154+
mutating func decodeHex<T: DecodableFromHex>(_ type: [T].Type) throws -> [T] {
155+
var array: [T] = []
156+
while !isAtEnd {
157+
let hexString = try decode(String.self)
158+
guard let item = T(fromHex: hexString) else { continue }
159+
array.append(item)
160+
}
161+
return array
162+
}
163+
164+
165+
/// Decodes a unkeyed value from Hex to `DecodableFromHex`
166+
///
167+
/// Currently this method supports only `Data.Type`, `BigUInt.Type`, `Date.Type`, `EthereumAddress`
116168
///
117169
/// - Parameter type: Generic type `T` wich conforms to `DecodableFromHex` protocol
118170
/// - Parameter key: The key that the decoded value is associated with.
119-
/// - Returns: A decoded value of type Optional `DecodableFromHex?`
120-
/// - throws: `Web3Error.dataError` if value associated with key are unable
121-
/// to be initialized as `DecodableFromHex`.
122-
public func decodeHexIfPresent<T: DecodableFromHex>(to type: T.Type, key: KeyedDecodingContainer<K>.Key) throws -> T? {
123-
guard contains(key) else { return nil }
124-
return try decodeHex(to: type, key: key)
171+
/// - Returns: A decoded value of type `BigUInt`
172+
/// - throws: `Web3Error.dataError` if value associated with key are unable to be initialized as `[[DecodableFromHex]]`.
173+
mutating func decodeHex<T: DecodableFromHex>(_ type: [[T]].Type) throws -> [[T]] {
174+
var array: [[T]] = []
175+
while !isAtEnd {
176+
var container = try nestedUnkeyedContainer()
177+
let intArr = try container.decodeHex([T].self)
178+
array.append(intArr)
179+
}
180+
return array
125181
}
126182
}
127183

@@ -143,14 +199,6 @@ extension BigUInt: DecodableFromHex {
143199
}
144200
}
145201

146-
extension UInt: DecodableFromHex {
147-
public init?(fromHex hexString: String) {
148-
self.init()
149-
guard let tmp = UInt(hexString.stripHexPrefix(), radix: 16) else { return nil }
150-
self = tmp
151-
}
152-
}
153-
154202
extension Date: DecodableFromHex {
155203
public init?(fromHex hexString: String) {
156204
self.init()
@@ -166,6 +214,7 @@ extension EthereumAddress: DecodableFromHex {
166214
}
167215
}
168216

217+
// deprecated, should be removed in 3.0.0
169218
private extension KeyedDecodingContainer {
170219
func decode(_ type: [String: Any].Type) throws -> [String: Any] {
171220
var dictionary: [String: Any] = [:]
@@ -190,6 +239,7 @@ private extension KeyedDecodingContainer {
190239
}
191240
}
192241

242+
// deprecated, should be removed in 3.0.0
193243
private extension UnkeyedDecodingContainer {
194244
mutating func decode(_ type: [Any].Type) throws -> [Any] {
195245
var elements: [Any] = []
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// Promise+Web3+Eth+FeeHistory.swift
3+
// web3swift
4+
//
5+
// Created by Yaroslav on 11.04.2022.
6+
// Copyright © 2022 web3swift. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import PromiseKit
11+
import BigInt
12+
13+
extension web3.Eth {
14+
func feeHistory(blockCount: BigUInt, block: String, percentiles:[Double]) throws -> Web3.Oracle.FeeHistory {
15+
let request = JSONRPCRequestFabric.prepareRequest(.feeHistory, parameters: [blockCount.description.addHexPrefix(), block, percentiles])
16+
let rp = web3.dispatch(request)
17+
let queue = web3.requestDispatcher.queue
18+
return try rp.map(on: queue) { response in
19+
guard let value: Web3.Oracle.FeeHistory = response.getValue() else {
20+
if response.error != nil {
21+
throw Web3Error.nodeError(desc: response.error!.message)
22+
}
23+
throw Web3Error.nodeError(desc: "Invalid value from Ethereum node")
24+
}
25+
return value
26+
}.wait()
27+
}
28+
}

0 commit comments

Comments
 (0)