diff --git a/AlphaWallet/Browser/Coordinators/DappBrowserCoordinator.swift b/AlphaWallet/Browser/Coordinators/DappBrowserCoordinator.swift index af47733fe..d9ac84e89 100644 --- a/AlphaWallet/Browser/Coordinators/DappBrowserCoordinator.swift +++ b/AlphaWallet/Browser/Coordinators/DappBrowserCoordinator.swift @@ -40,6 +40,7 @@ final class DappBrowserCoordinator: NSObject, Coordinator { private let sharedRealm: Realm private let browserOnly: Bool + private let nativeCryptoCurrencyPrices: ServerDictionary> private var nativeCryptoCurrencyBalanceView: NativeCryptoCurrencyBalanceView { //Not the best implementation. Hopefully this will be unnecessary @@ -119,6 +120,7 @@ final class DappBrowserCoordinator: NSObject, Coordinator { config: Config, sharedRealm: Realm, browserOnly: Bool, + nativeCryptoCurrencyPrices: ServerDictionary>, analyticsCoordinator: AnalyticsCoordinator? ) { self.navigationController = UINavigationController(navigationBarClass: DappBrowserNavigationBar.self, toolbarClass: nil) @@ -127,6 +129,7 @@ final class DappBrowserCoordinator: NSObject, Coordinator { self.config = config self.sharedRealm = sharedRealm self.browserOnly = browserOnly + self.nativeCryptoCurrencyPrices = nativeCryptoCurrencyPrices self.analyticsCoordinator = analyticsCoordinator super.init() @@ -156,7 +159,8 @@ final class DappBrowserCoordinator: NSObject, Coordinator { private func executeTransaction(account: AlphaWallet.Address, action: DappAction, callbackID: Int, transaction: UnconfirmedTransaction, type: ConfirmType, server: RPCServer) { pendingTransaction = .data(callbackID: callbackID) - let coordinator = TransactionConfirmationCoordinator(navigationController: navigationController, session: session, transaction: transaction, configuration: .dappTransaction(confirmType: type, keystore: keystore), analyticsCoordinator: analyticsCoordinator) + let ethPrice = nativeCryptoCurrencyPrices[server] + let coordinator = TransactionConfirmationCoordinator(navigationController: navigationController, session: session, transaction: transaction, configuration: .dappTransaction(confirmType: type, keystore: keystore, ethPrice: ethPrice), analyticsCoordinator: analyticsCoordinator) coordinator.delegate = self addCoordinator(coordinator) coordinator.start() diff --git a/AlphaWallet/InCoordinator.swift b/AlphaWallet/InCoordinator.swift index 095e0acd4..d84fcf380 100644 --- a/AlphaWallet/InCoordinator.swift +++ b/AlphaWallet/InCoordinator.swift @@ -444,7 +444,7 @@ class InCoordinator: NSObject, Coordinator { } private func createBrowserCoordinator(sessions: ServerDictionary, realm: Realm, browserOnly: Bool, analyticsCoordinator: AnalyticsCoordinator?) -> DappBrowserCoordinator { - let coordinator = DappBrowserCoordinator(sessions: sessions, keystore: keystore, config: config, sharedRealm: realm, browserOnly: browserOnly, analyticsCoordinator: analyticsCoordinator) + let coordinator = DappBrowserCoordinator(sessions: sessions, keystore: keystore, config: config, sharedRealm: realm, browserOnly: browserOnly, nativeCryptoCurrencyPrices: nativeCryptoCurrencyPrices, analyticsCoordinator: analyticsCoordinator) coordinator.delegate = self coordinator.start() coordinator.rootViewController.tabBarItem = UITabBarItem(title: R.string.localizable.browserTabbarItemTitle(), image: R.image.tab_browser(), selectedImage: nil) diff --git a/AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift b/AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift index 4a4f0934d..437489383 100644 --- a/AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift +++ b/AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift @@ -661,7 +661,7 @@ extension SingleChainTokenCoordinator: TokenInstanceActionViewControllerDelegate func confirmTransactionSelected(in viewController: TokenInstanceActionViewController, tokenObject: TokenObject, contract: AlphaWallet.Address, tokenId: TokenId, values: [AttributeId: AssetInternalValue], localRefs: [AttributeId: AssetInternalValue], server: RPCServer, session: WalletSession, keystore: Keystore, transactionFunction: FunctionOrigin) { switch transactionFunction.makeUnConfirmedTransaction(withTokenObject: tokenObject, tokenId: tokenId, attributeAndValues: values, localRefs: localRefs, server: server, session: session) { case .success(let transaction): - let coordinator = TransactionConfirmationCoordinator(navigationController: navigationController, session: session, transaction: transaction, configuration: .tokenScriptTransaction(confirmType: .signThenSend, contract: contract, keystore: keystore), analyticsCoordinator: analyticsCoordinator) + let coordinator = TransactionConfirmationCoordinator(navigationController: navigationController, session: session, transaction: transaction, configuration: .tokenScriptTransaction(confirmType: .signThenSend, contract: contract, keystore: keystore, ethPrice: cryptoPrice), analyticsCoordinator: analyticsCoordinator) coordinator.delegate = self addCoordinator(coordinator) coordinator.start() diff --git a/AlphaWallet/Transactions/Coordinators/TokensCardCoordinator.swift b/AlphaWallet/Transactions/Coordinators/TokensCardCoordinator.swift index a93dfa31e..fec0e863d 100644 --- a/AlphaWallet/Transactions/Coordinators/TokensCardCoordinator.swift +++ b/AlphaWallet/Transactions/Coordinators/TokensCardCoordinator.swift @@ -656,7 +656,7 @@ extension TokensCardCoordinator: TransferTokensCardViaWalletAddressViewControlle switch paymentFlow { case .send: if case .send(let transferType) = paymentFlow { - let coordinator = TransferNFTCoordinator(navigationController: navigationController, transferType: transferType, tokenHolder: tokenHolder, recipient: recipient, keystore: keystore, session: session, analyticsCoordinator: analyticsCoordinator) + let coordinator = TransferNFTCoordinator(navigationController: navigationController, transferType: transferType, tokenHolder: tokenHolder, recipient: recipient, keystore: keystore, session: session, ethPrice: ethPrice, analyticsCoordinator: analyticsCoordinator) addCoordinator(coordinator) coordinator.delegate = self coordinator.start() @@ -698,7 +698,7 @@ extension TokensCardCoordinator: TokenInstanceActionViewControllerDelegate { func confirmTransactionSelected(in viewController: TokenInstanceActionViewController, tokenObject: TokenObject, contract: AlphaWallet.Address, tokenId: TokenId, values: [AttributeId: AssetInternalValue], localRefs: [AttributeId: AssetInternalValue], server: RPCServer, session: WalletSession, keystore: Keystore, transactionFunction: FunctionOrigin) { switch transactionFunction.makeUnConfirmedTransaction(withTokenObject: tokenObject, tokenId: tokenId, attributeAndValues: values, localRefs: localRefs, server: server, session: session) { case .success(let transaction): - let coordinator = TransactionConfirmationCoordinator(navigationController: navigationController, session: session, transaction: transaction, configuration: .tokenScriptTransaction(confirmType: .signThenSend, contract: contract, keystore: keystore), analyticsCoordinator: analyticsCoordinator) + let coordinator = TransactionConfirmationCoordinator(navigationController: navigationController, session: session, transaction: transaction, configuration: .tokenScriptTransaction(confirmType: .signThenSend, contract: contract, keystore: keystore, ethPrice: ethPrice), analyticsCoordinator: analyticsCoordinator) coordinator.delegate = self addCoordinator(coordinator) coordinator.start() diff --git a/AlphaWallet/Transfer/Coordinators/TransactionConfirmationCoordinator.swift b/AlphaWallet/Transfer/Coordinators/TransactionConfirmationCoordinator.swift index b852682a7..e25271b61 100644 --- a/AlphaWallet/Transfer/Coordinators/TransactionConfirmationCoordinator.swift +++ b/AlphaWallet/Transfer/Coordinators/TransactionConfirmationCoordinator.swift @@ -11,22 +11,22 @@ import PromiseKit import Result enum TransactionConfirmationConfiguration { - case tokenScriptTransaction(confirmType: ConfirmType, contract: AlphaWallet.Address, keystore: Keystore) - case dappTransaction(confirmType: ConfirmType, keystore: Keystore) + case tokenScriptTransaction(confirmType: ConfirmType, contract: AlphaWallet.Address, keystore: Keystore, ethPrice: Subscribable) + case dappTransaction(confirmType: ConfirmType, keystore: Keystore, ethPrice: Subscribable) case sendFungiblesTransaction(confirmType: ConfirmType, keystore: Keystore, assetDefinitionStore: AssetDefinitionStore, amount: String, ethPrice: Subscribable) - case sendNftTransaction(confirmType: ConfirmType, keystore: Keystore) + case sendNftTransaction(confirmType: ConfirmType, keystore: Keystore, ethPrice: Subscribable) case claimPaidErc875MagicLink(confirmType: ConfirmType, keystore: Keystore, price: BigUInt, ethPrice: Subscribable, numberOfTokens: UInt) var confirmType: ConfirmType { switch self { - case .dappTransaction(let confirmType, _), .sendFungiblesTransaction(let confirmType, _, _, _, _), .sendNftTransaction(let confirmType, _), .tokenScriptTransaction(let confirmType, _, _), .claimPaidErc875MagicLink(let confirmType, _, _, _, _): + case .dappTransaction(let confirmType, _, _), .sendFungiblesTransaction(let confirmType, _, _, _, _), .sendNftTransaction(let confirmType, _, _), .tokenScriptTransaction(let confirmType, _, _, _), .claimPaidErc875MagicLink(let confirmType, _, _, _, _): return confirmType } } var keystore: Keystore { switch self { - case .dappTransaction(_, let keystore), .sendFungiblesTransaction(_, let keystore, _, _, _), .sendNftTransaction(_, let keystore), .tokenScriptTransaction(_, _, let keystore), .claimPaidErc875MagicLink(_, let keystore, _, _, _): + case .dappTransaction(_, let keystore, _), .sendFungiblesTransaction(_, let keystore, _, _, _), .sendNftTransaction(_, let keystore, _), .tokenScriptTransaction(_, _, let keystore, _), .claimPaidErc875MagicLink(_, let keystore, _, _, _), .claimPaidErc875MagicLink(_, let keystore, _, _, _): return keystore } } diff --git a/AlphaWallet/Transfer/Coordinators/TransferNFTCoordinator.swift b/AlphaWallet/Transfer/Coordinators/TransferNFTCoordinator.swift index e192cf991..8add8f8a2 100644 --- a/AlphaWallet/Transfer/Coordinators/TransferNFTCoordinator.swift +++ b/AlphaWallet/Transfer/Coordinators/TransferNFTCoordinator.swift @@ -16,17 +16,19 @@ class TransferNFTCoordinator: Coordinator { private let recipient: AlphaWallet.Address private let keystore: Keystore private let session: WalletSession + private let ethPrice: Subscribable private let analyticsCoordinator: AnalyticsCoordinator? var coordinators: [Coordinator] = [] weak var delegate: TransferNFTCoordinatorDelegate? - init(navigationController: UINavigationController, transferType: TransferType, tokenHolder: TokenHolder, recipient: AlphaWallet.Address, keystore: Keystore, session: WalletSession, analyticsCoordinator: AnalyticsCoordinator?) { + init(navigationController: UINavigationController, transferType: TransferType, tokenHolder: TokenHolder, recipient: AlphaWallet.Address, keystore: Keystore, session: WalletSession, ethPrice: Subscribable, analyticsCoordinator: AnalyticsCoordinator?) { self.navigationController = navigationController self.transferType = transferType self.tokenHolder = tokenHolder self.recipient = recipient self.keystore = keystore self.session = session + self.ethPrice = ethPrice self.analyticsCoordinator = analyticsCoordinator } @@ -40,7 +42,7 @@ class TransferNFTCoordinator: Coordinator { tokenId: tokenHolder.tokens[0].id, indices: tokenHolder.indices ) - let configuration: TransactionConfirmationConfiguration = .sendNftTransaction(confirmType: .signThenSend, keystore: keystore) + let configuration: TransactionConfirmationConfiguration = .sendNftTransaction(confirmType: .signThenSend, keystore: keystore, ethPrice: ethPrice) let coordinator = TransactionConfirmationCoordinator(navigationController: navigationController, session: session, transaction: transaction, configuration: configuration, analyticsCoordinator: analyticsCoordinator) addCoordinator(coordinator) coordinator.delegate = self diff --git a/AlphaWallet/Transfer/ViewControllers/TransactionConfirmationViewController.swift b/AlphaWallet/Transfer/ViewControllers/TransactionConfirmationViewController.swift index 9d0034136..66450e258 100644 --- a/AlphaWallet/Transfer/ViewControllers/TransactionConfirmationViewController.swift +++ b/AlphaWallet/Transfer/ViewControllers/TransactionConfirmationViewController.swift @@ -168,10 +168,18 @@ class TransactionConfirmationViewController: UIViewController { } switch viewModel { - case .dappTransaction: - break - case .tokenScriptTransaction: - break + case .dappTransaction(let dappTransactionViewModel): + dappTransactionViewModel.ethPrice.subscribe { [weak self] cryptoToDollarRate in + guard let strongSelf = self else { return } + dappTransactionViewModel.cryptoToDollarRate = cryptoToDollarRate + strongSelf.generateSubviews() + } + case .tokenScriptTransaction(let tokenScriptTransactionViewModel): + tokenScriptTransactionViewModel.ethPrice.subscribe { [weak self] cryptoToDollarRate in + guard let strongSelf = self else { return } + tokenScriptTransactionViewModel.cryptoToDollarRate = cryptoToDollarRate + strongSelf.generateSubviews() + } case .sendFungiblesTransaction(let sendFungiblesViewModel): sendFungiblesViewModel.recipientResolver.resolve { [weak self] in guard let strongSelf = self else { return } @@ -185,24 +193,31 @@ class TransactionConfirmationViewController: UIViewController { sendFungiblesViewModel.updateBalance(.nativeCryptocurrency(balanceViewModel: balanceBaseViewModel)) strongSelf.generateSubviews() } - sendFungiblesViewModel.ethPrice.subscribe { [weak self] cryptoToDollarRate in guard let strongSelf = self else { return } sendFungiblesViewModel.cryptoToDollarRate = cryptoToDollarRate strongSelf.generateSubviews() } - sendFungiblesViewModel.session.refresh(.ethBalance) case .ERC20Token(let token, _, _): sendFungiblesViewModel.updateBalance(.erc20(token: token)) case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .ERC721ForTicketToken, .dapp, .tokenScript, .claimPaidErc875MagicLink: - break + sendFungiblesViewModel.ethPrice.subscribe { [weak self] cryptoToDollarRate in + guard let strongSelf = self else { return } + sendFungiblesViewModel.cryptoToDollarRate = cryptoToDollarRate + strongSelf.generateSubviews() + } } case .sendNftTransaction(let sendNftViewModel): sendNftViewModel.recipientResolver.resolve { [weak self] in guard let strongSelf = self else { return } strongSelf.generateSubviews() } + sendNftViewModel.ethPrice.subscribe { [weak self] cryptoToDollarRate in + guard let strongSelf = self else {return} + sendNftViewModel.cryptoToDollarRate = cryptoToDollarRate + strongSelf.generateSubviews() + } case .claimPaidErc875MagicLink(let claimPaidErc875MagicLinkViewModel): claimPaidErc875MagicLinkViewModel.ethPrice.subscribe { [weak self] cryptoToDollarRate in guard let strongSelf = self else { return } diff --git a/AlphaWallet/Transfer/ViewModels/TransactionConfirmationViewModel.swift b/AlphaWallet/Transfer/ViewModels/TransactionConfirmationViewModel.swift index 8812a0a31..5b86bf268 100644 --- a/AlphaWallet/Transfer/ViewModels/TransactionConfirmationViewModel.swift +++ b/AlphaWallet/Transfer/ViewModels/TransactionConfirmationViewModel.swift @@ -12,16 +12,16 @@ enum TransactionConfirmationViewModel { init(configurator: TransactionConfigurator, configuration: TransactionConfirmationConfiguration) { switch configuration { - case .tokenScriptTransaction(_, let contract, _): - self = .tokenScriptTransaction(.init(address: contract, configurator: configurator)) - case .dappTransaction: - self = .dappTransaction(.init(configurator: configurator)) + case .tokenScriptTransaction(_, let contract, _, let ethPrice): + self = .tokenScriptTransaction(.init(address: contract, configurator: configurator, ethPrice: ethPrice)) + case .dappTransaction(_, _, let ethPrice): + self = .dappTransaction(.init(configurator: configurator, ethPrice: ethPrice)) case .sendFungiblesTransaction(_, _, let assetDefinitionStore, let amount, let ethPrice): let resolver = RecipientResolver(address: configurator.transaction.recipient) self = .sendFungiblesTransaction(.init(configurator: configurator, assetDefinitionStore: assetDefinitionStore, recipientResolver: resolver, amount: amount, ethPrice: ethPrice)) - case .sendNftTransaction: + case .sendNftTransaction(_, _, let ethPrice): let resolver = RecipientResolver(address: configurator.transaction.recipient) - self = .sendNftTransaction(.init(configurator: configurator, recipientResolver: resolver)) + self = .sendNftTransaction(.init(configurator: configurator, recipientResolver: resolver, ethPrice: ethPrice)) case .claimPaidErc875MagicLink(_, _, let price, let ethPrice, let numberOfTokens): self = .claimPaidErc875MagicLink(.init(configurator: configurator, price: price, ethPrice: ethPrice, numberOfTokens: numberOfTokens)) } @@ -75,6 +75,19 @@ enum UpdateBalanceValue { } extension TransactionConfirmationViewModel { + private static func gasFeeString(withConfigurator configurator: TransactionConfigurator, cryptoToDollarRate: Double?) -> String { + let fee = configurator.currentConfiguration.gasPrice * configurator.currentConfiguration.gasLimit + let symbol = configurator.session.server.symbol + let feeString = EtherNumberFormatter.short.string(from: fee) + let cryptoToDollarSymbol = Constants.Currency.usd + if let cryptoToDollarRate = cryptoToDollarRate { + let cryptoToDollarValue = StringFormatter().currency(with: Double(fee) * cryptoToDollarRate / Double(EthereumUnit.ether.rawValue), and: cryptoToDollarSymbol) + return "< ~\(feeString) \(symbol) (\(cryptoToDollarValue) \(cryptoToDollarSymbol))" + } else { + return "< ~\(feeString) \(symbol)" + } + } + class SendFungiblesTransactionViewModel: SectionProtocol { enum Section: Int, CaseIterable { case balance @@ -166,6 +179,18 @@ extension TransactionConfirmationViewModel { } } + var gasFee: String { + let fee: BigInt = configurator.currentConfiguration.gasPrice * configurator.currentConfiguration.gasLimit + let feeString = EtherNumberFormatter.short.string(from: fee) + let cryptoToDollarSymbol = Constants.Currency.usd + if let cryptoToDollarRate = cryptoToDollarRate { + let cryptoToDollarValue = StringFormatter().currency(with: Double(fee) * cryptoToDollarRate / Double(EthereumUnit.ether.rawValue), and: cryptoToDollarSymbol) + return "< ~\(feeString) \(session.server.symbol) (\(cryptoToDollarValue) \(cryptoToDollarSymbol))" + } else { + return "< ~\(feeString) \(session.server.symbol)" + } + } + func isSubviewsHidden(section: Int, row: Int) -> Bool { let isOpened = openedSections.contains(section) @@ -192,14 +217,14 @@ extension TransactionConfirmationViewModel { section: section, shouldHideChevron: sections[section] != .recipient ) - let placeholder = sections[section].title switch sections[section] { case .balance: let title = R.string.localizable.tokenTransactionConfirmationDefault() return .init(title: balance ?? title, placeholder: placeholder, details: newBalance, configuration: configuration) case .gas: - return .init(title: configurationTitle, placeholder: placeholder, configuration: configuration) + let gasFee = gasFeeString(withConfigurator: configurator, cryptoToDollarRate: cryptoToDollarRate) + return .init(title: configurationTitle, placeholder: placeholder, details: gasFee, configuration: configuration) case .amount: return .init(title: formattedAmountValue, placeholder: placeholder, configuration: configuration) case .recipient: @@ -224,14 +249,17 @@ extension TransactionConfirmationViewModel { return configurator.selectedConfigurationType.title } + var cryptoToDollarRate: Double? + let ethPrice: Subscribable var openedSections = Set() var sections: [Section] { return Section.allCases } - init(configurator: TransactionConfigurator) { + init(configurator: TransactionConfigurator, ethPrice: Subscribable) { self.configurator = configurator + self.ethPrice = ethPrice } func isSubviewHidden(section: Int, row: Int) -> Bool { @@ -252,7 +280,8 @@ extension TransactionConfirmationViewModel { let placeholder = sections[section].title switch sections[section] { case .gas: - return .init(title: configurationTitle, placeholder: placeholder, configuration: configuration) + let gasFee = gasFeeString(withConfigurator: configurator, cryptoToDollarRate: cryptoToDollarRate) + return .init(title: configurationTitle, placeholder: placeholder, details: gasFee, configuration: configuration) } } } @@ -278,14 +307,17 @@ extension TransactionConfirmationViewModel { configurator.selectedConfigurationType.title } + var cryptoToDollarRate: Double? + let ethPrice: Subscribable var openedSections = Set() var sections: [Section] { return Section.allCases } - init(address: AlphaWallet.Address, configurator: TransactionConfigurator) { + init(address: AlphaWallet.Address, configurator: TransactionConfigurator, ethPrice: Subscribable) { self.address = address self.configurator = configurator + self.ethPrice = ethPrice } func headerViewModel(section: Int) -> TransactionConfirmationHeaderViewModel { @@ -294,7 +326,8 @@ extension TransactionConfirmationViewModel { let placeholder = sections[section].title switch sections[section] { case .gas: - return .init(title: configurationTitle, placeholder: placeholder, configuration: configuration) + let gasFee = gasFeeString(withConfigurator: configurator, cryptoToDollarRate: cryptoToDollarRate) + return .init(title: configurationTitle, placeholder: placeholder, details: gasFee, configuration: configuration) case .contract: return .init(title: address.truncateMiddle, placeholder: placeholder, configuration: configuration) } @@ -331,15 +364,18 @@ extension TransactionConfirmationViewModel { var addressString: String? { recipientResolver.address?.eip55String } var openedSections = Set() let recipientResolver: RecipientResolver + var cryptoToDollarRate: Double? + let ethPrice: Subscribable var sections: [Section] { return Section.allCases } - init(configurator: TransactionConfigurator, recipientResolver: RecipientResolver) { + init(configurator: TransactionConfigurator, recipientResolver: RecipientResolver, ethPrice: Subscribable) { self.configurator = configurator self.transferType = configurator.transaction.transferType self.session = configurator.session self.recipientResolver = recipientResolver + self.ethPrice = ethPrice } func isSubviewsHidden(section: Int, row: Int) -> Bool { @@ -371,7 +407,8 @@ extension TransactionConfirmationViewModel { let placeholder = sections[section].title switch sections[section] { case .gas: - return .init(title: configurationTitle, placeholder: placeholder, configuration: configuration) + let gasFee = gasFeeString(withConfigurator: configurator, cryptoToDollarRate: cryptoToDollarRate) + return .init(title: configurationTitle, placeholder: placeholder, details: gasFee, configuration: configuration) case .tokenId: //TODO be good to display the token instance's name or equivalent too let tokenId = configurator.transaction.tokenId.flatMap({ String($0) }) ?? "" diff --git a/AlphaWallet/Transfer/Views/TransactionConfirmationHeaderView.swift b/AlphaWallet/Transfer/Views/TransactionConfirmationHeaderView.swift index e55459653..140598adb 100644 --- a/AlphaWallet/Transfer/Views/TransactionConfirmationHeaderView.swift +++ b/AlphaWallet/Transfer/Views/TransactionConfirmationHeaderView.swift @@ -48,7 +48,7 @@ class TransactionConfirmationHeaderView: UIView { return view }() - + private let chevronImageView: UIImageView = { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false