mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 21:35:21 +01:00
REF: Fee estimation and price fetch improvement
This commit is contained in:
parent
126ce1dcb2
commit
3730ba441a
@ -136,6 +136,11 @@
|
||||
B44034102BCC40A400162242 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = B440340E2BCC40A400162242 /* fiatUnits.json */; };
|
||||
B44034112BCC40A400162242 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = B440340E2BCC40A400162242 /* fiatUnits.json */; };
|
||||
B44034122BCC40A400162242 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = B440340E2BCC40A400162242 /* fiatUnits.json */; };
|
||||
B45010982C0FCB7700619044 /* MarketAPI+Electrum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6CA5142558EBA3009312A5 /* MarketAPI+Electrum.swift */; };
|
||||
B450109C2C0FCD8A00619044 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B450109B2C0FCD8A00619044 /* Utilities.swift */; };
|
||||
B450109D2C0FCD9F00619044 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B450109B2C0FCD8A00619044 /* Utilities.swift */; };
|
||||
B450109E2C0FCDA000619044 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B450109B2C0FCD8A00619044 /* Utilities.swift */; };
|
||||
B450109F2C0FCDA500619044 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B450109B2C0FCD8A00619044 /* Utilities.swift */; };
|
||||
B4549F362B82B10D002E3153 /* ci_post_clone.sh in Resources */ = {isa = PBXBuildFile; fileRef = B4549F352B82B10D002E3153 /* ci_post_clone.sh */; };
|
||||
B461B852299599F800E431AA /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = B461B851299599F800E431AA /* AppDelegate.mm */; };
|
||||
B47B21EC2B2128B8001F6690 /* BlueWalletUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B47B21EB2B2128B8001F6690 /* BlueWalletUITests.swift */; };
|
||||
@ -438,6 +443,7 @@
|
||||
B44033F82BCC379200162242 /* WidgetDataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDataStore.swift; sourceTree = "<group>"; };
|
||||
B44033FF2BCC37F800162242 /* Bundle+decode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+decode.swift"; sourceTree = "<group>"; };
|
||||
B440340E2BCC40A400162242 /* fiatUnits.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = fiatUnits.json; path = ../../../models/fiatUnits.json; sourceTree = "<group>"; };
|
||||
B450109B2C0FCD8A00619044 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
||||
B4549F352B82B10D002E3153 /* ci_post_clone.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = ci_post_clone.sh; sourceTree = "<group>"; };
|
||||
B461B850299599F800E431AA /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = BlueWallet/AppDelegate.h; sourceTree = "<group>"; };
|
||||
B461B851299599F800E431AA /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = BlueWallet/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
@ -825,6 +831,7 @@
|
||||
B44033C82BCC34AC00162242 /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B450109A2C0FCD7E00619044 /* Utilities */,
|
||||
6D2AA8062568B8E50090B089 /* Fiat */,
|
||||
6D9A2E6A254BAB1B007B5B82 /* MarketAPI.swift */,
|
||||
6D6CA5142558EBA3009312A5 /* MarketAPI+Electrum.swift */,
|
||||
@ -847,6 +854,14 @@
|
||||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B450109A2C0FCD7E00619044 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B450109B2C0FCD8A00619044 /* Utilities.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B4549F2E2B80FEA1002E3153 /* ci_scripts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1505,6 +1520,7 @@
|
||||
B44033DD2BCC36C300162242 /* LatestTransaction.swift in Sources */,
|
||||
6D32C5C62596CE3A008C077C /* EventEmitter.m in Sources */,
|
||||
B44033FE2BCC37D700162242 /* MarketAPI.swift in Sources */,
|
||||
B450109C2C0FCD8A00619044 /* Utilities.swift in Sources */,
|
||||
B44033CE2BCC352900162242 /* UserDefaultsGroup.swift in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
B461B852299599F800E431AA /* AppDelegate.mm in Sources */,
|
||||
@ -1533,6 +1549,7 @@
|
||||
6DD410B4266CAF5C0087DE03 /* MarketAPI.swift in Sources */,
|
||||
B40FC3FA29CCD1D00007EBAC /* SwiftTCPClient.swift in Sources */,
|
||||
6DD410A1266CADF10087DE03 /* Widgets.swift in Sources */,
|
||||
B450109D2C0FCD9F00619044 /* Utilities.swift in Sources */,
|
||||
6DD410AC266CAE470087DE03 /* PriceWidget.swift in Sources */,
|
||||
B4B1A4642BFA73110072E3BB /* WidgetHelper.swift in Sources */,
|
||||
B44033D52BCC368800162242 /* UserDefaultsGroupKey.swift in Sources */,
|
||||
@ -1572,6 +1589,7 @@
|
||||
32F0A29A2311DBB20095C559 /* ComplicationController.swift in Sources */,
|
||||
B40D4E602258425500428FCC /* SpecifyInterfaceController.swift in Sources */,
|
||||
B43D0379225847C500FBAA95 /* WatchDataSource.swift in Sources */,
|
||||
B450109F2C0FCDA500619044 /* Utilities.swift in Sources */,
|
||||
B44033D42BCC368800162242 /* UserDefaultsGroupKey.swift in Sources */,
|
||||
B44034012BCC37F800162242 /* Bundle+decode.swift in Sources */,
|
||||
849047CA2702A32A008EE567 /* Handoff.swift in Sources */,
|
||||
@ -1627,6 +1645,7 @@
|
||||
B44033D62BCC368800162242 /* UserDefaultsGroupKey.swift in Sources */,
|
||||
B44033FD2BCC37D600162242 /* MarketAPI.swift in Sources */,
|
||||
B4A29A2D2B55C990002A67DF /* main.m in Sources */,
|
||||
B45010982C0FCB7700619044 /* MarketAPI+Electrum.swift in Sources */,
|
||||
B4A29A2E2B55C990002A67DF /* AppDelegate.mm in Sources */,
|
||||
B44033C72BCC332400162242 /* Balance.swift in Sources */,
|
||||
B44033FC2BCC379200162242 /* WidgetDataStore.swift in Sources */,
|
||||
@ -1639,6 +1658,7 @@
|
||||
B44033CF2BCC352C00162242 /* UserDefaultsGroup.swift in Sources */,
|
||||
B4A29A2F2B55C990002A67DF /* Bridge.swift in Sources */,
|
||||
B44034042BCC389100162242 /* XMLParserDelegate.swift in Sources */,
|
||||
B450109E2C0FCDA000619044 /* Utilities.swift in Sources */,
|
||||
B44033EC2BCC371A00162242 /* MarketData.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
//
|
||||
// WidgetAPI+Electrum.swift
|
||||
// MarketAPI+Electrum.swift
|
||||
// BlueWallet
|
||||
//
|
||||
// Created by Marcos Rodriguez on 11/8/20.
|
||||
@ -13,83 +13,80 @@ struct APIError: LocalizedError {
|
||||
}
|
||||
|
||||
extension MarketAPI {
|
||||
|
||||
static func fetchNextBlockFee(completion: @escaping ((MarketData?, Error?) -> Void), userElectrumSettings: UserDefaultsElectrumSettings = UserDefaultsGroup.getElectrumSettings()) {
|
||||
let settings = userElectrumSettings
|
||||
let portToUse = settings.sslPort ?? settings.port
|
||||
let isSSLSupported = settings.sslPort != nil
|
||||
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
let client = SwiftTCPClient()
|
||||
|
||||
defer {
|
||||
print("Closing connection to \(String(describing: settings.host)):\(String(describing: portToUse)).")
|
||||
client.close()
|
||||
}
|
||||
|
||||
guard let host = settings.host, let portToUse = portToUse else { return }
|
||||
|
||||
print("Attempting to connect to \(String(describing: settings.host)):\(portToUse) with SSL supported: \(isSSLSupported).")
|
||||
|
||||
if client.connect(to: host, port: UInt32(portToUse), useSSL: isSSLSupported) {
|
||||
print("Successfully connected to \(String(describing: settings.host)):\(portToUse) with SSL:\(isSSLSupported).")
|
||||
} else {
|
||||
print("Failed to connect to \(String(describing: settings.host)):\(portToUse) with SSL:\(isSSLSupported).")
|
||||
completion(nil, APIError())
|
||||
return
|
||||
}
|
||||
|
||||
let message = "{\"id\": 1, \"method\": \"blockchain.estimatefee\", \"params\": [1]}\n"
|
||||
guard let data = message.data(using: .utf8), client.send(data: data) else {
|
||||
print("Message sending failed to \(String(describing: settings.host)):\(portToUse) with SSL supported: \(isSSLSupported).")
|
||||
completion(nil, APIError())
|
||||
return
|
||||
}
|
||||
print("Message sent successfully to \(String(describing: settings.host)):\(portToUse) with SSL:\(isSSLSupported).")
|
||||
static func fetchNextBlockFee(completion: @escaping ((MarketData?, Error?) -> Void), userElectrumSettings: UserDefaultsElectrumSettings = UserDefaultsGroup.getElectrumSettings()) {
|
||||
let settings = userElectrumSettings
|
||||
let portToUse = settings.sslPort ?? settings.port
|
||||
let isSSLSupported = settings.sslPort != nil
|
||||
|
||||
do {
|
||||
let receivedData = try client.receive()
|
||||
print("Data received. Parsing...")
|
||||
guard let responseString = String(data: receivedData, encoding: .utf8),
|
||||
let responseData = responseString.data(using: .utf8),
|
||||
let json = try JSONSerialization.jsonObject(with: responseData, options: .allowFragments) as? [String: AnyObject],
|
||||
let nextBlockResponseDouble = json["result"] as? Double else {
|
||||
print("Failed to parse response from \(String(describing: settings.host)).")
|
||||
completion(nil, APIError())
|
||||
return
|
||||
}
|
||||
|
||||
print("Response: \(json)")
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
let client = SwiftTCPClient()
|
||||
|
||||
print("Response from \(String(describing: settings.host)) parsed successfully.")
|
||||
let marketData = MarketData(nextBlock: String(format: "%.0f", (nextBlockResponseDouble / 1024) * 100000000), sats: "0", price: "0", rate: 0)
|
||||
completion(marketData, nil) // Successfully fetched data, return it
|
||||
} catch {
|
||||
print("Error receiving data from \(String(describing: settings.host)): \(error.localizedDescription)")
|
||||
completion(nil, APIError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func fetchMarketData(currency: String, completion: @escaping ((MarketData?, Error?) -> Void)) {
|
||||
var marketDataEntry = MarketData(nextBlock: "...", sats: "...", price: "...", rate: 0)
|
||||
MarketAPI.fetchPrice(currency: currency, completion: { (result, error) in
|
||||
if let result = result {
|
||||
marketDataEntry.rate = result.rateDouble
|
||||
marketDataEntry.price = result.formattedRate ?? "!"
|
||||
}
|
||||
MarketAPI.fetchNextBlockFee { (marketData, error) in
|
||||
if let nextBlock = marketData?.nextBlock {
|
||||
marketDataEntry.nextBlock = nextBlock
|
||||
} else {
|
||||
marketDataEntry.nextBlock = "!"
|
||||
defer {
|
||||
print("Closing connection to \(String(describing: settings.host)):\(String(describing: portToUse)).")
|
||||
client.close()
|
||||
}
|
||||
|
||||
guard let host = settings.host, let portToUse = portToUse else { return }
|
||||
|
||||
print("Attempting to connect to \(String(describing: settings.host)):\(portToUse) with SSL supported: \(isSSLSupported).")
|
||||
|
||||
if client.connect(to: host, port: UInt32(portToUse), useSSL: isSSLSupported) {
|
||||
print("Successfully connected to \(String(describing: settings.host)):\(portToUse) with SSL:\(isSSLSupported).")
|
||||
} else {
|
||||
print("Failed to connect to \(String(describing: settings.host)):\(portToUse) with SSL:\(isSSLSupported).")
|
||||
completion(nil, APIError())
|
||||
return
|
||||
}
|
||||
|
||||
let message = "{\"id\": 1, \"method\": \"mempool.get_fee_histogram\", \"params\": []}\n"
|
||||
guard let data = message.data(using: .utf8), client.send(data: data) else {
|
||||
print("Message sending failed to \(String(describing: settings.host)):\(portToUse) with SSL supported: \(isSSLSupported).")
|
||||
completion(nil, APIError())
|
||||
return
|
||||
}
|
||||
print("Message sent successfully to \(String(describing: settings.host)):\(portToUse) with SSL:\(isSSLSupported).")
|
||||
|
||||
do {
|
||||
let receivedData = try client.receive()
|
||||
print("Data received. Parsing...")
|
||||
guard let responseString = String(data: receivedData, encoding: .utf8),
|
||||
let responseData = responseString.data(using: .utf8),
|
||||
let json = try JSONSerialization.jsonObject(with: responseData, options: .allowFragments) as? [String: AnyObject],
|
||||
let feeHistogram = json["result"] as? [[Double]] else {
|
||||
print("Failed to parse response from \(String(describing: settings.host)).")
|
||||
completion(nil, APIError())
|
||||
return
|
||||
}
|
||||
|
||||
let fastestFee = calcEstimateFeeFromFeeHistogram(numberOfBlocks: 1, feeHistogram: feeHistogram)
|
||||
let marketData = MarketData(nextBlock: String(format: "%.0f", fastestFee), sats: "0", price: "0", rate: 0)
|
||||
completion(marketData, nil) // Successfully fetched data, return it
|
||||
} catch {
|
||||
print("Error receiving data from \(String(describing: settings.host)): \(error.localizedDescription)")
|
||||
completion(nil, APIError())
|
||||
}
|
||||
}
|
||||
if let rateDouble = result?.rateDouble {
|
||||
marketDataEntry.sats = numberFormatter.string(from: NSNumber(value: Double(10 / rateDouble) * 10000000)) ?? "!"
|
||||
}
|
||||
completion(marketDataEntry, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static func fetchMarketData(currency: String, completion: @escaping ((MarketData?, Error?) -> Void)) {
|
||||
var marketDataEntry = MarketData(nextBlock: "...", sats: "...", price: "...", rate: 0)
|
||||
MarketAPI.fetchPrice(currency: currency, completion: { (result, error) in
|
||||
if let result = result {
|
||||
marketDataEntry.rate = result.rateDouble
|
||||
marketDataEntry.price = result.formattedRate ?? "!"
|
||||
}
|
||||
MarketAPI.fetchNextBlockFee { (marketData, error) in
|
||||
if let nextBlock = marketData?.nextBlock {
|
||||
marketDataEntry.nextBlock = nextBlock
|
||||
} else {
|
||||
marketDataEntry.nextBlock = "!"
|
||||
}
|
||||
if let rateDouble = result?.rateDouble {
|
||||
marketDataEntry.sats = numberFormatter.string(from: NSNumber(value: Double(10 / rateDouble) * 10000000)) ?? "!"
|
||||
}
|
||||
completion(marketDataEntry, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -128,17 +128,22 @@ class MarketAPI {
|
||||
completion(nil, CurrencyError(errorDescription: "BNR data source is not yet implemented"))
|
||||
|
||||
case "Kraken":
|
||||
if let result = json["result"] as? [String: Any],
|
||||
let tickerData = result["XXBTZ\(endPointKey.uppercased())"] as? [String: Any],
|
||||
let c = tickerData["c"] as? [String],
|
||||
let rateString = c.first,
|
||||
let rateDouble = Double(rateString) {
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date())
|
||||
latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
completion(latestRateDataStore, nil)
|
||||
} else {
|
||||
completion(nil, CurrencyError(errorDescription: "Data formatting error for source: \(source)"))
|
||||
}
|
||||
if let result = json["result"] as? [String: Any],
|
||||
let tickerData = result["XXBTZ\(endPointKey.uppercased())"] as? [String: Any],
|
||||
let c = tickerData["c"] as? [String],
|
||||
let rateString = c.first,
|
||||
let rateDouble = Double(rateString) {
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date())
|
||||
latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
completion(latestRateDataStore, nil)
|
||||
} else {
|
||||
if let errorMessage = json["error"] as? [String] {
|
||||
completion(nil, CurrencyError(errorDescription: "Kraken API error: \(errorMessage.joined(separator: ", "))"))
|
||||
} else {
|
||||
completion(nil, CurrencyError(errorDescription: "Data formatting error for source: \(source)"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
completion(nil, CurrencyError(errorDescription: "Unsupported data source \(source)"))
|
||||
@ -178,30 +183,47 @@ class MarketAPI {
|
||||
}
|
||||
|
||||
static func fetchPrice(currency: String, completion: @escaping ((WidgetDataStore?, Error?) -> Void)) {
|
||||
let currencyToFiatUnit = fiatUnit(currency: currency)
|
||||
guard let source = currencyToFiatUnit?.source, let endPointKey = currencyToFiatUnit?.endPointKey else {
|
||||
completion(nil, CurrencyError(errorDescription: "Invalid currency unit or endpoint."))
|
||||
return
|
||||
}
|
||||
let currencyToFiatUnit = fiatUnit(currency: currency)
|
||||
guard let source = currencyToFiatUnit?.source, let endPointKey = currencyToFiatUnit?.endPointKey else {
|
||||
completion(nil, CurrencyError(errorDescription: "Invalid currency unit or endpoint."))
|
||||
return
|
||||
}
|
||||
|
||||
let urlString = buildURLString(source: source, endPointKey: endPointKey)
|
||||
guard let url = URL(string: urlString) else {
|
||||
completion(nil, CurrencyError(errorDescription: "Invalid URL."))
|
||||
return
|
||||
}
|
||||
let urlString = buildURLString(source: source, endPointKey: endPointKey)
|
||||
guard let url = URL(string: urlString) else {
|
||||
completion(nil, CurrencyError(errorDescription: "Invalid URL."))
|
||||
return
|
||||
}
|
||||
|
||||
URLSession.shared.dataTask(with: url) { data, response, error in
|
||||
guard let data = data, error == nil else {
|
||||
completion(nil, error ?? CurrencyError(errorDescription: "Network error or data not found."))
|
||||
return
|
||||
}
|
||||
fetchData(url: url, source: source, endPointKey: endPointKey, completion: completion)
|
||||
}
|
||||
|
||||
if source == "BNR" {
|
||||
handleBNRData(data: data, completion: completion)
|
||||
} else {
|
||||
handleDefaultData(data: data, source: source, endPointKey: endPointKey, completion: completion)
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
private static func fetchData(url: URL, source: String, endPointKey: String, retries: Int = 3, completion: @escaping ((WidgetDataStore?, Error?) -> Void)) {
|
||||
URLSession.shared.dataTask(with: url) { data, response, error in
|
||||
if let error = error {
|
||||
if retries > 0 {
|
||||
fetchData(url: url, source: source, endPointKey: endPointKey, retries: retries - 1, completion: completion)
|
||||
} else {
|
||||
completion(nil, error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
if retries > 0 {
|
||||
fetchData(url: url, source: source, endPointKey: endPointKey, retries: retries - 1, completion: completion)
|
||||
} else {
|
||||
completion(nil, CurrencyError(errorDescription: "Data not found."))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if source == "BNR" {
|
||||
handleBNRData(data: data, completion: completion)
|
||||
} else {
|
||||
handleDefaultData(data: data, source: source, endPointKey: endPointKey, completion: completion)
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
}
|
||||
|
53
ios/Shared/Utilities/Utilities.swift
Normal file
53
ios/Shared/Utilities/Utilities.swift
Normal file
@ -0,0 +1,53 @@
|
||||
//
|
||||
// Utilities.swift
|
||||
// BlueWallet
|
||||
//
|
||||
// Created by Marcos Rodriguez on 6/4/24.
|
||||
// Copyright © 2024 BlueWallet. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
func percentile(_ arr: [Double], p: Double) -> Double {
|
||||
guard !arr.isEmpty else { return 0 }
|
||||
guard p >= 0, p <= 1 else { fatalError("Percentile must be between 0 and 1") }
|
||||
|
||||
if p == 0 { return arr.first! }
|
||||
if p == 1 { return arr.last! }
|
||||
|
||||
let index = Double(arr.count - 1) * p
|
||||
let lower = Int(floor(index))
|
||||
let upper = lower + 1
|
||||
let weight = index - Double(lower)
|
||||
|
||||
if upper >= arr.count { return arr[lower] }
|
||||
return arr[lower] * (1 - weight) + arr[upper] * weight
|
||||
}
|
||||
|
||||
func calcEstimateFeeFromFeeHistogram(numberOfBlocks: Int, feeHistogram: [[Double]]) -> Double {
|
||||
var totalVsize = 0.0
|
||||
var histogramToUse: [(fee: Double, vsize: Double)] = []
|
||||
|
||||
for h in feeHistogram {
|
||||
var (fee, vsize) = (h[0], h[1])
|
||||
var timeToStop = false
|
||||
|
||||
if totalVsize + vsize >= 1000000.0 * Double(numberOfBlocks) {
|
||||
vsize = 1000000.0 * Double(numberOfBlocks) - totalVsize
|
||||
timeToStop = true
|
||||
}
|
||||
|
||||
histogramToUse.append((fee, vsize))
|
||||
totalVsize += vsize
|
||||
if timeToStop { break }
|
||||
}
|
||||
|
||||
var histogramFlat: [Double] = []
|
||||
for hh in histogramToUse {
|
||||
histogramFlat += Array(repeating: hh.fee, count: Int(hh.vsize / 25000))
|
||||
}
|
||||
|
||||
histogramFlat.sort()
|
||||
|
||||
return max(2, percentile(histogramFlat, p: 0.5))
|
||||
}
|
Loading…
Reference in New Issue
Block a user