From 141e58e862b9db6c55ced91235757f6f8cb9f102 Mon Sep 17 00:00:00 2001 From: Rafael Muhamedzyanov Date: Wed, 15 Nov 2023 18:18:26 +0600 Subject: [PATCH] Combine address parsers in single place --- .../modules/address/AddressParserChain.kt | 23 ++-- .../modules/address/AddressParserFactory.kt | 51 +++++--- .../modules/balance/BalanceViewModel.kt | 6 +- .../modules/contacts/ContactAddressParser.kt | 43 ------- .../modules/contacts/ContactsModule.kt | 11 +- .../contacts/viewmodel/AddressViewModel.kt | 113 +++++------------- 6 files changed, 91 insertions(+), 156 deletions(-) delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactAddressParser.kt diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserChain.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserChain.kt index 5ad86ba2573..0f42c0abb0e 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserChain.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserChain.kt @@ -2,15 +2,16 @@ package io.horizontalsystems.bankwallet.modules.address import io.horizontalsystems.bankwallet.entities.Address -class AddressParserChain { - val handlers = mutableListOf() +class AddressParserChain( + handlers: List = emptyList(), + domainHandlers: List = emptyList() +) { - fun addHandlers(handlers: List) { - this.handlers.addAll(handlers) - } + private val domainHandlers = domainHandlers.toMutableList() + private val addressHandlers = handlers.toMutableList() - fun handlers(address: String): List { - return handlers.filter { + fun supportedAddressHandlers(address: String): List { + return addressHandlers.filter { try { it.isSupported(address) } catch (t: Throwable) { @@ -20,7 +21,11 @@ class AddressParserChain { } fun handle(address: String): Address? { - val handler = handlers(address).firstOrNull() - return handler?.parseAddress(address) + return supportedAddressHandlers(address).firstOrNull()?.parseAddress(address ) + } + + fun getAddressFromDomain(address: String): Address? { + return domainHandlers.firstOrNull { it.isSupported(address) }?.parseAddress(address) } + } \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserFactory.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserFactory.kt index a98d02a665d..309da6af39d 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserFactory.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/address/AddressParserFactory.kt @@ -14,9 +14,7 @@ class AddressParserFactory( private val udnApiKey: String, ) { - private fun parserChainHandlers(blockchainType: BlockchainType, withEns: Boolean = true): List { - val udnHandler = AddressHandlerUdn(TokenQuery(blockchainType, TokenType.Native), "", udnApiKey) - val domainAddressHandlers = mutableListOf(udnHandler) + private fun parserChainHandlers(blockchainType: BlockchainType): List { val addressHandlers = mutableListOf() when (blockchainType) { BlockchainType.Bitcoin -> { @@ -64,7 +62,6 @@ class AddressParserFactory( BlockchainType.Gnosis, BlockchainType.Fantom, BlockchainType.ArbitrumOne -> { - domainAddressHandlers.add(AddressHandlerEns(blockchainType, EnsResolverHolder.resolver)) addressHandlers.add(AddressHandlerEvm(blockchainType)) } @@ -79,25 +76,47 @@ class AddressParserFactory( is BlockchainType.Unsupported -> { } } - if (withEns) { - addressHandlers.addAll(domainAddressHandlers) - } return addressHandlers } - fun parserChain(blockchainType: BlockchainType?, withEns: Boolean = true): AddressParserChain { - blockchainType?.let { - return AddressParserChain().apply { - addHandlers(parserChainHandlers(it, withEns)) + private fun domainHandlers(blockchainType: BlockchainType): List { + val udnHandler = AddressHandlerUdn(TokenQuery(blockchainType, TokenType.Native), "", udnApiKey) + val domainAddressHandlers = mutableListOf(udnHandler) + when (blockchainType) { + BlockchainType.Ethereum, + BlockchainType.BinanceSmartChain, + BlockchainType.Polygon, + BlockchainType.Avalanche, + BlockchainType.Optimism, + BlockchainType.Gnosis, + BlockchainType.Fantom, + BlockchainType.ArbitrumOne -> { + domainAddressHandlers.add(AddressHandlerEns(blockchainType, EnsResolverHolder.resolver)) } + + else -> {} } + return domainAddressHandlers + } - val handlers = BlockchainType.supported.map { - parserChainHandlers(it, withEns) - }.flatten() + fun parserChain(blockchainType: BlockchainType?, withEns: Boolean = false): AddressParserChain { + val addressHandlers = mutableListOf() + val domainHandlers = mutableListOf() - return AddressParserChain().apply { - addHandlers(handlers) + blockchainType?.let { + addressHandlers.addAll(parserChainHandlers(it)) + if (withEns) { + domainHandlers.addAll(domainHandlers(it)) + } + } ?: run { + BlockchainType.supported.forEach { + addressHandlers.addAll(parserChainHandlers(it)) + if (withEns) { + domainHandlers.addAll(domainHandlers(it)) + } + } } + + return AddressParserChain(addressHandlers, domainHandlers) } } \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt index 0d5f8a45a6d..e5137963af8 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/balance/BalanceViewModel.kt @@ -218,10 +218,8 @@ class BalanceViewModel( connectionResult = WalletConnectListViewModel.ConnectionResult.Error }) } else { - val addressParserChain = addressParserFactory.parserChain(null, false) - val supportedHandlers = addressParserChain.handlers.filter { handler -> - handler.isSupported(scannedText) - } + val addressParserChain = addressParserFactory.parserChain(null) + val supportedHandlers = addressParserChain.supportedAddressHandlers(scannedText) if (supportedHandlers.isNotEmpty()) { val address = supportedHandlers.first().parseAddress(scannedText) openSendTokenSelect = OpenSendTokenSelect(supportedHandlers.map { it.blockchainType }, address.hex) diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactAddressParser.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactAddressParser.kt deleted file mode 100644 index e59c50c8b6b..00000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactAddressParser.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.contacts - -import io.horizontalsystems.bankwallet.entities.Address -import io.horizontalsystems.bankwallet.modules.address.AddressValidationException -import io.horizontalsystems.bankwallet.modules.address.IAddressHandler -import io.horizontalsystems.marketkit.models.Blockchain -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -class ContactAddressParser( - private val domainAddressHandlers: List, - private val rawAddressHandlers: List, - private val blockchain: Blockchain -) { - suspend fun parseAddress(value: String): Address = withContext(Dispatchers.IO) { - try { - val resolvedAddress = parse(value, domainAddressHandlers) - parse(resolvedAddress.hex, rawAddressHandlers) - } catch (error: Throwable) { - parse(value, rawAddressHandlers) - } - } - - private fun parse(value: String, handlers: List): Address { - if (value.isBlank()) throw AddressValidationException.Blank() - - val vTrimmed = value.trim() - - val handler = handlers.firstOrNull { - try { - it.isSupported(vTrimmed) - } catch (t: Throwable) { - false - } - } ?: throw AddressValidationException.Unsupported(blockchain.name) - - try { - return handler.parseAddress(vTrimmed) - } catch (t: Throwable) { - throw AddressValidationException.Invalid(t, blockchain.name) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactsModule.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactsModule.kt index ef852de84de..1d25c59d982 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactsModule.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/ContactsModule.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModelProvider import io.horizontalsystems.bankwallet.R import io.horizontalsystems.bankwallet.core.App import io.horizontalsystems.bankwallet.core.providers.Translator +import io.horizontalsystems.bankwallet.modules.address.AddressParserFactory import io.horizontalsystems.bankwallet.modules.contacts.model.Contact import io.horizontalsystems.bankwallet.modules.contacts.model.ContactAddress import io.horizontalsystems.bankwallet.modules.contacts.viewmodel.AddressViewModel @@ -38,7 +39,15 @@ object ContactsModule { ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { - return AddressViewModel(contactUid, App.contactsRepository, App.appConfigProvider.udnApiKey, App.evmBlockchainManager, App.marketKit, contactAddress, definedAddresses) as T + return AddressViewModel( + contactUid, + App.contactsRepository, + AddressParserFactory(App.appConfigProvider.udnApiKey), + App.evmBlockchainManager, + App.marketKit, + contactAddress, + definedAddresses + ) as T } } diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/viewmodel/AddressViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/viewmodel/AddressViewModel.kt index 36d3375b265..1ca89d48122 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/viewmodel/AddressViewModel.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/contacts/viewmodel/AddressViewModel.kt @@ -11,39 +11,25 @@ import io.horizontalsystems.bankwallet.core.managers.MarketKitWrapper import io.horizontalsystems.bankwallet.core.order import io.horizontalsystems.bankwallet.entities.Address import io.horizontalsystems.bankwallet.entities.DataState -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerBase58 -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerBech32 -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerBinanceChain -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerBitcoinCash -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerEns -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerEvm -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerPure -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerSolana -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerTron -import io.horizontalsystems.bankwallet.modules.address.AddressHandlerUdn -import io.horizontalsystems.bankwallet.modules.address.EnsResolverHolder +import io.horizontalsystems.bankwallet.modules.address.AddressParserChain +import io.horizontalsystems.bankwallet.modules.address.AddressParserFactory +import io.horizontalsystems.bankwallet.modules.address.AddressValidationException import io.horizontalsystems.bankwallet.modules.address.IAddressHandler -import io.horizontalsystems.bankwallet.modules.contacts.ContactAddressParser import io.horizontalsystems.bankwallet.modules.contacts.ContactsRepository import io.horizontalsystems.bankwallet.modules.contacts.model.ContactAddress import io.horizontalsystems.bankwallet.ui.compose.TranslatableString -import io.horizontalsystems.bitcoincash.MainNetBitcoinCash -import io.horizontalsystems.bitcoinkit.MainNet -import io.horizontalsystems.dashkit.MainNetDash -import io.horizontalsystems.ecash.MainNetECash -import io.horizontalsystems.litecoinkit.MainNetLitecoin import io.horizontalsystems.marketkit.models.Blockchain import io.horizontalsystems.marketkit.models.BlockchainType -import io.horizontalsystems.marketkit.models.TokenQuery -import io.horizontalsystems.marketkit.models.TokenType +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.ensureActive import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class AddressViewModel( private val contactUid: String?, private val contactsRepository: ContactsRepository, - private val udnApiKey: String, + private val addressParserFactory: AddressParserFactory, evmBlockchainManager: EvmBlockchainManager, marketKit: MarketKitWrapper, contactAddress: ContactAddress?, @@ -82,7 +68,7 @@ class AddressViewModel( } private var blockchain = contactAddress?.blockchain ?: availableBlockchains.first() - private var addressParser: ContactAddressParser = addressParser(blockchain) + private var addressParser: AddressParserChain = addressParserFactory.parserChain(blockchain.type, true) var uiState by mutableStateOf(uiState()) private set @@ -97,7 +83,7 @@ class AddressViewModel( fun onEnterBlockchain(blockchain: Blockchain) { this.blockchain = blockchain - this.addressParser = addressParser(blockchain) + this.addressParser = addressParserFactory.parserChain(blockchain.type, true) emitUiState() @@ -120,7 +106,7 @@ class AddressViewModel( emitUiState() addressState = try { - val parsedAddress = addressParser.parseAddress(address) + val parsedAddress = parseAddress(addressParser, address.trim()) ensureActive() contactsRepository.validateAddress(contactUid, ContactAddress(blockchain, parsedAddress.hex)) DataState.Success(parsedAddress) @@ -132,66 +118,6 @@ class AddressViewModel( } } - private fun addressParser(blockchain: Blockchain): ContactAddressParser { - val udnHandler = AddressHandlerUdn(TokenQuery(blockchain.type, TokenType.Native), "", udnApiKey) - val domainAddressHandlers = mutableListOf(udnHandler) - val rawAddressHandlers = mutableListOf() - when (blockchain.type) { - BlockchainType.Bitcoin -> { - val network = MainNet() - rawAddressHandlers.add(AddressHandlerBase58(network, blockchain.type)) - rawAddressHandlers.add(AddressHandlerBech32(network, blockchain.type)) - } - BlockchainType.BitcoinCash -> { - val network = MainNetBitcoinCash() - rawAddressHandlers.add(AddressHandlerBase58(network, blockchain.type)) - rawAddressHandlers.add(AddressHandlerBitcoinCash(network)) - } - BlockchainType.ECash -> { - val network = MainNetECash() - rawAddressHandlers.add(AddressHandlerBase58(network, blockchain.type)) - rawAddressHandlers.add(AddressHandlerBitcoinCash(network)) - } - BlockchainType.Litecoin -> { - val network = MainNetLitecoin() - rawAddressHandlers.add(AddressHandlerBase58(network, blockchain.type)) - rawAddressHandlers.add(AddressHandlerBech32(network, blockchain.type)) - } - BlockchainType.Dash -> { - val network = MainNetDash() - rawAddressHandlers.add(AddressHandlerBase58(network, blockchain.type)) - } - BlockchainType.BinanceChain -> { - rawAddressHandlers.add(AddressHandlerBinanceChain()) - } - BlockchainType.Zcash -> { - //No validation - rawAddressHandlers.add(AddressHandlerPure(blockchain.type)) - } - BlockchainType.Ethereum, - BlockchainType.BinanceSmartChain, - BlockchainType.Polygon, - BlockchainType.Avalanche, - BlockchainType.Optimism, - BlockchainType.Gnosis, - BlockchainType.Fantom, - BlockchainType.ArbitrumOne -> { - domainAddressHandlers.add(AddressHandlerEns(blockchain.type, EnsResolverHolder.resolver)) - rawAddressHandlers.add(AddressHandlerEvm(blockchain.type)) - } - BlockchainType.Solana -> { - rawAddressHandlers.add(AddressHandlerSolana()) - } - BlockchainType.Tron -> { - rawAddressHandlers.add(AddressHandlerTron()) - } - is BlockchainType.Unsupported -> { - } - } - - return ContactAddressParser(domainAddressHandlers, rawAddressHandlers, blockchain) - } - private fun uiState() = UiState( headerTitle = title, editingAddress = editingAddress, @@ -208,6 +134,27 @@ class AddressViewModel( uiState = uiState() } + private suspend fun parseAddress(addressParser: AddressParserChain, value: String): Address = withContext(Dispatchers.IO) { + try { + val resolvedAddress = addressParser.getAddressFromDomain(value)?.hex ?: value + parse(resolvedAddress, addressParser.supportedAddressHandlers(resolvedAddress)) + } catch (error: Throwable) { + throw AddressValidationException.Invalid(error, blockchain.name) + } + } + + private fun parse(value: String, supportedHandlers: List): Address { + if (supportedHandlers.isEmpty()) { + throw AddressValidationException.Unsupported(blockchain.name) + } + + try { + return supportedHandlers.first().parseAddress(value) + } catch (t: Throwable) { + throw AddressValidationException.Invalid(t, blockchain.name) + } + } + data class UiState( val headerTitle: TranslatableString, val editingAddress: ContactAddress?,