From c72efae02849c3fc4ac5eccfff70d9418646fe38 Mon Sep 17 00:00:00 2001 From: rommex Date: Wed, 3 Apr 2024 11:11:26 +0300 Subject: [PATCH] MOB-1933 Fetch gas prices from Infura (#471) * initted * refactoring * refact * Update CryptoSenderProtocol.swift * added hash property into description * rm StoredTx * create TX also extracting JRPC methods * refactor protocol * extracting sendTx from WC2 * extracted fetchGasLimit() * added working demo with Sepolia * factor in amount and chain env * fixed amount computation * helper init * check support of native tokens * take use of default gas price * added TxSpeed, top-level CryptoSender * computeGasFeeFrom() finalyzed * refactoring * refact * refactor * support ext wallets * transferring chain to ext wallet * ext wallet fix * fetching range of gas prices * Update navigation title * Will use domain name for wallet on receiver selection screen if available * new enum SupportedToken * default body nil * removed double-checking * removed repeated code * refactored computeGasPrice() * computed gas fee in token units (Double) * use speed-based gas price * Fixed preview target * Implemented use max crypto to send calculations. Updated show pull up logic * Filter tokens by can send * Refactoring * Created bridge functions to send crypto in view model * Added ui entity for transaction speed * Fetch gas fee and display on the UI. * Updated gas fee price formatting * Updated token formatting and saved value * Refactoring * Restore limited scope of requested porfolio data * Remove speed estimation in sec from UI * Run refresh gas timer * Submit send crypto * introduced EVMTokenAmount, extra helper method fetchGasPrices() * fix: Eth Tx is build with WEIs only * refactoring * Update gas fee and prices calculations * renaming (#452) * Fix merging issue * Gas fee and gas prices refactoring * Fixed gas prices preserved and refactoring * Handle send all tokens * Check for user's fund is enough to cover gas before sending. * Force refresh wallet update * Add biometric verification to confirm send transaction. * Updated using max button title * Added crypto views lifecycle analytic events * Fixed typo * Added analytic events for scan qr code and select receiver views * Added analytics to asset selection and token input views * Added analytics to transfer domain view * Added analytics to send crypto view * Added analytics to success screen * Log send asset error * Refactoring * Refactoring * Updated and localized tx list items * Adjust UI for long values in tx list * Fixed targets * Extend tx id * Updated tx icons ui * bump version to 5.3.0(1) * added fetchInfuraGasPrices() * refactoring * rm auth key --------- Co-authored-by: Oleg --- .../CryptoSender/CryptoSender.swift | 17 +++++++--- .../Services/Networking/NetworkService.swift | 31 +++++++++++++++++-- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift index a1ba5979c..2b5364e16 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift @@ -96,9 +96,9 @@ struct NativeCryptoSender: CryptoSenderProtocol { } func fetchGasPrices(on chain: ChainSpec) async throws -> EstimatedGasPrices { - try await NetworkService().getStatusGasPrices(chainId: chain.id) + try await fetchGasPrices(chainId: chain.id) } - + // Private methods private func createNativeSendTransaction(crypto: CryptoSendingSpec, @@ -107,8 +107,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { chainId: Int) async throws -> EthereumTransaction { let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, chainId: chainId) - let speedBasedGasPrice = try await NetworkService().fetchGasPrice(chainId: chainId, - for: crypto.speed) + let speedBasedGasPrice = try await fetchGasPrice(chainId: chainId, for: crypto.speed) let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) @@ -126,4 +125,14 @@ struct NativeCryptoSender: CryptoSenderProtocol { } return transaction } + + private func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { + let prices: EstimatedGasPrices = try await fetchGasPrices(chainId: chainId) + return prices.getPriceForSpeed(speed) + } + + private func fetchGasPrices(chainId: Int) async throws -> EstimatedGasPrices { + // here routes to Status or Infura source + try await NetworkService().fetchInfuraGasPrices(chainId: chainId) + } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 1c54cacd6..b4698ec08 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -316,6 +316,7 @@ extension NetworkService { case genericError(String) case failedGetStatus case failedParseStatusPrices + case failedParseInfuraPrices case unknownChain init(message: String) { @@ -385,9 +386,33 @@ extension NetworkService { return ["ETH": ethPrices, "MATIC": maticPrices] } - func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { - let prices: EstimatedGasPrices = try await getStatusGasPrices(chainId: chainId) - return prices.getPriceForSpeed(speed) + enum InfuraSpeedCase: String, CaseIterable { + case low, medium, high + } + + func fetchInfuraGasPrices(chain: ChainSpec) async throws -> EstimatedGasPrices { + try await fetchInfuraGasPrices(chainId: chain.id) + } + + func fetchInfuraGasPrices(chainId: Int) async throws -> EstimatedGasPrices { + let url = URL(string: "https://gas.api.infura.io/networks/\(chainId)/suggestedGasFees")! + let data = try await NetworkService().fetchData(for: url, method: .get, extraHeaders: Self.infuraBasicAuthHeader) + let jsonInf = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] + + var priceDict: [InfuraSpeedCase: Double] = [:] + try Self.InfuraSpeedCase.allCases.forEach { + guard let section = jsonInf[$0.rawValue] as? [String: Any], + let priceString = section["suggestedMaxFeePerGas"] as? String, + let price = Double(priceString) else { + throw JRPCError.failedParseInfuraPrices + } + priceDict[$0] = price + } + assert(priceDict.count == Self.InfuraSpeedCase.allCases.count) // always true after forEach + + return EstimatedGasPrices(normal: EVMTokenAmount(gwei: priceDict[InfuraSpeedCase.low]!), + fast: EVMTokenAmount(gwei: priceDict[InfuraSpeedCase.medium]!), + urgent: EVMTokenAmount(gwei: priceDict[InfuraSpeedCase.high]!)) } func getStatusGasPrices(chainId: Int) async throws -> EstimatedGasPrices {