BlueWallet/ios/Shared/MarketAPI+Electrum.swift
2024-11-20 03:49:52 -04:00

101 lines
4.7 KiB
Swift

//
// MarketAPI+Electrum.swift
// BlueWallet
//
// Created by Marcos Rodriguez on 11/8/20.
// Copyright © 2020 BlueWallet. All rights reserved.
//
import Foundation
struct APIError: LocalizedError {
var errorDescription: String = "Failed to fetch Electrum data..."
}
extension MarketAPI {
static func fetchNextBlockFee(completion: @escaping ((MarketData?, Error?) -> Void), userElectrumSettings: UserDefaultsElectrumSettings = UserDefaultsGroup.getElectrumSettings(), retries: Int = 0) {
Task.detached {
let client = SwiftTCPClient()
defer {
print("Closing connection to \(userElectrumSettings.host ?? "unknown"):\(userElectrumSettings.sslPort ?? userElectrumSettings.port ?? 0).")
client.close()
}
guard let host = userElectrumSettings.host, let portToUse = userElectrumSettings.sslPort ?? userElectrumSettings.port else {
completion(nil, APIError())
return
}
let isSSLSupported = userElectrumSettings.sslPort != nil
print("Attempting to connect to \(host):\(portToUse) with SSL supported: \(isSSLSupported).")
let connected = await client.connect(to: host, port: portToUse, useSSL: isSSLSupported)
if connected {
print("Successfully connected to \(host):\(portToUse) with SSL: \(isSSLSupported).")
} else {
print("Failed to connect to \(host):\(portToUse) with SSL: \(isSSLSupported).")
if retries < client.maxRetries {
print("Retrying fetchNextBlockFee (\(retries + 1)/\(client.maxRetries))...")
fetchNextBlockFee(completion: completion, userElectrumSettings: userElectrumSettings, retries: retries + 1)
} else {
completion(nil, APIError())
}
return
}
let message = "{\"id\": 1, \"method\": \"mempool.get_fee_histogram\", \"params\": []}\n"
guard let data = message.data(using: .utf8), await client.send(data: data) else {
print("Message sending failed to \(host):\(portToUse) with SSL supported: \(isSSLSupported).")
completion(nil, APIError())
return
}
print("Message sent successfully to \(host):\(portToUse) with SSL: \(isSSLSupported).")
do {
let receivedData = try await client.receive()
print("Data received. Parsing...")
guard let json = try JSONSerialization.jsonObject(with: receivedData, options: .allowFragments) as? [String: AnyObject],
let feeHistogram = json["result"] as? [[Double]] else {
print("Failed to parse response from \(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, dateString: "")
print("Parsed MarketData: \(marketData)")
completion(marketData, nil)
} catch {
print("Error receiving data from \(host): \(error.localizedDescription)")
if retries < client.maxRetries {
print("Retrying fetchNextBlockFee (\(retries + 1)/\(client.maxRetries))...")
fetchNextBlockFee(completion: completion, userElectrumSettings: userElectrumSettings, retries: retries + 1)
} else {
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 = "!"
}
if let rateDouble = result?.rateDouble {
marketDataEntry.sats = numberFormatter.string(from: NSNumber(value: Double(10 / rateDouble) * 10000000)) ?? "!"
}
completion(marketDataEntry, nil)
}
})
}
}