Merge pull request #3257 from vladyslav-iosdev/#3250

Switch from Gasnow to Etherscan for gas price estimates #3250
pull/3263/head
Hwee-Boon Yar 3 years ago committed by GitHub
commit 77a0884bf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 40
      AlphaWallet/Gas/Models/GasNowGasPriceEstimator.swift
  2. 32
      AlphaWallet/Gas/Models/GasNowPriceEstimates.swift
  3. 2
      AlphaWallet/Settings/Types/RPCServers.swift
  4. 26
      AlphaWallet/Transfer/Controllers/TransactionConfigurator.swift
  5. 4
      AlphaWallet/Transfer/ViewControllers/ConfigureTransactionViewController.swift

@ -2,6 +2,7 @@
import Foundation
import PromiseKit
import Alamofire
class GasNowGasPriceEstimator {
func fetch() -> Promise<GasNowPriceEstimates> {
@ -11,3 +12,42 @@ class GasNowGasPriceEstimator {
}
}
}
class EtherscanGasPriceEstimator {
struct EtherscanPriceEstimatesResponse: Decodable {
let result: EtherscanPriceEstimates
}
static func supports(server: RPCServer) -> Bool {
return server.etherscanGasPriceEstimatesURL != nil
}
func fetch(server: RPCServer) -> Promise<GasNowPriceEstimates> {
struct AnyError: Error {}
guard let url = server.etherscanGasPriceEstimatesURL else {
return .init(error: AnyError())
}
return Alamofire.request(url, method: .get).responseDecodable(EtherscanPriceEstimatesResponse.self).compactMap { response in
EtherscanPriceEstimates.bridgeToGasNowPriceEstimates(for: response.result)
}
}
}
fileprivate extension RPCServer {
var etherscanGasPriceEstimatesURL: URL? {
let apiKeyParameter: String
if let apiKey = etherscanApiKey {
apiKeyParameter = "&apikey=\(apiKey)"
} else {
apiKeyParameter = ""
}
switch self {
case .main, .kovan, .ropsten, .rinkeby, .goerli, .binance_smart_chain, .heco, .polygon, .optimistic, .optimisticKovan:
return etherscanApiRoot?.appendingQueryString("\("module=gastracker&action=gasoracle")\(apiKeyParameter)")
case .artis_sigma1, .artis_tau1, .binance_smart_chain_testnet, .callisto, .poa, .sokol, .classic, .xDai, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .mumbai_testnet, .cronosTestnet, .custom:
return nil
}
}
}

@ -38,3 +38,35 @@ struct GasNowPriceEstimates: Decodable {
case code
}
}
extension EtherscanPriceEstimates {
/// Current label in UI Key to pull gas price from
/// - Rapid "FastGasPrice" * 1.2
/// - Fast "FastGasPrice"
/// - Standard/Average "ProposeGasPrice"
/// - Slow "SafeGasPrice"
static func bridgeToGasNowPriceEstimates(for value: EtherscanPriceEstimates) -> GasNowPriceEstimates? {
let slow = EtherNumberFormatter.full.number(from: value.safeGasPrice, units: UnitConfiguration.gasPriceUnit)!
let standard = EtherNumberFormatter.full.number(from: value.proposeGasPrice, units: UnitConfiguration.gasPriceUnit)!
let fastGasPrice = EtherNumberFormatter.full.number(from: value.fastGasPrice, units: UnitConfiguration.gasPriceUnit)!
guard let slow = Int(slow.description), let standard = Int(standard.description), let fast = Int(fastGasPrice.description) else { return nil }
let data = GasNowPriceEstimates.Data(slow: slow, standard: standard, fast: fast, rapid: Int((Double(fast) * 1.2).rounded(.down)))
return GasNowPriceEstimates(data: data, code: 1)
}
}
struct EtherscanPriceEstimates: Decodable {
enum CodingKeys: String, CodingKey {
case fastGasPrice = "FastGasPrice"
case proposeGasPrice = "ProposeGasPrice"
case safeGasPrice = "SafeGasPrice"
case suggestBaseFee = "suggestBaseFee"
}
let fastGasPrice: String
let proposeGasPrice: String
let safeGasPrice: String
let suggestBaseFee: String
}

@ -807,7 +807,7 @@ extension RPCServer: Codable {
}
}
fileprivate extension URL {
extension URL {
//Much better to use URLComponents, but this is much simpler for our use. This probably doesn't percent-escape probably, but we shouldn't need it for the URLs we access here
func appendingQueryString(_ queryString: String) -> URL? {
let urlString = absoluteString

@ -209,24 +209,24 @@ class TransactionConfigurator {
}
static func estimateGasPrice(server: RPCServer) -> Promise<GasEstimates> {
switch server {
case .main:
return firstly {
estimateGasPriceForEthMainnetUsingThirdPartyApi()
}.recover { _ in
if EtherscanGasPriceEstimator.supports(server: server) {
return estimateGasPriceForUsingEtherscanApi(server: server).recover { _ in
estimateGasPriceForUseRpcNode(server: server)
}
} else {
switch server {
case .xDai:
return estimateGasPriceForXDai()
case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet:
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet:
return Promise(estimateGasPriceForUseRpcNode(server: server))
}
}
}
private static func estimateGasPriceForEthMainnetUsingThirdPartyApi() -> Promise<GasEstimates> {
let estimator = GasNowGasPriceEstimator()
private static func estimateGasPriceForUsingEtherscanApi(server: RPCServer) -> Promise<GasEstimates> {
let estimator = EtherscanGasPriceEstimator()
return firstly {
estimator.fetch()
estimator.fetch(server: server)
}.map { estimates in
GasEstimates(standard: BigInt(estimates.standard), others: [
TransactionConfigurationType.slow: BigInt(estimates.slow),
@ -290,9 +290,11 @@ class TransactionConfigurator {
}
switch session.server {
case .main:
if (configurations.standard.gasPrice / BigInt(EthereumUnit.gwei.rawValue)) > Constants.highStandardGasThresholdGwei {
return .networkCongested
}
//NOTE: Suppose that these lines were related to GasNow
//if (configurations.standard.gasPrice / BigInt(EthereumUnit.gwei.rawValue)) > Constants.highStandardGasThresholdGwei {
// return .networkCongested
//}
break
case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .fantom, .fantom_testnet, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet:
break
}

@ -240,9 +240,7 @@ class ConfigureTransactionViewController: UIViewController {
warningIcon.widthAnchor.constraint(equalToConstant: 24),
warningIcon.widthAnchor.constraint(equalTo: warningIcon.heightAnchor),
descriptionLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -50),
descriptionLabel.widthAnchor.constraint(equalToConstant: 250),
descriptionLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -50)
])
var frame = footer.frame

Loading…
Cancel
Save