Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-21 14:29:41 +03:00
parent 73028360f4
commit eaba6ee88d
21 changed files with 282 additions and 81 deletions

View file

@ -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))
}
}
}

View file

@ -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,
)

View file

@ -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" }
}

View file

@ -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"
}
}

View file

@ -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,
)
}

View file

@ -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<CryptoCurrency.Token>(),
newTokens = filteredCurrencies.filterIsInstance<CryptoCurrency.Token>(),
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) {

View file

@ -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
}
}

View file

@ -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)

View file

@ -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)

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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

View file

@ -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,

View file

@ -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?
}

View file

@ -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<Currency, CryptoCurrency> {
class SwapCurrencyConverter : TwoWayConverter<Currency, CryptoCurrency> {
override fun convert(value: Currency): CryptoCurrency {
return when (value) {

View file

@ -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,
)
}
}
@SwapScope
@Provides
@Singleton
fun providesGetSelectedWalletUseCase(walletsStateHolder: WalletsStateHolder): GetSelectedWalletUseCase {
return GetSelectedWalletUseCase(walletsStateHolder = walletsStateHolder)
}
}
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class SwapScope

View file

@ -38,6 +38,7 @@ dependencies {
/** Domain */
implementation(projects.features.swap.domain)
implementation(projects.domain.tokens.models)
implementation(projects.domain.settings)
/** Other libraries */

View file

@ -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"
}
}

View file

@ -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<String>(SwapFragment.DERIVATION_PATH)
private val network = savedStateHandle.get<Network>(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)
}

View file

@ -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)

View file

@ -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<String, ProxyAmount>
@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?
}