From eaba6ee88dd7ba4c932b715d96096112c41ddb92 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 21 Sep 2023 14:29:41 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../DefaultWalletManagersRepository.kt | 26 ++++---- .../middlewares/TradeCryptoMiddleware.kt | 21 ++++-- .../tap/proxy/TransactionManagerImpl.kt | 21 +++++- .../tangem/tap/proxy/UserWalletManagerImpl.kt | 32 ++++++++-- .../com/tangem/tap/proxy/di/ProxyModule.kt | 14 +++- .../repository/DefaultCurrenciesRepository.kt | 18 +++++- .../DefaultMarketCryptoCurrencyRepository.kt | 6 +- .../utils/WalletManagerFactory.kt | 22 +++---- features/swap/data/build.gradle.kts | 12 ++++ .../tangem/feature/swap/SwapRepositoryImpl.kt | 39 +++++++++++ features/swap/domain/build.gradle.kts | 25 +++++++- .../feature/swap/domain/SwapInteractor.kt | 3 +- .../feature/swap/domain/SwapInteractorImpl.kt | 64 +++++++++++++++---- .../feature/swap/domain/SwapRepository.kt | 5 ++ ...yConverter.kt => SwapCurrencyConverter.kt} | 2 +- .../swap/domain/di/SwapDomainModule.kt | 31 +++++++-- features/swap/presentation/build.gradle.kts | 1 + .../feature/swap/presentation/SwapFragment.kt | 1 + .../feature/swap/viewmodels/SwapViewModel.kt | 9 ++- .../wallet/viewmodels/WalletViewModel.kt | 5 +- .../tangem/lib/crypto/UserWalletManager.kt | 6 +- 21 files changed, 282 insertions(+), 81 deletions(-) rename features/swap/domain/src/main/java/com/tangem/feature/swap/domain/converters/{CryptoCurrencyConverter.kt => SwapCurrencyConverter.kt} (96%) 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 index 006bca6820..883b3a821d 100644 --- 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 @@ -1,18 +1,17 @@ package com.tangem.tap.domain.walletStores.repository.implementation import com.tangem.blockchain.common.* -import com.tangem.blockchain.common.derivation.DerivationStyle 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.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.extensions.makeWalletManagerForApp import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.models.scan.CardDTO +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 @@ -70,7 +69,7 @@ internal class DefaultWalletManagersRepository( } val derivationParams = getDerivationParams( derivationPath = blockchainNetwork?.derivationPath, - card = scanResponse.card, + derivationStyleProvider = scanResponse.derivationStyleProvider, ) val walletManager = blockchain?.let { @@ -209,17 +208,16 @@ internal class DefaultWalletManagersRepository( } } - private fun getDerivationParams(derivationPath: String?, card: CardDTO): DerivationParams? { - return derivationPath?.let { - DerivationParams.Custom( - path = DerivationPath(it), - ) - } ?: if (!card.settings.isHDWalletAllowed) { - null - } else if (card.useOldStyleDerivation) { - DerivationParams.Default(DerivationStyle.LEGACY) + private fun getDerivationParams( + derivationPath: String?, + derivationStyleProvider: DerivationStyleProvider, + ): DerivationParams? { + val derivationStyle = derivationStyleProvider.getDerivationStyle() ?: return null + + return if (derivationPath == null) { + DerivationParams.Default(derivationStyle) } else { - DerivationParams.Default(DerivationStyle.NEW) + DerivationParams.Custom(DerivationPath(derivationPath)) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index f0110e52fa..62cd206aab 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -13,6 +13,7 @@ import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Network import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Token @@ -53,11 +54,18 @@ class TradeCryptoMiddleware { is TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action) is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) is TradeCryptoAction.Swap -> { - openSwap(currency = store.state.walletState.selectedWalletData?.currency?.toSwapCurrency()) + openSwap( + currency = store.state.walletState.selectedWalletData?.currency?.toSwapCurrency(), + derivationPath = store.state.walletState.selectedWalletData?.currency?.derivationPath, + ) } is TradeCryptoAction.New.Buy -> proceedNewBuyAction(state, action) is TradeCryptoAction.New.Sell -> proceedNewSellAction(action) - is TradeCryptoAction.New.Swap -> openSwap(currency = action.cryptoCurrency.toSwapCurrency()) + is TradeCryptoAction.New.Swap -> openSwap( + currency = action.cryptoCurrency.toSwapCurrency(), + derivationPath = action.cryptoCurrency.network.derivationPath.value, + network = action.cryptoCurrency.network, + ) is TradeCryptoAction.New.SendToken -> handleNewSendToken(action = action) is TradeCryptoAction.New.SendCoin -> handleNewSendCoin(action = action) } @@ -240,10 +248,11 @@ class TradeCryptoMiddleware { )?.let { store.dispatchOpenUrl(it) } } - private fun openSwap(currency: SwapCurrency?) { + private fun openSwap(currency: SwapCurrency?, derivationPath: String?, network: Network? = null) { val bundle = bundleOf( SwapFragment.CURRENCY_BUNDLE_KEY to Json.encodeToString(currency), - SwapFragment.DERIVATION_PATH to store.state.walletState.selectedWalletData?.currency?.derivationPath, + SwapFragment.DERIVATION_PATH to derivationPath, + SwapFragment.NETWORK to network, ) store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Swap, bundle = bundle)) @@ -266,11 +275,11 @@ class TradeCryptoMiddleware { } is CryptoCurrency.Token -> { SwapCurrency.NonNativeToken( - id = id.value, + id = id.rawCurrencyId ?: "", name = name, symbol = symbol, networkId = blockchain.toNetworkId(), - logoUrl = getIconUrl(id.value), + logoUrl = getIconUrl(id.rawCurrencyId ?: ""), contractAddress = contractAddress, decimalCount = decimals, ) diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt index 57afd33a26..3df732e168 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -17,6 +17,8 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult @@ -35,6 +37,8 @@ class TransactionManagerImpl( private val appStateHolder: AppStateHolder, private val analytics: AnalyticsEventHandler, private val cardSdkConfigRepository: CardSdkConfigRepository, + private val walletManagersFacade: WalletManagersFacade, + private val walletFeatureToggles: WalletFeatureToggles, ) : TransactionManager { override suspend fun sendApproveTransaction( @@ -409,9 +413,20 @@ class TransactionManagerImpl( } } - private fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager { - val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) - val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork) + private suspend fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager { + val walletManager = if (walletFeatureToggles.isRedesignedScreenEnabled) { + val selectedUserWallet = requireNotNull( + appStateHolder.userWalletsListManager?.selectedUserWalletSync, + ) { "userWallet or userWalletsListManager is null" } + walletManagersFacade.getOrCreateWalletManager( + selectedUserWallet, + blockchain, + derivationPath, + ) + } else { + val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) + appStateHolder.walletState?.getWalletManager(blockchainNetwork) + } return requireNotNull(walletManager) { "no wallet manager found" } } diff --git a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt index 0403ad4383..ae87ca5aec 100644 --- a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt @@ -11,6 +11,8 @@ import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.userwallets.UserWalletIdBuilder +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.UserWalletManager import com.tangem.lib.crypto.models.Currency import com.tangem.lib.crypto.models.Currency.NativeToken @@ -29,6 +31,8 @@ import com.tangem.tap.features.wallet.models.Currency as WalletCurrency class UserWalletManagerImpl( private val appStateHolder: AppStateHolder, + private val walletManagersFacade: WalletManagersFacade, + private val walletFeatureToggles: WalletFeatureToggles, ) : UserWalletManager { override suspend fun getUserTokens( @@ -142,13 +146,13 @@ class UserWalletManagerImpl( } } - override fun getWalletAddress(networkId: String, derivationPath: String?): String { + override suspend fun getWalletAddress(networkId: String, derivationPath: String?): String { val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) return walletManager.wallet.address } - override fun getLastTransactionHash(networkId: String, derivationPath: String?): String? { + override suspend fun getLastTransactionHash(networkId: String, derivationPath: String?): String? { val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) return walletManager.wallet.recentTransactions @@ -187,7 +191,7 @@ class UserWalletManagerImpl( return balances } - override fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? { + override suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? { val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) return walletManager.wallet.amounts.firstNotNullOfOrNull { @@ -220,10 +224,24 @@ class UserWalletManagerImpl( appStateHolder.mainStore?.dispatchOnMain(WalletAction.LoadData.Refresh) } - @kotlin.jvm.Throws(IllegalArgumentException::class) - private fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager { - val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) - return requireNotNull(appStateHolder.walletState?.getWalletManager(blockchainNetwork)) { + @Throws(IllegalArgumentException::class) + private suspend fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager { + val walletManager = if (walletFeatureToggles.isRedesignedScreenEnabled) { + val selectedUserWallet = requireNotNull( + appStateHolder.userWalletsListManager?.selectedUserWalletSync, + ) { "userWallet or userWalletsListManager is null" } + walletManagersFacade.getOrCreateWalletManager( + selectedUserWallet, + blockchain, + derivationPath, + ) + } else { + val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) + return requireNotNull(appStateHolder.walletState?.getWalletManager(blockchainNetwork)) { + "No wallet manager found" + } + } + return requireNotNull(walletManager) { "No wallet manager found" } } diff --git a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt index 4a4acf7415..c4b935a3d5 100644 --- a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt +++ b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt @@ -6,7 +6,9 @@ import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.DerivationManager import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager @@ -32,9 +34,15 @@ class ProxyModule { @Provides @Singleton - fun provideUserWalletManager(appStateHolder: AppStateHolder): UserWalletManager { + fun provideUserWalletManager( + appStateHolder: AppStateHolder, + walletManagersFacade: WalletManagersFacade, + walletFeatureToggles: WalletFeatureToggles, + ): UserWalletManager { return UserWalletManagerImpl( appStateHolder = appStateHolder, + walletManagersFacade = walletManagersFacade, + walletFeatureToggles = walletFeatureToggles, ) } @@ -44,11 +52,15 @@ class ProxyModule { appStateHolder: AppStateHolder, analytics: AnalyticsEventHandler, cardSdkConfigRepository: CardSdkConfigRepository, + walletManagersFacade: WalletManagersFacade, + walletFeatureToggles: WalletFeatureToggles, ): TransactionManager { return TransactionManagerImpl( appStateHolder = appStateHolder, analytics = analytics, cardSdkConfigRepository = cardSdkConfigRepository, + walletManagersFacade = walletManagersFacade, + walletFeatureToggles = walletFeatureToggles, ) } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index e29095c31d..855817e2da 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -65,13 +65,25 @@ internal class DefaultCurrenciesRepository( lazyMessage = { "Saved tokens empty. Can not perform add currencies action" }, ) + val filteredCurrencies = currencies.toMutableList() + filteredCurrencies.filterNot { currency -> + val blockchain = getBlockchain(networkId = currency.network.id) + val networkId = blockchain.toNetworkId() + val contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress + savedCurrencies.tokens.firstOrNull { token -> + token.contractAddress == contractAddress && + token.networkId == networkId && + token.derivationPath == currency.network.derivationPath.value + } != null + } + val newCoins = createCoinsForNewTokens( userWalletId = userWalletId, - newTokens = currencies.filterIsInstance(), + newTokens = filteredCurrencies.filterIsInstance(), savedCurrencies = savedCurrencies.tokens, ) - val newCurrencies = newCoins + currencies + val newCurrencies = newCoins + filteredCurrencies storeAndPushTokens( userWalletId = userWalletId, @@ -276,7 +288,7 @@ internal class DefaultCurrenciesRepository( private suspend fun fetchUserMarketCoinsByIds(userWalletId: UserWalletId, userTokens: UserTokensResponse) { try { val networkIds = userTokens.tokens.joinToString(separator = ",") { it.networkId } - val response = tangemTechApi.getCoins(networkIds) + val response = tangemTechApi.getCoins(networkIds = networkIds) userMarketCoinsStore.store(userWalletId, response) } catch (e: Throwable) { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultMarketCryptoCurrencyRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultMarketCryptoCurrencyRepository.kt index 891a7c14d2..838775885f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultMarketCryptoCurrencyRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultMarketCryptoCurrencyRepository.kt @@ -1,6 +1,8 @@ package com.tangem.data.tokens.repository +import com.tangem.blockchain.common.Blockchain import com.tangem.datasource.local.token.UserMarketCoinsStore +import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository import com.tangem.domain.wallets.models.UserWalletId @@ -10,9 +12,11 @@ class DefaultMarketCryptoCurrencyRepository( ) : MarketCryptoCurrencyRepository { override suspend fun isExchangeable(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Boolean { + val blockchain = Blockchain.fromId(cryptoCurrencyId.rawNetworkId) + val apiNetworkId = blockchain.toNetworkId() return userMarketCoinsStore.getSyncOrNull(userWalletId)?.coins ?.firstOrNull { it.id == cryptoCurrencyId.rawCurrencyId } ?.networks - ?.firstOrNull { it.networkId == cryptoCurrencyId.rawNetworkId }?.exchangeable ?: false + ?.firstOrNull { it.networkId == apiNetworkId }?.exchangeable ?: false } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt index 770721f3f7..cd0b2a670b 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt @@ -1,13 +1,14 @@ package com.tangem.domain.walletmanager.utils -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.DerivationParams +import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager -import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation +import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.makeWalletManagerForApp -import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.ScanResponse internal class WalletManagerFactory( @@ -23,7 +24,7 @@ internal class WalletManagerFactory( blockchain: Blockchain, derivationPath: DerivationPath?, ): WalletManager? { - val derivationParams = getDerivationParams(derivationPath, scanResponse.card) + val derivationParams = getDerivationParams(derivationPath, scanResponse.derivationStyleProvider) return sdkWalletManagerFactory.makeWalletManagerForApp( scanResponse = scanResponse, @@ -32,12 +33,11 @@ internal class WalletManagerFactory( ) } - private fun getDerivationParams(derivationPath: DerivationPath?, card: CardDTO): DerivationParams? { - val derivationStyle = when { - !card.settings.isHDWalletAllowed -> return null - card.useOldStyleDerivation -> DerivationStyle.LEGACY - else -> DerivationStyle.NEW - } + private fun getDerivationParams( + derivationPath: DerivationPath?, + derivationStyleProvider: DerivationStyleProvider, + ): DerivationParams? { + val derivationStyle = derivationStyleProvider.getDerivationStyle() ?: return null return if (derivationPath == null) { DerivationParams.Default(derivationStyle) diff --git a/features/swap/data/build.gradle.kts b/features/swap/data/build.gradle.kts index 87bb14fe6b..f91ab4c7c0 100644 --- a/features/swap/data/build.gradle.kts +++ b/features/swap/data/build.gradle.kts @@ -16,6 +16,18 @@ dependencies { /** Network */ implementation(deps.retrofit) + /** Domain */ + implementation(projects.domain.tokens.models) + implementation(projects.domain.legacy) + implementation(projects.domain.models) + implementation(projects.domain.wallets.models) + + /** Data */ + implementation(projects.data.tokens) + + /** Tangem SDKs */ + implementation(deps.tangem.blockchain) + /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt index 09e9cea01c..f939cde5dd 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt @@ -1,11 +1,18 @@ package com.tangem.feature.swap +import com.tangem.blockchain.common.Blockchain +import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.datasource.api.oneinch.OneInchApi import com.tangem.datasource.api.oneinch.OneInchApiFactory import com.tangem.datasource.api.oneinch.OneInchErrorsHandler import com.tangem.datasource.api.oneinch.errors.OneIncResponseException import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.config.ConfigManager +import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.common.util.derivationStyleProvider +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.swap.converters.ApproveConverter import com.tangem.feature.swap.converters.QuotesConverter import com.tangem.feature.swap.converters.SwapConverter @@ -20,6 +27,7 @@ import com.tangem.feature.swap.domain.models.mapErrors import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import javax.inject.Inject +import com.tangem.blockchain.common.Token as SdkToken internal class SwapRepositoryImpl @Inject constructor( private val tangemTechApi: TangemTechApi, @@ -152,6 +160,37 @@ internal class SwapRepositoryImpl @Inject constructor( return configManager.config.swapReferrerAccount?.fee?.toDoubleOrNull() ?: 0.0 } + override suspend fun getCryptoCurrency( + userWallet: UserWallet, + currency: Currency, + network: Network, + ): CryptoCurrency? { + val blockchain = Blockchain.fromNetworkId(currency.networkId) ?: return null + val cryptoCurrencyFactory = CryptoCurrencyFactory() + return when (currency) { + is Currency.NativeToken -> { + cryptoCurrencyFactory.createCoin( + blockchain = blockchain, + extraDerivationPath = network.derivationPath.value, + derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider, + ) + } + is Currency.NonNativeToken -> { + val sdkToken = SdkToken( + symbol = currency.symbol, + contractAddress = currency.contractAddress, + decimals = currency.decimalCount, + ) + cryptoCurrencyFactory.createToken( + sdkToken = sdkToken, + blockchain = blockchain, + extraDerivationPath = network.derivationPath.value, + derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider, + ) + } + } as CryptoCurrency + } + private fun getOneInchApi(networkId: String): OneInchApi { return oneInchApiFactory.getApi(networkId) } diff --git a/features/swap/domain/build.gradle.kts b/features/swap/domain/build.gradle.kts index d5dec7c6d0..b757775fd0 100644 --- a/features/swap/domain/build.gradle.kts +++ b/features/swap/domain/build.gradle.kts @@ -1,19 +1,38 @@ plugins { - alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) alias(deps.plugins.kotlin.kapt) alias(deps.plugins.kotlin.serialization) id("configuration") } +android { + namespace = "com.tangem.domain.swap" +} + dependencies { /** Libs */ - implementation(project(":libs:crypto")) - implementation(project(":core:utils")) + implementation(projects.libs.crypto) /** DI */ implementation(deps.hilt.core) kapt(deps.hilt.kapt) + /** Domain */ + implementation(projects.domain.tokens) + implementation(projects.domain.tokens.models) + implementation(projects.domain.wallets) + implementation(projects.domain.wallets.models) + + /** Core modules */ + implementation(projects.core.utils) + + /** Feature Apis */ + implementation(projects.features.wallet.api) + /** Other Libraries **/ implementation(deps.kotlin.serialization) + implementation(deps.kotlin.coroutines) + implementation(deps.arrow.core) + implementation(deps.timber) } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt index 2415ba615a..73e969dc06 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt @@ -1,5 +1,6 @@ package com.tangem.feature.swap.domain +import com.tangem.domain.tokens.models.Network import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.Currency import com.tangem.feature.swap.domain.models.domain.PermissionOptions @@ -7,7 +8,7 @@ import com.tangem.feature.swap.domain.models.ui.* interface SwapInteractor { - fun initDerivationPath(derivationPath: String?) + fun initDerivationPathAndNetwork(derivationPath: String?, network: Network?) /** * Init tokens to swap, load tokens list available to swap for given network diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index f3f193b180..17556ec17b 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -1,37 +1,48 @@ package com.tangem.feature.swap.domain +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.CurrenciesRepository +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.feature.swap.domain.cache.SwapDataCache -import com.tangem.feature.swap.domain.converters.CryptoCurrencyConverter +import com.tangem.feature.swap.domain.converters.SwapCurrencyConverter import com.tangem.feature.swap.domain.models.DataError import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.* import com.tangem.feature.swap.domain.models.domain.Currency import com.tangem.feature.swap.domain.models.toStringWithRightOffset import com.tangem.feature.swap.domain.models.ui.* +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult import com.tangem.utils.toFiatString +import timber.log.Timber import java.math.BigDecimal import java.math.RoundingMode import javax.inject.Inject -@Suppress("LargeClass") +@Suppress("LargeClass", "LongParameterList") internal class SwapInteractorImpl @Inject constructor( private val transactionManager: TransactionManager, private val userWalletManager: UserWalletManager, private val repository: SwapRepository, private val cache: SwapDataCache, private val allowPermissionsHandler: AllowPermissionsHandler, + private val currenciesRepository: CurrenciesRepository, + private val walletFeatureToggles: WalletFeatureToggles, + private val getSelectedWalletUseCase: GetSelectedWalletUseCase, ) : SwapInteractor { - private val cryptoCurrencyConverter = CryptoCurrencyConverter() + private val swapCurrencyConverter = SwapCurrencyConverter() private val amountFormatter = AmountFormatter() private var derivationPath: String? = null + private var network: Network? = null - override fun initDerivationPath(derivationPath: String?) { + override fun initDerivationPathAndNetwork(derivationPath: String?, network: Network?) { this.derivationPath = derivationPath + this.network = network } override suspend fun initTokensToSwap(initialCurrency: Currency): TokensDataState { @@ -55,7 +66,7 @@ internal class SwapInteractorImpl @Inject constructor( allLoadedTokens.firstOrNull { it.symbol == token.symbol }?.let { loadedOnWalletsMap.add(it.symbol) it - } ?: cryptoCurrencyConverter.convertBack(token) + } ?: swapCurrencyConverter.convertBack(token) } val loadedTokens = allLoadedTokens .filter { @@ -195,7 +206,7 @@ internal class SwapInteractorImpl @Inject constructor( txData = SwapTxData( networkId = networkId, amountToSend = amount, - currencyToSend = cryptoCurrencyConverter.convert(currencyToSend), + currencyToSend = swapCurrencyConverter.convert(currencyToSend), feeAmount = fee.feeValue, gasLimit = fee.gasLimit, destinationAddress = swapStateData.swapModel.transaction.toWalletAddress, @@ -210,8 +221,11 @@ internal class SwapInteractorImpl @Inject constructor( ) return when (result) { is SendTxResult.Success -> { - userWalletManager.addToken(cryptoCurrencyConverter.convert(currencyToGet), derivationPath) - userWalletManager.refreshWallet() + if (walletFeatureToggles.isRedesignedScreenEnabled) { + onSuccessNewFlow(currencyToGet) + } else { + onSuccessLegacyFlow(currencyToGet) + } TxState.TxSent( fromAmount = amountFormatter.formatSwapAmountToUI( swapStateData.swapModel.fromTokenAmount, @@ -244,6 +258,32 @@ internal class SwapInteractorImpl @Inject constructor( return ONE_INCH_SUPPORTED_NETWORKS.contains(networkId) } + private suspend fun onSuccessLegacyFlow(currency: Currency) { + userWalletManager.addToken(swapCurrencyConverter.convert(currency), derivationPath) + userWalletManager.refreshWallet() + } + + private suspend fun onSuccessNewFlow(currency: Currency) { + val network = network ?: return + getSelectedWalletUseCase().fold( + ifRight = { userWallet -> + getAndAddCryptoCurrency(userWallet, currency, network) + }, + ifLeft = { + Timber.e("Swap Error on getSelectedWalletUseCase") + }, + ) + } + + private suspend fun getAndAddCryptoCurrency(userWallet: UserWallet, currency: Currency, network: Network) { + repository.getCryptoCurrency(userWallet, currency, network)?.let { + currenciesRepository.addCurrencies( + userWallet.walletId, + listOf(it), + ) + } + } + private fun getTangemFee(): Double { return repository.getTangemFee() } @@ -409,7 +449,7 @@ internal class SwapInteractorImpl @Inject constructor( val feeData = transactionManager.getFee( networkId = networkId, amountToSend = amount.value, - currencyToSend = cryptoCurrencyConverter.convert(fromToken), + currencyToSend = swapCurrencyConverter.convert(fromToken), destinationAddress = swapData.transaction.toWalletAddress, increaseBy = INCREASE_GAS_LIMIT_BY, data = swapData.transaction.data, @@ -562,7 +602,7 @@ internal class SwapInteractorImpl @Inject constructor( val tokensBalance = userWalletManager.getCurrentWalletTokensBalance( networkId = networkId, - extraTokens = tokensToSync.map { cryptoCurrencyConverter.convert(it) }, + extraTokens = tokensToSync.map { swapCurrencyConverter.convert(it) }, derivationPath = derivationPath, ) cache.cacheBalances( @@ -623,7 +663,7 @@ internal class SwapInteractorImpl @Inject constructor( } } - private fun getWalletAddress(networkId: String): String { + private suspend fun getWalletAddress(networkId: String): String { return userWalletManager.getWalletAddress(networkId, derivationPath) } @@ -638,7 +678,7 @@ internal class SwapInteractorImpl @Inject constructor( } } - private fun checkFeeIsEnough( + private suspend fun checkFeeIsEnough( fee: BigDecimal?, spendAmount: SwapAmount, networkId: String, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt index 70289f0c3f..6d1f5e2c00 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt @@ -1,5 +1,8 @@ package com.tangem.feature.swap.domain +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.swap.domain.models.data.AggregatedSwapDataModel import com.tangem.feature.swap.domain.models.domain.ApproveModel import com.tangem.feature.swap.domain.models.domain.Currency @@ -63,4 +66,6 @@ interface SwapRepository { * Example: 0.35% */ fun getTangemFee(): Double + + suspend fun getCryptoCurrency(userWallet: UserWallet, currency: Currency, network: Network): CryptoCurrency? } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/converters/CryptoCurrencyConverter.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/converters/SwapCurrencyConverter.kt similarity index 96% rename from features/swap/domain/src/main/java/com/tangem/feature/swap/domain/converters/CryptoCurrencyConverter.kt rename to features/swap/domain/src/main/java/com/tangem/feature/swap/domain/converters/SwapCurrencyConverter.kt index 90b978100e..d766f5029a 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/converters/CryptoCurrencyConverter.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/converters/SwapCurrencyConverter.kt @@ -4,7 +4,7 @@ import com.tangem.feature.swap.domain.models.domain.Currency import com.tangem.utils.converter.TwoWayConverter import com.tangem.lib.crypto.models.Currency as CryptoCurrency -class CryptoCurrencyConverter : TwoWayConverter { +class SwapCurrencyConverter : TwoWayConverter { override fun convert(value: Currency): CryptoCurrency { return when (value) { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt index 572c5484ac..054d9f3765 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt @@ -1,18 +1,18 @@ package com.tangem.feature.swap.domain.di -import com.tangem.feature.swap.domain.AllowPermissionsHandlerImpl -import com.tangem.feature.swap.domain.BlockchainInteractor -import com.tangem.feature.swap.domain.BlockchainInteractorImpl -import com.tangem.feature.swap.domain.SwapInteractor -import com.tangem.feature.swap.domain.SwapInteractorImpl -import com.tangem.feature.swap.domain.SwapRepository +import com.tangem.domain.tokens.repository.CurrenciesRepository +import com.tangem.domain.wallets.legacy.WalletsStateHolder +import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase +import com.tangem.feature.swap.domain.* import com.tangem.feature.swap.domain.cache.SwapDataCacheImpl +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import javax.inject.Qualifier import javax.inject.Singleton @Module @@ -25,6 +25,9 @@ class SwapDomainModule { swapRepository: SwapRepository, userWalletManager: UserWalletManager, transactionManager: TransactionManager, + currenciesRepository: CurrenciesRepository, + walletFeatureToggles: WalletFeatureToggles, + @SwapScope getSelectedWalletUseCase: GetSelectedWalletUseCase, ): SwapInteractor { return SwapInteractorImpl( transactionManager = transactionManager, @@ -32,6 +35,9 @@ class SwapDomainModule { repository = swapRepository, cache = SwapDataCacheImpl(), allowPermissionsHandler = AllowPermissionsHandlerImpl(), + currenciesRepository = currenciesRepository, + walletFeatureToggles = walletFeatureToggles, + getSelectedWalletUseCase = getSelectedWalletUseCase, ) } @@ -42,4 +48,15 @@ class SwapDomainModule { transactionManager = transactionManager, ) } -} \ No newline at end of file + + @SwapScope + @Provides + @Singleton + fun providesGetSelectedWalletUseCase(walletsStateHolder: WalletsStateHolder): GetSelectedWalletUseCase { + return GetSelectedWalletUseCase(walletsStateHolder = walletsStateHolder) + } +} + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class SwapScope \ No newline at end of file diff --git a/features/swap/presentation/build.gradle.kts b/features/swap/presentation/build.gradle.kts index 65614c399d..cd060cd2e8 100644 --- a/features/swap/presentation/build.gradle.kts +++ b/features/swap/presentation/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { /** Domain */ implementation(projects.features.swap.domain) + implementation(projects.domain.tokens.models) implementation(projects.domain.settings) /** Other libraries */ diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt index cc92e13246..07ab075e98 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt @@ -83,5 +83,6 @@ class SwapFragment : Fragment() { companion object { const val CURRENCY_BUNDLE_KEY = "swap_currency" const val DERIVATION_PATH = "DERIVATION_STYLE" + const val NETWORK = "NETWORK" } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 2379a8438d..acddac5150 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -9,6 +9,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.utils.InputNumberFormatter import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase import com.tangem.domain.balancehiding.ListenToFlipsUseCase +import com.tangem.domain.tokens.models.Network import com.tangem.feature.swap.analytics.SwapEvents import com.tangem.feature.swap.domain.BlockchainInteractor import com.tangem.feature.swap.domain.SwapInteractor @@ -16,10 +17,7 @@ import com.tangem.feature.swap.domain.models.domain.Currency import com.tangem.feature.swap.domain.models.domain.PermissionOptions import com.tangem.feature.swap.domain.models.formatToUIRepresentation import com.tangem.feature.swap.domain.models.ui.* -import com.tangem.feature.swap.models.SwapPermissionState -import com.tangem.feature.swap.models.SwapStateHolder -import com.tangem.feature.swap.models.UiActions -import com.tangem.feature.swap.models.toDomainApproveType +import com.tangem.feature.swap.models.* import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.feature.swap.router.SwapNavScreen import com.tangem.feature.swap.router.SwapRouter @@ -56,6 +54,7 @@ internal class SwapViewModel @Inject constructor( ?: error("no expected parameter Currency found"), ) private val derivationPath = savedStateHandle.get(SwapFragment.DERIVATION_PATH) + private val network = savedStateHandle.get(SwapFragment.NETWORK) private var isBalanceHidden = true @@ -87,7 +86,7 @@ internal class SwapViewModel @Inject constructor( get() = swapRouter.currentScreen init { - swapInteractor.initDerivationPath(derivationPath) + swapInteractor.initDerivationPathAndNetwork(derivationPath, network) initTokens(currency) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 3621c26e28..0d90d2c801 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -523,7 +523,7 @@ internal class WalletViewModel @Inject constructor( } override fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - // todo implement onSwapClick [REDACTED_JIRA] + reduxStateHolder.dispatch(TradeCryptoAction.New.Swap(cryptoCurrencyStatus.currency)) } override fun onSingleCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus?) { @@ -698,8 +698,7 @@ internal class WalletViewModel @Inject constructor( val state = uiState as? WalletState.ContentState ?: return val userWallet = getWallet(state.walletsListConfig.selectedWalletIndex) viewModelScope.launch(dispatchers.io) { - getCryptoCurrencyActionsUseCase - .invoke(userWallet.walletId, cryptoCurrencyStatus) + getCryptoCurrencyActionsUseCase(userWallet.walletId, cryptoCurrencyStatus) .take(count = 1) .collectLatest { uiState = stateFactory.getStateWithTokenActionBottomSheet(it) diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt index 72f8074791..0a489b3498 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt @@ -51,7 +51,7 @@ interface UserWalletManager { * @param derivationPath if null uses default */ @Throws(IllegalStateException::class) - fun getWalletAddress(networkId: String, derivationPath: String?): String + suspend fun getWalletAddress(networkId: String, derivationPath: String?): String /** * Return balances from wallet found by networkId @@ -69,7 +69,7 @@ interface UserWalletManager { ): Map @Throws(IllegalStateException::class) - fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? + suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? /** * @param networkId @@ -84,5 +84,5 @@ interface UserWalletManager { fun getUserAppCurrency(): ProxyFiatCurrency @Throws(IllegalStateException::class) - fun getLastTransactionHash(networkId: String, derivationPath: String?): String? + suspend fun getLastTransactionHash(networkId: String, derivationPath: String?): String? } \ No newline at end of file