From a02ea5daa89e2a42af124184d18dc59c70c96717 Mon Sep 17 00:00:00 2001 From: Esenbek Date: Mon, 7 Aug 2023 14:51:03 +0600 Subject: [PATCH] Add minimum fee rate to BTC fee settings --- .../project.pbxproj | 14 +++--- .../UnstoppableWallet/Core/Protocols.swift | 7 +-- .../Core/Providers/AppConfig.swift | 2 +- .../Core/Providers/FeeRateProvider.swift | 49 ++++++++++--------- ...el.swift => SendFeeCautionViewModel.swift} | 23 +++++---- .../Bitcoin/SendBitcoinAdapterService.swift | 11 +++-- .../Bitcoin/SendBitcoinFactory.swift | 6 +-- .../Bitcoin/SendBitcoinViewController.swift | 16 +++--- .../Modules/Send/Platforms/SendModule.swift | 8 +-- .../Settings/FeeRate/FeeRateDataSource.swift | 10 ++-- .../Settings/FeeRate/FeeRateService.swift | 11 +++-- .../Settings/FeeRate/FeeRateViewModel.swift | 13 +++-- .../Settings/SendSettingsViewController.swift | 3 +- .../en.lproj/Localizable.strings | 3 ++ 14 files changed, 92 insertions(+), 84 deletions(-) rename UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/FeeWarning/{SendFeeWarningViewModel.swift => SendFeeCautionViewModel.swift} (50%) diff --git a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj index 8d28f4a81e..925eb1be61 100644 --- a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj +++ b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj @@ -1455,7 +1455,7 @@ 2FA5D1897F90F77E6D9076AB /* LegacyEvmFeeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D735675BD26B75B7FFC7 /* LegacyEvmFeeDataSource.swift */; }; 2FA5D18A57B386FD3A4384BA /* Eip1559EvmFeeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5DEDF215D171796AA34FE /* Eip1559EvmFeeViewModel.swift */; }; 2FA5D1A368FEA07EA308A94A /* BitcoinIncomingTransactionRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5DE3FC4EA4B42BC982C9A /* BitcoinIncomingTransactionRecord.swift */; }; - 2FA5D1E33A714D64DDAFA119 /* SendFeeWarningViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D5CBE81863EDADA1D47D /* SendFeeWarningViewModel.swift */; }; + 2FA5D1E33A714D64DDAFA119 /* SendFeeCautionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D5CBE81863EDADA1D47D /* SendFeeCautionViewModel.swift */; }; 2FA5D1F6979A345597788DE9 /* NonceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D64286A249D6D106DDFC /* NonceService.swift */; }; 2FA5D201AED8FB83968A5220 /* StepperAmountInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D5A6EDE4A729F3DFF626 /* StepperAmountInputView.swift */; }; 2FA5D20BC28280786A0FC3FE /* DropDownListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5DB591688EEA6116C8323 /* DropDownListCell.swift */; }; @@ -1467,7 +1467,7 @@ 2FA5D2FA6FE5298D991A7248 /* FeeRateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D96AE7C5E97B6E609CA5 /* FeeRateService.swift */; }; 2FA5D2FE81DA1FB5A63C2D7C /* BitcoinCore+Hodler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D33978B54432713B047C /* BitcoinCore+Hodler.swift */; }; 2FA5D342449885CF8D58B704 /* HdWalletExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5DA265C9608D0BCF88F17 /* HdWalletExtensions.swift */; }; - 2FA5D37B3AD06F8249FA7959 /* SendFeeWarningViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D5CBE81863EDADA1D47D /* SendFeeWarningViewModel.swift */; }; + 2FA5D37B3AD06F8249FA7959 /* SendFeeCautionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D5CBE81863EDADA1D47D /* SendFeeCautionViewModel.swift */; }; 2FA5D3893C3B6F2270F857C7 /* SendEvmCautionsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5DD57DA8B88537341459E /* SendEvmCautionsFactory.swift */; }; 2FA5D3AFFBE0E51DD79236EF /* LegacyEvmFeeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D735675BD26B75B7FFC7 /* LegacyEvmFeeDataSource.swift */; }; 2FA5D3C3D3AB41202B592640 /* Eip1559EvmFeeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D5BC7249DDB680769ADD /* Eip1559EvmFeeDataSource.swift */; }; @@ -3384,7 +3384,7 @@ 2FA5D5A6EDE4A729F3DFF626 /* StepperAmountInputView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StepperAmountInputView.swift; sourceTree = ""; }; 2FA5D5B2E57E92B6EF057356 /* FeeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeeCell.swift; sourceTree = ""; }; 2FA5D5BC7249DDB680769ADD /* Eip1559EvmFeeDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Eip1559EvmFeeDataSource.swift; sourceTree = ""; }; - 2FA5D5CBE81863EDADA1D47D /* SendFeeWarningViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendFeeWarningViewModel.swift; sourceTree = ""; }; + 2FA5D5CBE81863EDADA1D47D /* SendFeeCautionViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendFeeCautionViewModel.swift; sourceTree = ""; }; 2FA5D64286A249D6D106DDFC /* NonceService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonceService.swift; sourceTree = ""; }; 2FA5D6522540716BACE4AD09 /* InputOutputOrderViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputOutputOrderViewModel.swift; sourceTree = ""; }; 2FA5D657FD86FE9B675B475D /* TransactionInfoViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionInfoViewModel.swift; sourceTree = ""; }; @@ -5994,7 +5994,7 @@ 2FA5D752137F74F62C01383E /* FeeWarning */ = { isa = PBXGroup; children = ( - 2FA5D5CBE81863EDADA1D47D /* SendFeeWarningViewModel.swift */, + 2FA5D5CBE81863EDADA1D47D /* SendFeeCautionViewModel.swift */, 2FA5D8546E264BEEE654D17B /* SendFeeSettingsAmountCautionViewModel.swift */, ); path = FeeWarning; @@ -8808,7 +8808,7 @@ 2FA5DF42271E59C1CE40B5DB /* FeeRateViewModel.swift in Sources */, 2FA5D2FA6FE5298D991A7248 /* FeeRateService.swift in Sources */, 2FA5DF8F364E6B5BED14C0B4 /* SendSettingsViewController.swift in Sources */, - 2FA5D1E33A714D64DDAFA119 /* SendFeeWarningViewModel.swift in Sources */, + 2FA5D1E33A714D64DDAFA119 /* SendFeeCautionViewModel.swift in Sources */, 2FA5DCCE2273577F8F12004F /* SendFeeSettingsAmountCautionViewModel.swift in Sources */, 2FA5D7C2BEF5739328BA8239 /* TimeLockDataSource.swift in Sources */, 2FA5DE46C61524144F406FDB /* TimeLockService.swift in Sources */, @@ -10065,7 +10065,7 @@ 2FA5DF43F20E03E94403555D /* FeeRateViewModel.swift in Sources */, 2FA5DF7570C7D9E33922CB03 /* FeeRateService.swift in Sources */, 2FA5DD05ECC5501DC33050C6 /* SendSettingsViewController.swift in Sources */, - 2FA5D37B3AD06F8249FA7959 /* SendFeeWarningViewModel.swift in Sources */, + 2FA5D37B3AD06F8249FA7959 /* SendFeeCautionViewModel.swift in Sources */, 2FA5D7CC1617FDF8D2EAFB56 /* SendFeeSettingsAmountCautionViewModel.swift in Sources */, 2FA5DA3E8B501D515A25E8A2 /* TimeLockDataSource.swift in Sources */, 2FA5D4AB0DEF37A103E1E130 /* TimeLockService.swift in Sources */, @@ -10718,7 +10718,7 @@ repositoryURL = "https://github.com/horizontalsystems/FeeRateKit.Swift"; requirement = { kind = exactVersion; - version = 2.0.0; + version = 2.1.0; }; }; D3604E8328F03CDC0066C366 /* XCRemoteSwiftPackageReference "BinanceChainKit.Swift" */ = { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift b/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift index 6d94af44d5..aa76d1bd21 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift @@ -129,12 +129,7 @@ protocol INftEventProvider { } protocol IFeeRateProvider { - func recommendedFeeRate() async throws -> Int -} - -protocol ICustomRangedFeeRateProvider: IFeeRateProvider { - var customFeeRange: ClosedRange { get } - var step: Int { get } + func feeRates() async throws -> FeeRateProvider.FeeRates } protocol IAppManager { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Providers/AppConfig.swift b/UnstoppableWallet/UnstoppableWallet/Core/Providers/AppConfig.swift index f0be470f83..cbe4d444bf 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Providers/AppConfig.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Providers/AppConfig.swift @@ -14,7 +14,7 @@ struct AppConfig { static let appTwitterAccount = "unstoppablebyhs" static let appTelegramAccount = "unstoppable_announcements" static let appRedditAccount = "UNSTOPPABLEWallet" - static let btcCoreRpcUrl = "https://btc.blocksdecoded.com/rpc" + static let mempoolSpaceUrl = "https://mempool.space" static let guidesIndexUrl = URL(string: "https://raw.githubusercontent.com/horizontalsystems/blockchain-crypto-guides/v1.2/index.json")! static let faqIndexUrl = URL(string: "https://raw.githubusercontent.com/horizontalsystems/unstoppable-wallet-website/master/src/faq.json")! diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Providers/FeeRateProvider.swift b/UnstoppableWallet/UnstoppableWallet/Core/Providers/FeeRateProvider.swift index b9e6dab8f8..c745c93d2f 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Providers/FeeRateProvider.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Providers/FeeRateProvider.swift @@ -10,9 +10,7 @@ class FeeRateProvider { ethEvmUrl: FeeProviderConfig.infuraUrl(projectId: AppConfig.infuraCredentials.id), ethEvmAuth: AppConfig.infuraCredentials.secret, bscEvmUrl: FeeProviderConfig.defaultBscEvmUrl, - btcCoreRpcUrl: AppConfig.btcCoreRpcUrl, - btcCoreRpcUser: nil, - btcCoreRpcPassword: nil + mempoolSpaceUrl: AppConfig.mempoolSpaceUrl ) feeRateKit = FeeRateKit.Kit.instance(providerConfig: providerConfig, minLogLevel: .error) } @@ -27,38 +25,43 @@ class FeeRateProvider { // feeRateKit.binanceSmartChain // } - var litecoinFeeRate: Int { + fileprivate var litecoinFeeRate: Int { feeRateKit.litecoin } - var bitcoinCashFeeRate: Int { + fileprivate var bitcoinCashFeeRate: Int { feeRateKit.bitcoinCash } - var dashFeeRate: Int { + fileprivate var dashFeeRate: Int { feeRateKit.dash } - func bitcoinFeeRate(blockCount: Int) async throws -> Int { - try await feeRateKit.bitcoin(blockCount: blockCount) + fileprivate func bitcoinFeeRate() async throws -> MempoolSpaceProvider.RecommendedFees { + try await feeRateKit.bitcoin() } } -class BitcoinFeeRateProvider: ICustomRangedFeeRateProvider { - static let defaultFeeRange: ClosedRange = 1...200 - let customFeeRange: ClosedRange = BitcoinFeeRateProvider.defaultFeeRange - let step: Int = 1 +extension FeeRateProvider { + struct FeeRates { + let recommended: Int + let minimum: Int + } + +} + +class BitcoinFeeRateProvider: IFeeRateProvider { private let feeRateProvider: FeeRateProvider - private let recommendedBlockCount = 2 init(feeRateProvider: FeeRateProvider) { self.feeRateProvider = feeRateProvider } - func recommendedFeeRate() async throws -> Int { - try await feeRateProvider.bitcoinFeeRate(blockCount: recommendedBlockCount) + func feeRates() async throws -> FeeRateProvider.FeeRates { + let rates = try await feeRateProvider.bitcoinFeeRate() + return .init(recommended: rates.economyFee, minimum: rates.minimumFee) } } @@ -70,8 +73,8 @@ class LitecoinFeeRateProvider: IFeeRateProvider { self.feeRateProvider = feeRateProvider } - func recommendedFeeRate() async throws -> Int { - feeRateProvider.litecoinFeeRate + func feeRates() async throws -> FeeRateProvider.FeeRates { + .init(recommended: feeRateProvider.litecoinFeeRate, minimum: 0) } } @@ -83,16 +86,16 @@ class BitcoinCashFeeRateProvider: IFeeRateProvider { self.feeRateProvider = feeRateProvider } - func recommendedFeeRate() async throws -> Int { - feeRateProvider.bitcoinCashFeeRate + func feeRates() async throws -> FeeRateProvider.FeeRates { + .init(recommended: feeRateProvider.bitcoinCashFeeRate, minimum: 0) } } class ECashFeeRateProvider: IFeeRateProvider { - func recommendedFeeRate() async throws -> Int { - 1 + func feeRates() async throws -> FeeRateProvider.FeeRates { + .init(recommended: 1, minimum: 0) } } @@ -104,8 +107,8 @@ class DashFeeRateProvider: IFeeRateProvider { self.feeRateProvider = feeRateProvider } - func recommendedFeeRate() async throws -> Int { - feeRateProvider.dashFeeRate + func feeRates() async throws -> FeeRateProvider.FeeRates { + .init(recommended: feeRateProvider.dashFeeRate, minimum: 0) } } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/FeeWarning/SendFeeWarningViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/FeeWarning/SendFeeCautionViewModel.swift similarity index 50% rename from UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/FeeWarning/SendFeeWarningViewModel.swift rename to UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/FeeWarning/SendFeeCautionViewModel.swift index 347f804c49..6a99c9daf3 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/FeeWarning/SendFeeWarningViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/FeeWarning/SendFeeCautionViewModel.swift @@ -1,13 +1,11 @@ +import Foundation import RxSwift import RxRelay import RxCocoa -class SendFeeWarningViewModel { - +class SendFeeCautionViewModel { private let disposeBag = DisposeBag() private let service: FeeRateService - private let cautionTitle: String - private let cautionText: String private let cautionRelay = BehaviorRelay(value: nil) var caution: TitledCaution? { @@ -18,18 +16,25 @@ class SendFeeWarningViewModel { } } - init(service: FeeRateService, cautionTitle: String = "send.fee_settings.stuck_warning.title".localized, cautionText: String = "send.fee_settings.stuck_warning".localized) { + init(service: FeeRateService) { self.service = service - self.cautionTitle = cautionTitle - self.cautionText = cautionText subscribe(disposeBag, service.statusObservable) { [weak self] _ in self?.sync() } sync() } private func sync() { + guard service.feeRateAvailble else { + caution = TitledCaution(title: "send.fee_settings.fee_error.title".localized, text: "send.fee_settings.fee_rate_unavailable".localized, type: .error) + return + } + if case let .completed(feeRate) = service.status, service.recommendedFeeRate > feeRate { - caution = TitledCaution(title: cautionTitle, text: cautionText, type: .warning) + if service.minimumFeeRate <= feeRate { + caution = TitledCaution(title: "send.fee_settings.stuck_warning.title".localized, text: "send.fee_settings.stuck_warning".localized, type: .warning) + } else { + caution = TitledCaution(title: "send.fee_settings.fee_error.title".localized, text: "send.fee_settings.too_low".localized, type: .error) + } } else { caution = nil } @@ -37,7 +42,7 @@ class SendFeeWarningViewModel { } -extension SendFeeWarningViewModel: ITitledCautionViewModel { +extension SendFeeCautionViewModel: ITitledCautionViewModel { var cautionDriver: Driver { cautionRelay.asDriver() diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinAdapterService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinAdapterService.swift index 6b22e4688c..71c768a717 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinAdapterService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinAdapterService.swift @@ -103,10 +103,11 @@ class SendBitcoinAdapterService { } private func sync(feeRate: DataStatus? = nil, updatedFrom: UpdatedField = .feeRate) { - let feeRate = feeRate ?? feeRateService.status + let feeRateStatus = feeRate ?? feeRateService.status let amount = amountInputService.amount + var feeRate = 0 - switch feeRate { + switch feeRateStatus { case .loading: guard !amount.isZero else { // force update fee for bitcoin, when clear amount to zero value feeState = .completed(0) @@ -116,9 +117,11 @@ class SendBitcoinAdapterService { feeState = .loading case .failed(let error): feeState = .failed(error) - case .completed(let feeRate): - update(feeRate: feeRate, amount: amount, address: addressService.state.address?.raw, pluginData: pluginData, updatedFrom: updatedFrom) + case .completed(let _feeRate): + feeRate = _feeRate } + + update(feeRate: feeRate, amount: amount, address: addressService.state.address?.raw, pluginData: pluginData, updatedFrom: updatedFrom) } private func update(feeRate: Int, amount: Decimal, address: String?, pluginData: [UInt8: IBitcoinPluginData], updatedFrom: UpdatedField) { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinFactory.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinFactory.swift index 0ed83b9213..dd77b9d856 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinFactory.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinFactory.swift @@ -72,11 +72,10 @@ class SendBitcoinFactory: BaseSendFactory { private let addressService: AddressService private let timeLockService: TimeLockService? private let adapterService: SendBitcoinAdapterService - private let customFeeRateProvider: ICustomRangedFeeRateProvider? private let logger: Logger private let token: Token - init(fiatService: FiatService, amountCautionService: SendAmountCautionService, addressService: AddressService, feeFiatService: FiatService, feeService: SendFeeService, feeRateService: FeeRateService, timeLockService: TimeLockService?, adapterService: SendBitcoinAdapterService, customFeeRateProvider: ICustomRangedFeeRateProvider?, logger: Logger, token: Token) { + init(fiatService: FiatService, amountCautionService: SendAmountCautionService, addressService: AddressService, feeFiatService: FiatService, feeService: SendFeeService, feeRateService: FeeRateService, timeLockService: TimeLockService?, adapterService: SendBitcoinAdapterService, logger: Logger, token: Token) { self.fiatService = fiatService self.amountCautionService = amountCautionService self.feeFiatService = feeFiatService @@ -85,7 +84,6 @@ class SendBitcoinFactory: BaseSendFactory { self.addressService = addressService self.timeLockService = timeLockService self.adapterService = adapterService - self.customFeeRateProvider = customFeeRateProvider self.logger = logger self.token = token } @@ -133,7 +131,7 @@ extension SendBitcoinFactory: ISendFeeSettingsFactory { var dataSources: [ISendSettingsDataSource] = [] let feeViewModel = SendFeeViewModel(service: feeService) - let feeCautionViewModel = SendFeeWarningViewModel(service: feeRateService) + let feeCautionViewModel = SendFeeCautionViewModel(service: feeRateService) let amountCautionViewModel = SendFeeSettingsAmountCautionViewModel(service: amountCautionService, feeToken: token) let feeRateViewModel = FeeRateViewModel(service: feeRateService, feeCautionViewModel: feeCautionViewModel, amountCautionViewModel: amountCautionViewModel) if token.blockchainType == .bitcoin { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinViewController.swift index 1d84ad8b35..4960e0be15 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Bitcoin/SendBitcoinViewController.swift @@ -8,7 +8,7 @@ import RxCocoa class SendBitcoinViewController: BaseSendViewController { private let disposeBag = DisposeBag() - private let feeWarningViewModel: ITitledCautionViewModel + private let feeCautionViewModel: ITitledCautionViewModel private let feeCell: FeeCell private let feeCautionCell = TitledHighlightedDescriptionCell() @@ -21,10 +21,10 @@ class SendBitcoinViewController: BaseSendViewController { amountCautionViewModel: SendAmountCautionViewModel, recipientViewModel: RecipientAddressViewModel, feeViewModel: SendFeeViewModel, - feeWarningViewModel: ITitledCautionViewModel + feeCautionViewModel: ITitledCautionViewModel ) { - self.feeWarningViewModel = feeWarningViewModel + self.feeCautionViewModel = feeCautionViewModel feeCell = FeeCell(viewModel: feeViewModel, title: "fee_settings.fee".localized) @@ -50,7 +50,7 @@ class SendBitcoinViewController: BaseSendViewController { self?.openInfo(title: "fee_settings.fee".localized, description: "fee_settings.fee.info".localized) } - subscribe(disposeBag, feeWarningViewModel.cautionDriver) { [weak self] in + subscribe(disposeBag, feeCautionViewModel.cautionDriver) { [weak self] in self?.handle(caution: $0) } @@ -86,14 +86,14 @@ class SendBitcoinViewController: BaseSendViewController { ) } - var feeWarningSection: SectionProtocol { + var feeCautionSection: SectionProtocol { Section( - id: "fee-warning", + id: "fee-caution", headerState: .margin(height: .margin12), rows: [ StaticRow( cell: feeCautionCell, - id: "fee-warning", + id: "fee-caution", dynamicHeight: { [weak self] containerWidth in self?.feeCautionCell.cellHeight(containerWidth: containerWidth) ?? 0 } @@ -104,7 +104,7 @@ class SendBitcoinViewController: BaseSendViewController { override func buildSections() -> [SectionProtocol] { var sections = [availableBalanceSection, amountSection, recipientSection, feeSection] - sections.append(contentsOf: [feeWarningSection, buttonSection]) + sections.append(contentsOf: [feeCautionSection, buttonSection]) return sections } diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/SendModule.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/SendModule.swift index 7b714e02f2..33b55617e9 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/SendModule.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/SendModule.swift @@ -127,10 +127,7 @@ class SendModule { // Fee let feeViewModel = SendFeeViewModel(service: feeService) - let feeWarningViewModel = SendFeeWarningViewModel(service: feeRateService) - - // Confirmation and Settings - let customRangedFeeRateProvider = feeRateProvider as? ICustomRangedFeeRateProvider + let feeCautionViewModel = SendFeeCautionViewModel(service: feeRateService) let sendFactory = SendBitcoinFactory( fiatService: fiatService, @@ -141,7 +138,6 @@ class SendModule { feeRateService: feeRateService, timeLockService: timeLockService, adapterService: bitcoinAdapterService, - customFeeRateProvider: customRangedFeeRateProvider, logger: App.shared.logger, token: token ) @@ -155,7 +151,7 @@ class SendModule { amountCautionViewModel: amountCautionViewModel, recipientViewModel: recipientViewModel, feeViewModel: feeViewModel, - feeWarningViewModel: feeWarningViewModel + feeCautionViewModel: feeCautionViewModel ) return ThemeNavigationController(rootViewController: viewController) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateDataSource.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateDataSource.swift index 74a6876789..4025b1ca69 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateDataSource.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateDataSource.swift @@ -35,14 +35,16 @@ class FeeRateDataSource { subscribe(disposeBag, feeRateViewModel.feeRateDriver) { [weak self] in self?.feeRateCell.value = $0 } subscribe(disposeBag, feeRateViewModel.alteredStateSignal) { [weak self] in self?.onUpdateAlteredState?() } - subscribe(disposeBag, feeRateViewModel.cautionDriver) { [weak self] in - self?.feeRateCell.set(cautionType: $0?.type) - self?.onCaution?($0) - } + subscribe(disposeBag, feeRateViewModel.cautionDriver) { [weak self] in self?.handleCaution(caution: $0) } feeRateCell.onChangeValue = { [weak self] value in self?.feeRateViewModel.set(value: value) } } + private func handleCaution(caution: TitledCaution?) { + feeRateCell.set(cautionType: caution?.type) + onCaution?(caution) + } + } extension FeeRateDataSource: ISendSettingsDataSource { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateService.swift index e116a35a25..0d6fd662f2 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateService.swift @@ -9,6 +9,8 @@ class FeeRateService { private let provider: IFeeRateProvider private(set) var recommendedFeeRate: Int = 0 + private(set) var minimumFeeRate: Int = 0 + private(set) var feeRateAvailble = true private var feeRate = 0 { didSet { status = .completed(feeRate) @@ -55,12 +57,15 @@ extension FeeRateService { Task { [weak self, provider] in do { - let feeRate = try await provider.recommendedFeeRate() + let feeRates = try await provider.feeRates() - self?.recommendedFeeRate = feeRate - self?.feeRate = feeRate + self?.recommendedFeeRate = feeRates.recommended + self?.minimumFeeRate = feeRates.minimum + self?.feeRateAvailble = true self?.usingRecommended = true + self?.feeRate = feeRates.recommended } catch { + self?.feeRateAvailble = false self?.status = .failed(error) } }.store(in: &tasks) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateViewModel.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateViewModel.swift index 46caf5d8b8..677ed0d069 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateViewModel.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/FeeRate/FeeRateViewModel.swift @@ -7,14 +7,14 @@ class FeeRateViewModel { private let disposeBag = DisposeBag() private let service: FeeRateService - private let feeCautionViewModel: SendFeeWarningViewModel + private let feeCautionViewModel: SendFeeCautionViewModel private let amountCautionViewModel: SendFeeSettingsAmountCautionViewModel private let alteredStateRelay = PublishRelay() private let feeRateRelay = BehaviorRelay(value: nil) private let cautionRelay = BehaviorRelay(value: nil) - init(service: FeeRateService, feeCautionViewModel: SendFeeWarningViewModel, amountCautionViewModel: SendFeeSettingsAmountCautionViewModel) { + init(service: FeeRateService, feeCautionViewModel: SendFeeCautionViewModel, amountCautionViewModel: SendFeeSettingsAmountCautionViewModel) { self.service = service self.feeCautionViewModel = feeCautionViewModel self.amountCautionViewModel = amountCautionViewModel @@ -31,16 +31,15 @@ class FeeRateViewModel { private func syncCaution() { var caution: TitledCaution? = nil - if let error = amountCautionViewModel.amountCaution { - caution = error - } else if let warning = feeCautionViewModel.caution { - caution = warning + if let amountCaution = amountCautionViewModel.amountCaution { + caution = amountCaution + } else if let feeCaution = feeCautionViewModel.caution { + caution = feeCaution } cautionRelay.accept(caution) } - private func sync(feeRateStatus: DataStatus) { if case .completed(let feeRate) = feeRateStatus { feeRateRelay.accept(Decimal(feeRate)) diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/SendSettingsViewController.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/SendSettingsViewController.swift index be7afe3b94..a3c4dcf176 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/SendSettingsViewController.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Settings/SendSettingsViewController.swift @@ -90,11 +90,10 @@ class SendSettingsViewController: ThemeViewController { tableView.registerCell(forClass: TitledHighlightedDescriptionCell.self) tableView.sectionDataSource = self - dataSources.forEach { $0.viewDidLoad() } - tableView.buildSections() syncResetButton() handle(caution: nil) + dataSources.forEach { $0.viewDidLoad() } loaded = true } diff --git a/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings b/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings index 7cc316a3db..e4e3f62362 100644 --- a/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings +++ b/UnstoppableWallet/UnstoppableWallet/en.lproj/Localizable.strings @@ -410,6 +410,9 @@ Go to Settings - > %@ and allow access to the camera."; "send.fee_settings.stuck_warning.title" = "Risk of getting stuck"; "send.fee_settings.stuck_warning" = "The transaction may get stuck or fail."; +"send.fee_settings.fee_error.title" = "Fee Error"; +"send.fee_settings.too_low" = "Fee Rate is too low."; +"send.fee_settings.fee_rate_unavailable" = "Fee Rate unavailable. Please, check fee rates manually"; "send.stuck_warning" = "Warning! Risk of getting stuck";