diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 904ef1dc22..9ce22e7a27 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -10,8 +10,6 @@ import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.Logger import com.tangem.Log import com.tangem.LogFormat -import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.filter.OneTimeEventFilter @@ -35,7 +33,6 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.domain.wallets.legacy.WalletManagersRepository import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles @@ -62,11 +59,6 @@ import com.tangem.tap.domain.tokens.UserTokensRepository import com.tangem.tap.domain.tokens.UserTokensStorageService import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.domain.walletStores.di.provideDefaultImplementation -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.di.provideDefaultImplementation import com.tangem.tap.domain.walletconnect.WalletConnectRepository import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles @@ -93,32 +85,6 @@ lateinit var shopService: TangemShopService internal lateinit var userTokensRepository: UserTokensRepository internal lateinit var derivationsFinder: DerivationsFinder -private val walletStoresRepository by lazy { WalletStoresRepository.provideDefaultImplementation() } -private val walletManagersRepository by lazy { - WalletManagersRepository.provideDefaultImplementation( - walletManagerFactory = WalletManagerFactory( - config = store.state.globalState.configManager - ?.config - ?.blockchainSdkConfig - ?: BlockchainSdkConfig(), - ), - ) -} -private val walletAmountsRepository by lazy { - WalletAmountsRepository.provideDefaultImplementation( - tangemTechService = store.state.domainNetworks.tangemTechService, - ) -} -val walletStoresManager by lazy { - WalletStoresManager.provideDefaultImplementation( - userTokensRepository = userTokensRepository, - walletStoresRepository = walletStoresRepository, - walletManagersRepository = walletManagersRepository, - walletAmountsRepository = walletAmountsRepository, - appCurrencyProvider = { store.state.globalState.appCurrency }, - ) -} - @HiltAndroidApp internal class TapApplication : Application(), ImageLoaderFactory { @@ -263,7 +229,6 @@ internal class TapApplication : Application(), ImageLoaderFactory { ) appStateHolder.mainStore = store appStateHolder.userTokensRepository = userTokensRepository - appStateHolder.walletStoresManager = walletStoresManager walletConnect2Repository.init(projectId = configManager.config.walletConnectProjectId) } diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresError.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresError.kt deleted file mode 100644 index 32097cfcdf..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresError.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.tap.domain.walletStores - -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.core.TangemError - -sealed class WalletStoresError(code: Int) : TangemError(code) { - override val silent: Boolean - get() = (cause as? TangemError)?.silent == true - - override val messageResId: Int? = null - override val message: String? - get() = customMessage - - class FetchFiatRatesError( - currencies: List, - override val cause: Throwable?, - ) : WalletStoresError(code = 60011) { - override var customMessage: String = "Failed to fetch fiat rates for currencies $currencies" - } - - class UnknownBlockchain : WalletStoresError(code = 60012) { - override var customMessage: String = "Unknown blockchain" - } - - object NoInternetConnection : WalletStoresError(code = 60013) { - override var customMessage: String = "No internet connection" - } - - class WalletManagerNotCreated(blockchain: Blockchain) : WalletStoresError(code = 60014) { - override var customMessage: String = "Wallet manager can not be created for $blockchain" - } - - class UpdateWalletManagerTokensError( - blockchain: Blockchain, - override val cause: Throwable, - ) : WalletStoresError(code = 600015) { - override var customMessage: String = "Unable to update wallet manager tokens for currency $blockchain: $cause" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt deleted file mode 100644 index 10d89c17eb..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.tangem.tap.domain.walletStores - -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.flow.Flow - -interface WalletStoresManager { - /** - * Get all [WalletStoreModel]s updates - * - * @return [Flow] with map of [WalletStoreModel] list assigned by [UserWalletId] - * */ - fun getAll(): Flow>> - - /** - * Get [WalletStoreModel]s updates which associated with provided [UserWalletId] - * - * @param userWalletId [UserWalletId] of user wallet - * - * @return [Flow] with [WalletStoreModel] list - * */ - fun get(userWalletId: UserWalletId): Flow> - - suspend fun getSync(userWalletId: UserWalletId): List - - /** - * Delete [WalletStoreModel]s associated with provided [UserWalletId]s - * - * @param userWalletsIds [UserWalletId] list - * - * @return [CompletionResult] of operation - * */ - suspend fun delete(userWalletsIds: List): CompletionResult - - /** - * Clear all [WalletStoreModel]s - * - * @return [CompletionResult] of operation - * */ - suspend fun clear(): CompletionResult - - /** - * Fetch wallet stores associated with provided [UserWallet]. Fetched [WalletStoreModel]s updates can be observed - * with [get] and [getAll] methods - * - * @param userWallet [UserWallet] to fetch [WalletStoreModel]s - - * - * @return [CompletionResult] of operation - * */ - suspend fun fetch(userWallet: UserWallet, refresh: Boolean = false): CompletionResult - - /** - * Fetch wallet stores associated with provided [UserWallet]s. Fetched [WalletStoreModel]s updates can be observed - * with [get] and [getAll] methods - * - * @param userWallets [UserWallet]s list to fetch [WalletStoreModel]s - - * - * @return [CompletionResult] of operation - * */ - suspend fun fetch(userWallets: List, refresh: Boolean = false): CompletionResult - - /** - * Update [WalletStoreModel]s amounts associated with provided [UserWallet]s - * - * @param userWallets [UserWallet]s list to update [WalletStoreModel]s amounts - * - * @return [CompletionResult] of operation - * */ - suspend fun updateAmounts(userWallets: List): CompletionResult - - suspend fun updateSelectedAddress( - userWalletId: UserWalletId, - currency: Currency, - addressType: AddressType, - ): CompletionResult - - // For provider - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/di/WalletsStoresManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/di/WalletsStoresManagerProvider.kt deleted file mode 100644 index d913969cac..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/di/WalletsStoresManagerProvider.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.tap.domain.walletStores.di - -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.domain.walletStores.implementation.DefaultWalletStoresManager -import com.tangem.tap.domain.walletStores.implementation.DummyWalletStoresManager -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository - -fun WalletStoresManager.Companion.provideDummyImplementation(): WalletStoresManager { - return DummyWalletStoresManager() -} - -fun WalletStoresManager.Companion.provideDefaultImplementation( - userTokensRepository: UserTokensRepository, - walletStoresRepository: WalletStoresRepository, - walletAmountsRepository: WalletAmountsRepository, - walletManagersRepository: WalletManagersRepository, - appCurrencyProvider: () -> FiatCurrency, -): WalletStoresManager { - return DefaultWalletStoresManager( - userTokensRepository = userTokensRepository, - walletStoresRepository = walletStoresRepository, - walletAmountsRepository = walletAmountsRepository, - walletManagersRepository = walletManagersRepository, - appCurrencyProvider = appCurrencyProvider, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt deleted file mode 100644 index 4e60254106..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.tangem.tap.domain.walletStores.implementation - -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.* -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.model.builders.WalletStoreBuilder -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletStores.WalletStoresError -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateSelectedAddress -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.toBlockchainNetworks -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.withContext - -internal class DefaultWalletStoresManager( - private val userTokensRepository: UserTokensRepository, - private val walletStoresRepository: WalletStoresRepository, - private val walletAmountsRepository: WalletAmountsRepository, - private val walletManagersRepository: WalletManagersRepository, - private val appCurrencyProvider: () -> FiatCurrency, -) : WalletStoresManager { - private val state = MutableStateFlow(State()) - - override fun getAll(): Flow>> { - return walletStoresRepository.getAll() - } - - override fun get(userWalletId: UserWalletId): Flow> { - return walletStoresRepository.get(userWalletId) - .distinctUntilChanged() - } - - override suspend fun getSync(userWalletId: UserWalletId): List { - return walletStoresRepository.getSync(userWalletId) - } - - override suspend fun delete(userWalletsIds: List): CompletionResult { - return walletStoresRepository.delete(userWalletsIds) - .flatMap { walletManagersRepository.delete(userWalletsIds) } - } - - override suspend fun clear(): CompletionResult { - return walletStoresRepository.clear() - } - - override suspend fun fetch(userWallets: List, refresh: Boolean): CompletionResult = - withContext(Dispatchers.Default) { - val fiatCurrency = appCurrencyProvider.invoke() - val isFiatCurrencyChanged = state.value.fiatCurrency != fiatCurrency - - state.update { prevState -> - prevState.copy( - fiatCurrency = fiatCurrency, - ) - } - - userWallets - .mapNotNull { userWallet -> - val hasNotWalletStoresForUserWallet = !walletStoresRepository.contains(userWallet.walletId) - if (refresh || hasNotWalletStoresForUserWallet || isFiatCurrencyChanged) { - fetchWalletsIfNeeded(userWallet) - } else { - null - } - } - .fold(arrayListOf()) { acc, data -> - acc.apply { add(data) } - } - .flatMap { - walletAmountsRepository.updateAmountsForUserWallets(it, fiatCurrency) - } - } - - override suspend fun fetch(userWallet: UserWallet, refresh: Boolean): CompletionResult { - return fetch(listOf(userWallet), refresh) - } - - override suspend fun updateAmounts(userWallets: List): CompletionResult { - val fiatCurrency = appCurrencyProvider.invoke() - - return walletAmountsRepository.updateAmountsForUserWallets(userWallets, fiatCurrency) - .doOnSuccess { - state.update { prevState -> - prevState.copy( - fiatCurrency = fiatCurrency, - ) - } - } - } - - override suspend fun updateSelectedAddress( - userWalletId: UserWalletId, - currency: Currency, - addressType: AddressType, - ): CompletionResult { - return walletStoresRepository.update(userWalletId) { walletStores -> - walletStores - .firstOrNull { - it.blockchain == currency.blockchain && - it.derivationPath?.rawPath == currency.derivationPath - } - ?.updateSelectedAddress(currency, addressType) - } - } - - private suspend fun fetchWalletsIfNeeded(userWallet: UserWallet): CompletionResult { - return if (userWallet.isMultiCurrency) { - fetchMultiWallets(userWallet) - } else { - fetchSingleWallet(userWallet) - } - .map { userWallet } - } - - private suspend fun fetchMultiWallets(userWallet: UserWallet): CompletionResult { - val scanResponse = userWallet.scanResponse - val derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle() - val userTokens = withContext(Dispatchers.IO) { - userTokensRepository.getUserTokens(scanResponse.card, derivationStyle) - } - val userWalletId = userWallet.walletId - - return withContext(Dispatchers.Default) { - userTokens.toBlockchainNetworks() - .also { blockchainNetworks -> - walletStoresRepository.deleteDifference( - userWalletId = userWalletId, - currentBlockchains = blockchainNetworks.map { - Currency.Blockchain(it.blockchain, it.derivationPath) - }, - ) - } - .map { blockchainNetwork -> - val storeWalletStore: suspend (WalletManager?) -> CompletionResult = - { walletManager -> - walletStoresRepository.storeOrUpdate( - userWalletId = userWalletId, - walletStore = WalletStoreBuilder(userWallet, blockchainNetwork) - .walletManager(walletManager) - .build(), - ) - } - - walletManagersRepository.findOrMakeMultiCurrencyWalletManager( - userWallet = userWallet, - blockchainNetwork = blockchainNetwork, - ) - .flatMap { walletManager -> - storeWalletStore(walletManager) - } - .flatMapOnFailure { error -> - when (error) { - is WalletStoresError.WalletManagerNotCreated, - is WalletStoresError.UpdateWalletManagerTokensError, - -> storeWalletStore(null) - else -> CompletionResult.Failure(error) - } - } - } - .fold() - } - } - - private suspend fun fetchSingleWallet(userWallet: UserWallet): CompletionResult { - return walletManagersRepository.findOrMakeSingleCurrencyWalletManager( - userWallet = userWallet, - ) - .flatMap { walletManager -> - val userWalletId = userWallet.walletId - walletStoresRepository.storeOrUpdate( - userWalletId = userWalletId, - walletStore = WalletStoreBuilder(userWallet, walletManager) - .build(), - ) - } - .flatMapOnFailure { error -> - when (error) { - is WalletStoresError.WalletManagerNotCreated, - is WalletStoresError.UpdateWalletManagerTokensError, - -> CompletionResult.Success(Unit) - else -> CompletionResult.Failure(error) - } - } - } - - internal data class State( - val fiatCurrency: FiatCurrency? = null, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt deleted file mode 100644 index 9b6ab8abbb..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tap.domain.walletStores.implementation - -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow - -internal class DummyWalletStoresManager : WalletStoresManager { - override fun getAll(): Flow>> { - return emptyFlow() - } - - override fun get(userWalletId: UserWalletId): Flow> { - return emptyFlow() - } - - override suspend fun getSync(userWalletId: UserWalletId): List { - return emptyList() - } - - override suspend fun delete(userWalletsIds: List): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun clear(): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun fetch(userWallet: UserWallet, refresh: Boolean): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun fetch(userWallets: List, refresh: Boolean): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun updateAmounts(userWallets: List): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun updateSelectedAddress( - userWalletId: UserWalletId, - currency: Currency, - addressType: AddressType, - ): CompletionResult { - return CompletionResult.Success(Unit) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt deleted file mode 100644 index c25635dd50..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository - -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.WalletStoreModel - -interface WalletAmountsRepository { - - /** - * Fetch wallet amounts and fiat rates then update [com.tangem.tap.domain.walletStores.storage.WalletStoresStorage] - * and [com.tangem.tap.domain.walletStores.storage.WalletManagerStorage] with new data - * @param userWallets list of [UserWallet] which will be used to get the list of associated [WalletStoreModel] - * @param fiatCurrency current app [FiatCurrency] - * */ - suspend fun updateAmountsForUserWallets( - userWallets: List, - fiatCurrency: FiatCurrency, - ): CompletionResult - - /** - * Fetch wallet amounts and fiat rates then update [com.tangem.tap.domain.walletStores.storage.WalletStoresStorage] - * and [com.tangem.tap.domain.walletStores.storage.WalletManagerStorage] with new data - * @param userWallet [UserWallet] which will be used to get the list of associated [WalletStoreModel] - * @param fiatCurrency current app [FiatCurrency] - * */ - suspend fun updateAmountsForUserWallet(userWallet: UserWallet, fiatCurrency: FiatCurrency): CompletionResult - - suspend fun updateAmountsForWalletStores( - walletStores: List, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult - - /** - * Fetch wallet amounts and fiat rates then update [com.tangem.tap.domain.walletStores.storage.WalletStoresStorage] - * and [com.tangem.tap.domain.walletStores.storage.WalletManagerStorage] with new data - * @param walletStore [WalletStoreModel] to update - * @param userWallet [UserWallet] associated with provided [walletStore] - * @param fiatCurrency current app [FiatCurrency] - * */ - suspend fun updateAmountsForWalletStore( - walletStore: WalletStoreModel, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult - - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletStoresRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletStoresRepository.kt deleted file mode 100644 index 6007fcb2c3..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletStoresRepository.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository - -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.flow.Flow - -interface WalletStoresRepository { - fun getAll(): Flow>> - fun get(userWalletId: UserWalletId): Flow> - suspend fun getSync(userWalletId: UserWalletId): List - - suspend fun contains(userWalletId: UserWalletId): Boolean - - suspend fun delete(userWalletsIds: List): CompletionResult - - suspend fun deleteDifference( - userWalletId: UserWalletId, - currentBlockchains: List, - ): CompletionResult - - suspend fun clear(): CompletionResult - - suspend fun storeOrUpdate(userWalletId: UserWalletId, walletStore: WalletStoreModel): CompletionResult - - /** - * Updates [WalletStoreModel] in storage for user wallet with provided [UserWalletId] - * - * @param userWalletId [UserWalletId] of user wallet - * @param operation Lambda which receives list of [WalletStoreModel] assigned to user wallet with [userWalletId] - * and returns updated [WalletStoreModel]. If null returned then do nothing - * - * @return [CompletionResult] of operation - * */ - suspend fun update( - userWalletId: UserWalletId, - operation: (List) -> WalletStoreModel?, - ): CompletionResult - - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/di/RepositoryProvider.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/di/RepositoryProvider.kt deleted file mode 100644 index 769a1e0219..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/di/RepositoryProvider.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.di - -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.datasource.api.tangemTech.TangemTechService -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.implementation.DefaultWalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.implementation.DefaultWalletManagersRepository -import com.tangem.tap.domain.walletStores.repository.implementation.DefaultWalletStoresRepository -import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider - -fun WalletStoresRepository.Companion.provideDefaultImplementation(): WalletStoresRepository { - return DefaultWalletStoresRepository() -} - -fun WalletManagersRepository.Companion.provideDefaultImplementation( - walletManagerFactory: WalletManagerFactory, -): WalletManagersRepository { - return DefaultWalletManagersRepository(walletManagerFactory) -} - -fun WalletAmountsRepository.Companion.provideDefaultImplementation( - tangemTechService: TangemTechService, -): WalletAmountsRepository { - // TODO("After adding DI") get dependencies by DI - return DefaultWalletAmountsRepository( - tangemTechApi = tangemTechService.api, - dispatchers = AppCoroutineDispatcherProvider(), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt deleted file mode 100644 index 0203abef4b..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt +++ /dev/null @@ -1,447 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation - -import com.tangem.blockchain.blockchains.solana.RentProvider -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.extensions.Result.Failure -import com.tangem.blockchain.extensions.Result.Success -import com.tangem.common.* -import com.tangem.common.core.TangemError -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.domain.common.util.hasDerivation -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.TestActions -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.walletStores.WalletStoresError -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.implementation.utils.* -import com.tangem.tap.domain.walletStores.storage.WalletManagerStorage -import com.tangem.tap.domain.walletStores.storage.WalletStoresStorage -import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.PendingTransactionType -import com.tangem.tap.features.wallet.models.filterByCoin -import com.tangem.tap.features.wallet.models.getPendingTransactions -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.store -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.extensions.addOrReplace -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.firstOrNull -import timber.log.Timber -import java.math.BigDecimal -import kotlin.time.Duration - -@Suppress("LargeClass") -internal class DefaultWalletAmountsRepository( - private val tangemTechApi: TangemTechApi, - private val dispatchers: CoroutineDispatcherProvider, -) : WalletAmountsRepository { - - override suspend fun updateAmountsForUserWallets( - userWallets: List, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return if (userWallets.isEmpty()) { - CompletionResult.Success(Unit) - } else { - withContext(Dispatchers.Default) { - awaitAll( - async { fetchAmountsForUserWallets(userWallets) }, - async { fetchFiatRates(userWallets, walletStores = null, fiatCurrency) }, - ).fold() - } - } - } - - override suspend fun updateAmountsForUserWallet( - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return updateAmountsForUserWallets(listOf(userWallet), fiatCurrency) - } - - override suspend fun updateAmountsForWalletStores( - walletStores: List, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return if (walletStores.isEmpty()) { - CompletionResult.Success(Unit) - } else { - withContext(Dispatchers.Default) { - val userWalletId = userWallet.walletId - val scanResponse = userWallet.scanResponse - - awaitAll( - async { fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) }, - async { fetchFiatRates(listOf(userWallet), walletStores, fiatCurrency) }, - ).fold() - } - } - } - - override suspend fun updateAmountsForWalletStore( - walletStore: WalletStoreModel, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return updateAmountsForWalletStores(listOf(walletStore), userWallet, fiatCurrency) - } - - private suspend fun fetchFiatRates( - userWallets: List, - walletStores: List?, - fiatCurrency: FiatCurrency, - ): CompletionResult { - val networkConnectionManager = store.state.daggerGraphState.get(DaggerGraphState::networkConnectionManager) - if (!networkConnectionManager.isOnline) { - return CompletionResult.Failure(WalletStoresError.NoInternetConnection) - } - - val walletStoresInternal = walletStores ?: getWalletStores(userWallets) - val currencies = walletStoresInternal.asSequence().flatMap { it.walletsData }.map { it.currency } - - val coinsIds = currencies.mapNotNull { it.coinId }.distinct().toList() - - return withContext(dispatchers.io) { - runCatching { - tangemTechApi.getRates( - fiatCurrency.code.lowercase(), - coinsIds.joinToString(","), - ) - }.onSuccess { - updateWalletStoresWithFiatRates(walletStores = walletStoresInternal, fiatRates = it.rates) - return@withContext CompletionResult.Success(Unit) - }.onFailure { - val error = WalletStoresError.FetchFiatRatesError( - currencies = currencies.map(Currency::currencySymbol).toList(), - cause = it, - ) - - Timber.e( - error, - """ - Unable to fetch fiat rates - |- Coins ids: $coinsIds - """.trimIndent(), - ) - - return@withContext CompletionResult.Failure(error) - } - - error("Unreachable code because runCatching must return result") - } - } - - private suspend fun fetchAmountsForUserWallets(userWallets: List): CompletionResult = - withContext(Dispatchers.Default) { - userWallets.map { async { fetchAmountsForUserWallet(it) } }.awaitAll().fold() - } - - private suspend fun fetchAmountsForUserWallet(userWallet: UserWallet): CompletionResult = - withContext(Dispatchers.Default) { - val userWalletId = userWallet.walletId - val scanResponse = userWallet.scanResponse - val walletStores = getWalletStores(listOf(userWallet)) - - fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) - } - - private suspend fun fetchAmountForWalletStores( - userWalletId: UserWalletId, - scanResponse: ScanResponse, - walletStores: List, - ): CompletionResult = coroutineScope { - val networkConnectionManager = store.state.daggerGraphState.get(DaggerGraphState::networkConnectionManager) - if (!networkConnectionManager.isOnline) { - walletStores.forEach { - updateWalletStoreWithUnreachable(it) - } - return@coroutineScope CompletionResult.Failure(WalletStoresError.NoInternetConnection) - } - - walletStores.map { walletStore -> - async { - // TODO: Find wallet manager via [com.tangem.domain.wallets.legacy.WalletManagersRepository] - val walletManager = walletStore.walletManager - fetchAmountsForWalletStore(userWalletId, scanResponse, walletStore, walletManager) - } - }.awaitAll().fold() - } - - private suspend fun fetchAmountsForWalletStore( - userWalletId: UserWalletId, - scanResponse: ScanResponse, - walletStore: WalletStoreModel, - walletManager: WalletManager?, - ): CompletionResult { - val isDerivationMissed = with(walletStore) { - derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath.rawPath) - } - - return when { - isDerivationMissed -> { - updateWalletStoreWithMissedDerivation(walletStore) - } - walletManager == null -> { - updateWalletStoreWithUnreachable(walletStore) - } - else -> { - updateWalletManager(scanResponse, walletManager) - .map { - updateWalletManagerInStorage(userWalletId, walletManager) - } - .flatMap { - updateWalletStoreWithAmounts( - walletStore = walletStore, - updatedWallet = walletManager.wallet, - // FIXME: move DemoHelper to Demo core module maybe - isDemo = DemoHelper.isDemoCardId(scanResponse.card.cardId), - ) - } - .flatMap { - fetchWalletStoreRentIfNeeded(walletStore, walletManager) - } - .flatMapOnFailure { error -> - updateWalletStoreWithError( - walletStore = walletStore, - wallet = walletManager.wallet, - error = error, - ) - } - } - } - } - - private suspend fun updateWalletManager( - scanResponse: ScanResponse, - walletManager: WalletManager, - demoCardsDelay: Duration = with(Duration) { 500.milliseconds }, - ): CompletionResult = catching { - if (scanResponse.isDemoCard() || TestActions.testAmountInjectionForWalletManagerEnabled) { - delay(demoCardsDelay) - TestActions.testAmountInjectionForWalletManagerEnabled = false - } else { - walletManager.update() - } - } - - private suspend fun fetchWalletStoreRentIfNeeded( - walletStore: WalletStoreModel, - walletManager: WalletManager, - ): CompletionResult { - val rentProvider = walletManager as? RentProvider ?: return CompletionResult.Success(Unit) - - when (val result = rentProvider.minimalBalanceForRentExemption()) { - is Success -> { - val balance = walletManager.wallet.fundsAvailable(AmountType.Coin) - val outgoingTxs = walletManager.wallet.getPendingTransactions( - PendingTransactionType.Outgoing, - ).filterByCoin() - - val rentExempt = result.data - val setRent = if (outgoingTxs.isEmpty()) { - balance < rentExempt - } else { - val outgoingAmount = outgoingTxs.sumOf { it.amountValue ?: BigDecimal.ZERO } - val rest = balance.minus(outgoingAmount) - balance < rest - } - - updateWalletStoreWithRent( - walletStore = walletStore, - rent = if (setRent) { - WalletStoreModel.WalletRent( - rent = rentProvider.rentAmount(), - exemptionAmount = rentExempt, - ) - } else { - null - }, - ) - } - is Failure -> Unit - } - - return CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoreWithError(walletStore: WalletStoreModel, wallet: Wallet, error: TangemError) = - withContext(Dispatchers.Default) { - Timber.e( - error, - """ - Unable to fetch amounts - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - if (error is BlockchainSdkError) { - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithError( - wallet = wallet, - error = error, - ) - }, - ) - } - - CompletionResult.Success(Unit) - } else { - CompletionResult.Failure(error) - } - } - - private suspend fun updateWalletStoreWithAmounts( - walletStore: WalletStoreModel, - updatedWallet: Wallet, - isDemo: Boolean, - ) = withContext(Dispatchers.Default) { - Timber.d( - """ - Fetched amounts - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - if (isDemo) { - it.updateWithDemoAmounts(wallet = updatedWallet) - } else { - it.updateWithAmounts(wallet = updatedWallet) - } - }, - ) - } - - CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoreWithMissedDerivation(walletStore: WalletStoreModel) = - withContext(Dispatchers.Default) { - Timber.e( - """ - Missed derivation - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithMissedDerivation() - }, - ) - } - - CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoreWithUnreachable(walletStore: WalletStoreModel) = - withContext(Dispatchers.Default) { - Timber.e( - """ - Wallet manager is null - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithUnreachable() - }, - ) - } - - CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoresWithFiatRates( - walletStores: List, - fiatRates: Map, - ) = withContext(Dispatchers.Default) { - Timber.d( - """ - Fetched fiat rates - |- User wallets ids: ${walletStores.map { it.userWalletId }.distinct()} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStores( - walletStoresToUpdate = walletStores, - update = { - it.updateWithFiatRates(rates = fiatRates) - }, - ) - } - } - - private suspend fun updateWalletStoreWithRent(walletStore: WalletStoreModel, rent: WalletStoreModel.WalletRent?) = - withContext(Dispatchers.Default) { - Timber.d( - """ - Fetched wallet rent - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - |- Rent: $rent - """.trimIndent(), - ) - - if (rent != walletStore.walletRent) { - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithRent(rent) - }, - ) - } - } - } - - private suspend fun updateWalletManagerInStorage(userWalletId: UserWalletId, walletManager: WalletManager) = - withContext(Dispatchers.Default) { - WalletManagerStorage.update { prevManagers -> - val newManagersForUserWallet = prevManagers[userWalletId].orEmpty() - .addOrReplace(walletManager) { - it.wallet.blockchain == walletManager.wallet.blockchain - } - - prevManagers.apply { - set(userWalletId, newManagersForUserWallet) - } - } - } - - private suspend fun getWalletStores(userWallets: List): List { - return userWallets.map { it.walletId }.flatMap { userWalletId -> - WalletStoresStorage.getAll().firstOrNull()?.get(userWalletId).orEmpty() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt deleted file mode 100644 index 883b3a821d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt +++ /dev/null @@ -1,223 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation - -import com.tangem.blockchain.common.* -import com.tangem.common.CompletionResult -import com.tangem.common.catching -import com.tangem.common.doOnSuccess -import com.tangem.common.mapFailure -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.extensions.makeWalletManagerForApp -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.walletStores.WalletStoresError -import com.tangem.tap.domain.walletStores.storage.WalletManagerStorage -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.withContext -import timber.log.Timber - -internal class DefaultWalletManagersRepository( - private val walletManagerFactory: WalletManagerFactory, -) : WalletManagersRepository { - private val walletManagersStorage = WalletManagerStorage - - override suspend fun findOrMakeMultiCurrencyWalletManager( - userWallet: UserWallet, - blockchainNetwork: BlockchainNetwork, - ): CompletionResult { - return findOrMakeInternal(userWallet, blockchainNetwork) - } - - override suspend fun findOrMakeSingleCurrencyWalletManager( - userWallet: UserWallet, - ): CompletionResult { - return findOrMakeInternal(userWallet, blockchainNetwork = null) - } - - private suspend fun findOrMakeInternal( - userWallet: UserWallet, - blockchainNetwork: BlockchainNetwork?, - ): CompletionResult = withContext(Dispatchers.Default) { - val foundWalletManager = findWalletManager( - userWalletId = userWallet.walletId, - blockchain = blockchainNetwork?.blockchain, - derivationPath = blockchainNetwork?.derivationPath, - ) - - foundWalletManager?.updateTokens( - scanResponse = userWallet.scanResponse, - blockchainNetwork = blockchainNetwork, - ) - ?: makeAndStore(userWallet, blockchainNetwork) - } - - private suspend fun makeAndStore( - userWallet: UserWallet, - blockchainNetwork: BlockchainNetwork?, - ): CompletionResult { - val scanResponse = userWallet.scanResponse - val blockchain = blockchainNetwork?.blockchain - ?: scanResponse.cardTypesResolver.getBlockchain().let { blockchain -> - if (scanResponse.card.isTestCard) blockchain.getTestnetVersion() else blockchain - } - val derivationParams = getDerivationParams( - derivationPath = blockchainNetwork?.derivationPath, - derivationStyleProvider = scanResponse.derivationStyleProvider, - ) - - val walletManager = blockchain?.let { - walletManagerFactory.makeWalletManagerForApp( - scanResponse = userWallet.scanResponse, - blockchain = blockchain, - derivationParams = derivationParams, - ) - } - - return when { - blockchain == Blockchain.Unknown || blockchain == null -> { - val error = WalletStoresError.UnknownBlockchain() - Timber.e( - error, - """ - Unknown blockchain while creating wallet manager - |- User wallet ID: ${userWallet.walletId} - """.trimIndent(), - ) - CompletionResult.Failure(error) - } - walletManager != null -> { - walletManager.updateTokens( - scanResponse = scanResponse, - blockchainNetwork = blockchainNetwork, - ) - .doOnSuccess { store(userWallet.walletId, it) } - } - else -> { - val error = WalletStoresError.WalletManagerNotCreated(blockchain) - Timber.e( - error, - """ - Unable to create wallet manager - |- User wallet ID: ${userWallet.walletId} - |- Blockchain: $blockchain - |- Derivation path: ${blockchainNetwork?.derivationPath} - """.trimIndent(), - ) - CompletionResult.Failure(error) - } - } - } - - override suspend fun delete(userWalletIds: List): CompletionResult = catching { - walletManagersStorage.update { prevManagers -> - prevManagers.filterKeys { it !in userWalletIds } as HashMap> - } - } - - override suspend fun delete(userWalletId: UserWalletId, blockchain: Blockchain): CompletionResult = catching { - deleteInternal(userWalletId, blockchain) - } - - private suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager) { - walletManagersStorage.update { prevManagers -> - prevManagers.apply { - set( - key = userWalletId, - value = this[userWalletId].orEmpty() + walletManager, - ) - } - } - } - - private suspend fun deleteInternal(userWalletId: UserWalletId, blockchain: Blockchain?) { - walletManagersStorage.update { prevManagers -> - prevManagers.apply { - if (blockchain == null) { - set( - key = userWalletId, - value = emptyList(), - ) - } else { - set( - key = userWalletId, - value = this[userWalletId] - ?.filter { it.wallet.blockchain == blockchain } - .orEmpty(), - ) - } - } - } - } - - private fun WalletManager.updateTokens( - scanResponse: ScanResponse, - blockchainNetwork: BlockchainNetwork?, - ): CompletionResult { - val walletManager = this - return catching { - val tokens = blockchainNetwork?.tokens ?: listOfNotNull(scanResponse.cardTypesResolver.getPrimaryToken()) - - if (tokens != walletManager.cardTokens) { - // TODO: remove ability to manipulate with walletManager.cardTokens - walletManager.cardTokens.clear() - walletManager.wallet.removeAllTokens() - if (tokens.isNotEmpty()) { - walletManager.cardTokens.addAll(tokens) - // add empty amounts to prepare templates of tokens WalletDataModel - // see: WalletMangerWalletStoreBuilderImpl.build() - tokens.forEach { walletManager.wallet.setAmount(Amount(it)) } - } - } - - walletManager - } - .mapFailure { - val error = WalletStoresError.UpdateWalletManagerTokensError( - blockchain = walletManager.wallet.blockchain, - cause = it, - ) - Timber.e(error) - error - } - } - - private suspend fun findWalletManager( - userWalletId: UserWalletId, - blockchain: Blockchain?, - derivationPath: String?, - ): WalletManager? { - return walletManagersStorage.getAll() - .firstOrNull() - ?.get(userWalletId) - ?.let { userWalletManagers -> - if (blockchain == null) { - userWalletManagers.firstOrNull() - } else { - userWalletManagers.firstOrNull { - it.wallet.blockchain == blockchain && - it.wallet.publicKey.derivationPath?.rawPath == derivationPath - } - } - } - } - - private fun getDerivationParams( - derivationPath: String?, - derivationStyleProvider: DerivationStyleProvider, - ): DerivationParams? { - val derivationStyle = derivationStyleProvider.getDerivationStyle() ?: return null - - return if (derivationPath == null) { - DerivationParams.Default(derivationStyle) - } else { - DerivationParams.Custom(DerivationPath(derivationPath)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletStoresRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletStoresRepository.kt deleted file mode 100644 index fb78960bb1..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletStoresRepository.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation - -import com.tangem.common.CompletionResult -import com.tangem.common.catching -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.implementation.utils.isSameWalletStore -import com.tangem.tap.domain.walletStores.repository.implementation.utils.replaceWalletStore -import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithSelf -import com.tangem.tap.domain.walletStores.storage.WalletStoresStorage -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext - -internal class DefaultWalletStoresRepository : WalletStoresRepository { - private val walletStoresStorage = WalletStoresStorage - - override fun getAll(): Flow>> { - return walletStoresStorage.getAll() - } - - override fun get(userWalletId: UserWalletId): Flow> { - return getAll().map { it[userWalletId].orEmpty() } - } - - override suspend fun getSync(userWalletId: UserWalletId): List { - return get(userWalletId).firstOrNull() ?: emptyList() - } - - override suspend fun contains(userWalletId: UserWalletId): Boolean { - return getSync(userWalletId).isNotEmpty() - } - - override suspend fun delete(userWalletsIds: List): CompletionResult = catching { - walletStoresStorage.update { prevStores -> - prevStores.filterKeys { it !in userWalletsIds } as HashMap> - } - } - - override suspend fun deleteDifference( - userWalletId: UserWalletId, - currentBlockchains: List, - ): CompletionResult = catching { - if (currentBlockchains != getSync(userWalletId)) { - walletStoresStorage.update { prevStores -> - prevStores.apply { - this[userWalletId] = this[userWalletId] - ?.filter { it.blockchainWalletData.currency in currentBlockchains } - .orEmpty() - } - } - } - } - - override suspend fun clear(): CompletionResult = catching { - walletStoresStorage.update { hashMapOf() } - } - - override suspend fun storeOrUpdate( - userWalletId: UserWalletId, - walletStore: WalletStoreModel, - ): CompletionResult = catching { - walletStoresStorage.update { prevStores -> - prevStores.addOrUpdate(userWalletId, walletStore) - } - } - - override suspend fun update( - userWalletId: UserWalletId, - operation: (List) -> WalletStoreModel?, - ): CompletionResult = catching { - val walletStores = getSync(userWalletId).toMutableList() - val updatedWalletStore = operation(walletStores) ?: return CompletionResult.Success(Unit) - - val index = walletStores.indexOfFirst { it.isSameWalletStore(updatedWalletStore) } - if (index == -1 || updatedWalletStore == walletStores[index]) return CompletionResult.Success(Unit) - - walletStores[index] = updatedWalletStore - - walletStoresStorage.update { prevStores -> - prevStores.apply { - this[userWalletId] = walletStores - } - } - } - - private suspend fun HashMap>.addOrUpdate( - userWalletId: UserWalletId, - walletStore: WalletStoreModel, - ): HashMap> = withContext(Dispatchers.Default) { - val currentWalletStores = this@addOrUpdate - val userWalletStores = currentWalletStores[userWalletId] - - if (userWalletStores.isNullOrEmpty()) { - currentWalletStores.apply { - set(userWalletId, listOf(walletStore)) - } - } else { - val currentWalletStore = userWalletStores.find(walletStore::isSameWalletStore) - if (currentWalletStore == null) { - currentWalletStores.apply { - set(userWalletId, userWalletStores + walletStore) - } - } else { - currentWalletStores.replaceWalletStore( - walletStoreToUpdate = currentWalletStore, - update = { it.updateWithSelf(walletStore) }, - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt deleted file mode 100644 index c7eef5a974..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation.utils - -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.core.TangemError -import com.tangem.domain.common.extensions.amountToCreateAccount -import com.tangem.tap.domain.getFirstToken -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.getPendingTransactions -import java.math.BigDecimal - -internal fun WalletDataModel.updateWithFiatRate(fiatRate: BigDecimal?): WalletDataModel { - return this.copy( - fiatRate = fiatRate, - ) -} - -internal fun List.updateWithFiatRates(fiatRates: Map): List { - return this.map { walletData -> - val rate = fiatRates[walletData.currency.coinId]?.toBigDecimal() - walletData.updateWithFiatRate(rate) - } -} - -internal fun List.updateWithAmounts(wallet: Wallet): List { - return this.map { walletData -> - walletData.updateWithAmount(wallet) - } -} - -internal fun WalletDataModel.updateWithAmount(wallet: Wallet): WalletDataModel { - val pendingTransactions = wallet.getPendingTransactions() - return this.copy( - status = when (currency) { - is Currency.Blockchain -> { - val amount = wallet.fundsAvailable(AmountType.Coin) - if (pendingTransactions.isEmpty()) { - WalletDataModel.VerifiedOnline(amount) - } else { - WalletDataModel.TransactionInProgress(amount, pendingTransactions) - } - } - - is Currency.Token -> { - val token = currency.token - val amount = wallet.fundsAvailable(AmountType.Token(token)) - val hasTokenPendingTransactions = pendingTransactions.any { - it.transactionData.amount.currencySymbol == token.symbol - } - - when { - hasTokenPendingTransactions -> { - WalletDataModel.TransactionInProgress(amount, pendingTransactions) - } - - pendingTransactions.isNotEmpty() -> { - // FIXME: Necessary to avoid passing pending transactions - // because SameCurrencyTransactionInProgress didn't use it. It used only to define main button - // availability. UI layer turned off pending transaction visibility for this state. - WalletDataModel.SameCurrencyTransactionInProgress(amount, pendingTransactions) - } - else -> { - WalletDataModel.VerifiedOnline(amount) - } - } - } - }, - ) -} - -internal fun WalletDataModel.updateWithDemoAmount(wallet: Wallet): WalletDataModel { - val amount = DemoHelper.config.getBalance(wallet.blockchain) - wallet.setAmount(amount) - return this.copy( - status = WalletDataModel.VerifiedOnline(amount = amount.value ?: BigDecimal.ZERO), - ) -} - -internal fun List.updateWithDemoAmounts(wallet: Wallet): List { - return this.map { walletData -> - walletData.updateWithDemoAmount(wallet) - } -} - -internal fun WalletDataModel.updateWithError(wallet: Wallet, error: TangemError): WalletDataModel { - return this.copy( - status = when (error) { - is BlockchainSdkError.AccountNotFound -> { - val amountToCreateAccount = wallet.blockchain - .amountToCreateAccount(wallet.getFirstToken()) - - if (amountToCreateAccount != null) { - WalletDataModel.NoAccount( - amountToCreateAccount = amountToCreateAccount, - ) - } else { - WalletDataModel.Unreachable( - errorMessage = error.customMessage, - ) - } - } - else -> WalletDataModel.Unreachable( - errorMessage = error.customMessage, - ) - }, - ) -} - -internal fun List.updateWithError(wallet: Wallet, error: TangemError): List { - return this.map { walletData -> - walletData.updateWithError(wallet, error) - } -} - -internal fun WalletDataModel.updateWithSelf(newWalletData: WalletDataModel): WalletDataModel { - val oldWalletData = this - val oldStatus = oldWalletData.status - return oldWalletData.copy( - status = when (val newStatus = newWalletData.status) { - is WalletDataModel.Loading -> when (oldStatus) { - is WalletDataModel.MissedDerivation -> WalletDataModel.Loading - else -> oldStatus - } - is WalletDataModel.MissedDerivation, - is WalletDataModel.NoAccount, - is WalletDataModel.Unreachable, - is WalletDataModel.SameCurrencyTransactionInProgress, - is WalletDataModel.TransactionInProgress, - is WalletDataModel.VerifiedOnline, - -> newStatus - }, - existentialDeposit = newWalletData.existentialDeposit, - walletAddresses = newWalletData.walletAddresses?.copy( - selectedAddress = oldWalletData.walletAddresses?.selectedAddress - ?: newWalletData.walletAddresses.selectedAddress, - ), - fiatRate = newWalletData.fiatRate ?: oldWalletData.fiatRate, - ) -} - -internal fun List.updateWithMissedDerivation(): List { - return this.map { walletData -> - walletData.copy( - status = WalletDataModel.MissedDerivation, - ) - } -} - -internal fun List.updateWithUnreachable(): List { - return this.map { walletData -> - walletData.copy( - status = WalletDataModel.Unreachable( - errorMessage = null, - amount = walletData.status.amount, - ), - ) - } -} - -internal fun List.updateWithSelf(newWalletsData: List): List { - val oldWalletsData = this - val updatedWalletsData = arrayListOf() - - newWalletsData.forEach { newWalletData -> - val walletDataToUpdate = oldWalletsData.firstOrNull(newWalletData::isSameWalletData) - if (walletDataToUpdate != null) { - updatedWalletsData.add(walletDataToUpdate.updateWithSelf(newWalletData)) - } else { - updatedWalletsData.add(newWalletData) - } - } - - return updatedWalletsData -} - -internal fun List.updateSelectedAddress( - currency: Currency, - addressType: AddressType, -): List { - val index = this.indexOfFirst { it.currency == currency } - if (index == -1) return this - - val oldWalletData = this[index] - val updatedWalletData = oldWalletData.updateSelectedAddress(addressType) - if (oldWalletData == updatedWalletData) return this - - return this.toMutableList().apply { - this[index] = updatedWalletData - } -} - -internal fun WalletDataModel.updateSelectedAddress(addressType: AddressType): WalletDataModel { - val addresses = walletAddresses ?: return this - val selectedAddress = addresses.list - .firstOrNull { it.type == addressType } - ?: addresses.selectedAddress - - return this.copy( - walletAddresses = addresses.copy( - selectedAddress = selectedAddress, - ), - ) -} - -internal fun WalletDataModel.isSameWalletData(other: WalletDataModel): Boolean { - return this.currency == other.currency -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt deleted file mode 100644 index bc6c07563d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation.utils - -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.core.TangemError -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import timber.log.Timber - -internal inline fun HashMap>.replaceWalletStore( - walletStoreToUpdate: WalletStoreModel, - update: (walletStore: WalletStoreModel) -> WalletStoreModel, -): HashMap> { - return replaceWalletStores(listOf(walletStoreToUpdate), update) -} - -internal inline fun HashMap>.replaceWalletStores( - walletStoresToUpdate: List, - update: (walletStore: WalletStoreModel) -> WalletStoreModel, -): HashMap> { - return this.apply { - val currentWalletStores = this - walletStoresToUpdate - .groupBy { it.userWalletId } - .forEach { (userWalletId, walletStoresToUpdate) -> - currentWalletStores[userWalletId] = currentWalletStores[userWalletId] - ?.replaceWalletStores(walletStoresToUpdate, update) - .orEmpty() - } - } -} - -internal fun WalletStoreModel.updateWithError(wallet: Wallet, error: TangemError): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithError( - wallet = wallet, - error = error, - ), - ) -} - -internal fun WalletStoreModel.updateWithAmounts(wallet: Wallet): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithAmounts(wallet = wallet), - ) -} - -internal fun WalletStoreModel.updateWithDemoAmounts(wallet: Wallet): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithDemoAmounts(wallet = wallet), - ) -} - -internal fun WalletStoreModel.updateWithFiatRates(rates: Map): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithFiatRates(rates), - ) -} - -internal fun WalletStoreModel.updateWithSelf(newWalletStore: WalletStoreModel): WalletStoreModel { - val oldStore = this - return oldStore.copy( - derivationPath = newWalletStore.derivationPath, - walletsData = oldStore.walletsData.updateWithSelf(newWalletStore.walletsData), - walletRent = newWalletStore.walletRent, - blockchainNetwork = newWalletStore.blockchainNetwork, - walletManager = newWalletStore.walletManager, - ) -} - -internal fun WalletStoreModel.updateWithMissedDerivation(): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithMissedDerivation(), - ) -} - -internal fun WalletStoreModel.updateWithUnreachable(): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithUnreachable(), - ) -} - -internal fun WalletStoreModel.updateWithRent(rent: WalletStoreModel.WalletRent?): WalletStoreModel { - return this.copy( - walletRent = rent, - ) -} - -internal fun WalletStoreModel.updateSelectedAddress(currency: Currency, addressType: AddressType): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateSelectedAddress(currency, addressType), - ) -} - -private inline fun List.replaceWalletStores( - walletStoresToUpdate: List, - update: (walletStore: WalletStoreModel) -> WalletStoreModel, -): List { - val mutableStores = ArrayList(this) - - walletStoresToUpdate.forEach { walletStoreToUpdate -> - val index = mutableStores.indexOfFirst(walletStoreToUpdate::isSameWalletStore) - // Can be possible if user hides wallet store when it's tokens is loading - if (index == -1) return@forEach - - val currentWalletStore = mutableStores[index] - val updatedWalletStore = update(currentWalletStore) - - if (currentWalletStore != updatedWalletStore) { - Timber.d( - """ - Update wallet store in storage - |- User wallet ID: ${updatedWalletStore.userWalletId} - |- Blockchain: ${updatedWalletStore.blockchain} - |- Derivation path: ${updatedWalletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - mutableStores[index] = updatedWalletStore - } - } - - return mutableStores -} - -internal fun WalletStoreModel.isSameWalletStore(other: WalletStoreModel): Boolean { - return this.userWalletId == other.userWalletId && - this.blockchain == other.blockchain && - this.derivationPath == other.derivationPath -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletManagerStorage.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletManagerStorage.kt deleted file mode 100644 index 3d492d01da..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletManagerStorage.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.tap.domain.walletStores.storage - -import com.tangem.blockchain.common.WalletManager -import com.tangem.domain.wallets.models.UserWalletId -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock - -internal object WalletManagerStorage { - private val managers = MutableSharedFlow>>(replay = 1) - private val mutex = Mutex() - - init { - managers.tryEmit(hashMapOf()) - } - - fun getAll(): SharedFlow>> { - return managers.asSharedFlow() - } - - suspend fun update( - f: suspend (HashMap>) -> HashMap>, - ) { - while (mutex.isLocked) { - delay(timeMillis = 60) - } - - mutex.withLock { - val prevState = managers.first() - managers.emit(f(prevState)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletStoresStorage.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletStoresStorage.kt deleted file mode 100644 index 883783db86..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletStoresStorage.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.tap.domain.walletStores.storage - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock - -internal object WalletStoresStorage { - private val stores = MutableSharedFlow>>(replay = 1) - private val mutex = Mutex() - - init { - stores.tryEmit(hashMapOf()) - } - - fun getAll(): SharedFlow>> { - return stores.asSharedFlow() - } - - suspend fun update( - f: suspend (HashMap>) -> HashMap>, - ) { - while (mutex.isLocked) { - delay(timeMillis = 60) - } - - mutex.withLock { - val prevState = stores.first() - stores.emit(f(prevState)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 4a83bd8b41..fa10156fe6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -386,7 +386,6 @@ class DetailsMiddleware { private suspend fun deleteSavedWalletsAndAccessCodes(): CompletionResult { return userWalletsListManager.clear() - .flatMap { walletStoresManager.clear() } .doOnSuccess { Analytics.send(Settings.AppSettings.SaveWalletSwitcherChanged(AnalyticsParam.OnOffState.Off)) deleteSavedAccessCodes() diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 482671ce51..9ac98c675e 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -158,7 +158,6 @@ internal class WelcomeMiddleware { private suspend fun disableUserWalletsSaving() { userWalletsListManager.clear() - .flatMap { walletStoresManager.clear() } .flatMap { tangemSdkManager.clearSavedUserCodes() } .doOnFailure { e -> Timber.e(e, "Unable to clear user wallets") diff --git a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt index f5e834bb53..8d7150a02c 100644 --- a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt @@ -15,7 +15,6 @@ import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletStores.WalletStoresManager import com.tangem.tap.network.exchangeServices.ExchangeService import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -45,7 +44,6 @@ class AppStateHolder @Inject constructor() : WalletsStateHolder, ReduxNavControl var userTokensRepository: UserTokensRepository? = null var mainStore: Store? = null var tangemSdkManager: TangemSdkManager? = null - var walletStoresManager: WalletStoresManager? = null var appFiatCurrency: FiatCurrency = FiatCurrency.Default var exchangeService: ExchangeService? = null