Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-30 15:15:07 +03:00
parent 248dab7303
commit 653cd7e72c
10 changed files with 121 additions and 238 deletions

View file

@ -48,7 +48,6 @@ object TradeCryptoMiddleware {
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
is TradeCryptoAction.Buy -> proceedBuyAction(state, action)
is TradeCryptoAction.Sell -> proceedSellAction(action)
is TradeCryptoAction.Swap -> openSwap(currency = action.cryptoCurrency)
is TradeCryptoAction.Stake -> openStaking(
userWalletId = action.userWalletId,
cryptoCurrencyId = action.cryptoCurrencyId,
@ -141,10 +140,6 @@ object TradeCryptoMiddleware {
)?.let { store.dispatchOpenUrl(it) }
}
private fun openSwap(currency: CryptoCurrency) {
store.dispatchNavigationAction { push(AppRoute.Swap(currency = currency)) }
}
private fun openStaking(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, yield: Yield) {
store.dispatchNavigationAction {
push(

View file

@ -219,12 +219,14 @@ sealed class AppRoute(val path: String) : Route {
@Serializable
data class Swap(
val currency: CryptoCurrency,
) : AppRoute(path = "/swap/${currency.id.value}"), RouteBundleParams {
val userWalletId: UserWalletId,
) : AppRoute(path = "/swap/${currency.id.value}/${userWalletId.stringValue}"), RouteBundleParams {
override fun getBundle(): Bundle = bundle(serializer())
companion object {
const val CURRENCY_BUNDLE_KEY = "currency"
const val USER_WALLET_ID_KEY = "userWalletId"
}
}

View file

@ -40,8 +40,6 @@ sealed class TradeCryptoAction : Action {
val transactionInfo: TransactionInfo? = null,
) : TradeCryptoAction()
data class Swap(val cryptoCurrency: CryptoCurrency) : TradeCryptoAction()
data class Stake(
val userWalletId: UserWalletId,
val cryptoCurrencyId: CryptoCurrency.ID,

View file

@ -2,7 +2,7 @@ package com.tangem.feature.swap.domain
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.domain.IncludeFeeInAmount
import com.tangem.feature.swap.domain.models.domain.PermissionOptions
@ -79,12 +79,14 @@ interface SwapInteractor {
*/
fun getTokenBalance(token: CryptoCurrencyStatus): SwapAmount
fun getSelectedWallet(): UserWallet?
suspend fun selectInitialCurrencyToSwap(
initialCryptoCurrency: CryptoCurrency,
state: TokensDataStateExpress,
): CryptoCurrencyStatus?
fun getNativeToken(networkId: String): CryptoCurrency
interface Factory {
fun create(selectedWalletId: UserWalletId): SwapInteractor
}
}

View file

@ -14,7 +14,6 @@ import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
import com.tangem.domain.tokens.model.*
@ -29,7 +28,7 @@ import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.converters.SwapCurrencyConverter
import com.tangem.feature.swap.domain.models.DataError
@ -43,21 +42,22 @@ import com.tangem.lib.crypto.UserWalletManager
import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.lib.crypto.models.ProxyFee
import com.tangem.lib.crypto.models.ProxyFees
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.firstOrNull
import timber.log.Timber
import java.math.BigDecimal
import java.math.BigInteger
import java.math.RoundingMode
import javax.inject.Inject
@Suppress("LargeClass", "LongParameterList")
internal class SwapInteractorImpl @Inject constructor(
internal class SwapInteractorImpl @AssistedInject constructor(
private val transactionManager: TransactionManager,
private val userWalletManager: UserWalletManager,
private val repository: SwapRepository,
private val allowPermissionsHandler: AllowPermissionsHandler,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val createTransactionUseCase: CreateTransactionUseCase,
@ -69,9 +69,10 @@ internal class SwapInteractorImpl @Inject constructor(
private val appCurrencyRepository: AppCurrencyRepository,
private val currenciesRepository: CurrenciesRepository,
private val initialToCurrencyResolver: InitialToCurrencyResolver,
private val demoConfig: DemoConfig,
private val validateTransactionUseCase: ValidateTransactionUseCase,
private val estimateFeeUseCase: EstimateFeeUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
@Assisted private val userWalletId: UserWalletId,
) : SwapInteractor {
private val getSelectedAppCurrencyUseCase by lazy(LazyThreadSafetyMode.NONE) {
@ -82,15 +83,13 @@ internal class SwapInteractorImpl @Inject constructor(
private val amountFormatter = AmountFormatter()
private val hundredPercent = BigInteger("100")
private val userWallet
get() = getUserWalletUseCase(userWalletId).getOrElse {
error("Failed to get user wallet")
}
override suspend fun getTokensDataState(currency: CryptoCurrency): TokensDataStateExpress {
val selectedWallet = getSelectedWalletSyncUseCase().fold(
ifLeft = { null },
ifRight = { it },
)
requireNotNull(selectedWallet) { "No selected wallet" }
val walletCurrencyStatuses = getMultiCryptoCurrencyStatusUseCase(selectedWallet.walletId)
val walletCurrencyStatuses = getMultiCryptoCurrencyStatusUseCase(userWalletId)
.getOrElse { emptyList() }
val walletCurrencyStatusesExceptInitial = walletCurrencyStatuses
@ -133,10 +132,6 @@ internal class SwapInteractorImpl @Inject constructor(
)
}
override fun getSelectedWallet(): UserWallet? {
return getSelectedWalletSyncUseCase().getOrNull()
}
private fun getToCurrenciesGroup(
currency: CryptoCurrency,
leastPairs: List<SwapPairLeast>,
@ -224,7 +219,7 @@ internal class SwapInteractorImpl @Inject constructor(
memo = null,
destination = getTokenAddress(permissionOptions.fromToken),
network = permissionOptions.fromToken.network,
userWalletId = requireNotNull(getSelectedWallet()).walletId,
userWalletId = userWalletId,
txExtras = createDexTxExtras(
dataToSign,
permissionOptions.fromToken.network,
@ -237,7 +232,7 @@ internal class SwapInteractorImpl @Inject constructor(
val result = sendTransactionUseCase(
txData = approveTransaction,
userWallet = requireNotNull(getSelectedWallet()),
userWallet = userWallet,
network = permissionOptions.fromToken.network,
)
return result.fold(
@ -403,7 +398,6 @@ internal class SwapInteractorImpl @Inject constructor(
minAdaValue: BigDecimal?,
): List<Warning> {
val fromToken = fromTokenStatus.currency
val userWalletId = getSelectedWallet()?.walletId ?: return emptyList()
val warnings = mutableListOf<Warning>()
manageExistentialDepositWarning(warnings, userWalletId, amount, fromToken, feeState)
manageDustWarning(warnings, feeState, userWalletId, fromTokenStatus, amount)
@ -597,7 +591,8 @@ internal class SwapInteractorImpl @Inject constructor(
""".trimIndent(),
)
val cardId = getSelectedWallet()?.scanResponse?.card?.cardId ?: return SwapTransactionState.UnknownError
val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return SwapTransactionState.UnknownError
val cardId = userWallet.scanResponse.card.cardId
if (isDemoCardUseCase(cardId)) return SwapTransactionState.DemoMode
return when (swapProvider.type) {
@ -615,7 +610,6 @@ internal class SwapInteractorImpl @Inject constructor(
amount = amountToSwapWithFee,
txFee = fee,
swapProvider = swapProvider,
userWalletId = requireNotNull(getSelectedWallet()).walletId,
)
}
ExchangeProviderType.DEX, ExchangeProviderType.DEX_BRIDGE -> {
@ -627,7 +621,6 @@ internal class SwapInteractorImpl @Inject constructor(
currencyToGetStatus = currencyToGet,
fee = fee,
amountToSwap = amountToSwap,
userWalletId = requireNotNull(getSelectedWallet()).walletId,
)
}
}
@ -679,7 +672,6 @@ internal class SwapInteractorImpl @Inject constructor(
currencyToGetStatus: CryptoCurrencyStatus,
amountToSwap: String,
fee: TxFee,
userWalletId: UserWalletId,
): SwapTransactionState {
val amountDecimal = requireNotNull(toBigDecimalOrNull(amountToSwap)) { "wrong amount format" }
val amount = SwapAmount(amountDecimal, currencyToSendStatus.currency.decimals)
@ -706,7 +698,7 @@ internal class SwapInteractorImpl @Inject constructor(
val result = sendTransactionUseCase(
txData = txData,
userWallet = requireNotNull(getSelectedWallet()),
userWallet = getUserWalletUseCase(userWalletId).getOrElse { return SwapTransactionState.UnknownError },
network = currencyToSendStatus.currency.network,
)
return result.fold(
@ -766,7 +758,6 @@ internal class SwapInteractorImpl @Inject constructor(
amount: SwapAmount,
txFee: TxFee,
swapProvider: SwapProvider,
userWalletId: UserWalletId,
): SwapTransactionState {
val exchangeData = repository.getExchangeData(
fromContractAddress = currencyToSend.currency.getContractAddress(),
@ -787,8 +778,9 @@ internal class SwapInteractorImpl @Inject constructor(
val exchangeDataCex =
exchangeData.transaction as? ExpressTransactionModel.CEX ?: return SwapTransactionState.UnknownError
val cardId = getSelectedWallet()?.scanResponse?.card?.cardId ?: return SwapTransactionState.UnknownError
if (demoConfig.isDemoCardId(cardId)) return SwapTransactionState.UnknownError
val cardId = userWallet.scanResponse.card.cardId
if (isDemoCardUseCase(cardId)) return SwapTransactionState.UnknownError
val txData = createTransactionUseCase(
amount = amount.value.convertToSdkAmount(currencyToSend.currency),
@ -811,7 +803,7 @@ internal class SwapInteractorImpl @Inject constructor(
val result = sendTransactionUseCase(
txData = txData,
userWallet = requireNotNull(getSelectedWallet()),
userWallet = userWallet,
network = currencyToSend.currency.network,
)
@ -941,9 +933,8 @@ internal class SwapInteractorImpl @Inject constructor(
txExternalUrl: String? = null,
txExternalId: String? = null,
) {
val selectedWallet = getSelectedWallet() ?: return
swapTransactionRepository.storeTransaction(
userWalletId = selectedWallet.walletId,
userWalletId = userWalletId,
fromCryptoCurrency = currencyToSend.currency,
toCryptoCurrency = currencyToGet.currency,
transaction = SavedSwapTransactionModel(
@ -994,23 +985,16 @@ internal class SwapInteractorImpl @Inject constructor(
spenderAddress: String,
): Boolean {
if (fromToken is CryptoCurrency.Coin) return true
return getSelectedWalletSyncUseCase().fold(
ifRight = { userWallet ->
val allowance = repository.getAllowance(
userWalletId = userWallet.walletId,
networkId = networkId,
derivationPath = fromToken.network.derivationPath.value,
tokenDecimalCount = fromToken.decimals,
tokenAddress = getTokenAddress(fromToken),
spenderAddress = spenderAddress,
)
allowance >= amount.value
},
ifLeft = {
Timber.e("Swap Error on isAllowedToSpend")
false
},
val allowance = repository.getAllowance(
userWalletId = userWallet.walletId,
networkId = networkId,
derivationPath = fromToken.network.derivationPath.value,
tokenDecimalCount = fromToken.decimals,
tokenAddress = getTokenAddress(fromToken),
spenderAddress = spenderAddress,
)
return allowance >= amount.value
}
private suspend fun createEmptyAmountState(): SwapState {
@ -1040,13 +1024,11 @@ internal class SwapInteractorImpl @Inject constructor(
val fromToken = fromTokenStatus.currency
val toToken = toTokenStatus.currency
return coroutineScope {
val txFeeResult = getSelectedWalletSyncUseCase().getOrNull()?.let { userWallet ->
getUnhandledFee(
amount = amount.value,
userWallet = userWallet,
cryptoCurrency = fromToken,
)
}
val txFeeResult = getUnhandledFee(
amount = amount.value,
userWallet = userWallet,
cryptoCurrency = fromToken,
)
val txFee = if (provider.type == ExchangeProviderType.CEX) {
getFeeForCex(txFeeResult, fromTokenStatus)
@ -1196,7 +1178,6 @@ internal class SwapInteractorImpl @Inject constructor(
is TxFeeState.SingleFeeState -> txFee.fee.feeIncludeOtherNativeFee
}
val feePaidCurrency = getFeePaidCurrency(
userWalletId = requireNotNull(getSelectedWallet()).walletId,
currency = fromToken,
)
@ -1256,7 +1237,6 @@ internal class SwapInteractorImpl @Inject constructor(
private suspend fun getFormattedFiatFees(fromToken: CryptoCurrency, vararg fees: BigDecimal): List<String> {
val appCurrency = getSelectedAppCurrencyUseCase.unwrap()
val feePaidCurrency = getFeePaidCurrency(
userWalletId = requireNotNull(getSelectedWallet()).walletId,
currency = fromToken,
)
val feeCurrencyId: CryptoCurrency.ID = when (feePaidCurrency) {
@ -1308,7 +1288,6 @@ internal class SwapInteractorImpl @Inject constructor(
val otherNativeFee = transaction.otherNativeFeeWei
?.movePointLeft(nativeCoinDecimals)
?: BigDecimal.ZERO
val userWallet = getSelectedWallet()
val txFeeState = when (
val feeData = getFeeDataForDexSwap(
networkId = networkId,
@ -1509,7 +1488,6 @@ internal class SwapInteractorImpl @Inject constructor(
swapAmount = swapAmount,
spenderAddress = requireNotNull(spenderAddress) { "Spender address is null" },
)
val userWallet = getSelectedWallet()
val cardId = userWallet?.scanResponse?.card?.cardId
val feeData = if (cardId != null && isDemoCardUseCase(cardId)) {
getDemoFees(fromTokenStatus.currency)
@ -1846,7 +1824,6 @@ internal class SwapInteractorImpl @Inject constructor(
): Boolean {
val tokenBalance = getTokenBalance(fromToken).value
val feePaidCurrency = getFeePaidCurrency(
userWalletId = requireNotNull(getSelectedWallet()).walletId,
currency = fromToken.currency,
)
return when (feePaidCurrency) {
@ -1861,7 +1838,7 @@ internal class SwapInteractorImpl @Inject constructor(
}
}
private suspend fun getFeePaidCurrency(userWalletId: UserWalletId, currency: CryptoCurrency): FeePaidCurrency {
private suspend fun getFeePaidCurrency(currency: CryptoCurrency): FeePaidCurrency {
return currenciesRepository.getFeePaidCurrency(
userWalletId = userWalletId,
currency = currency,
@ -1894,13 +1871,12 @@ internal class SwapInteractorImpl @Inject constructor(
networkId: String,
fromTokenStatus: CryptoCurrencyStatus,
): SwapFeeState {
val userWalletId = requireNotNull(getSelectedWallet()).walletId
if (fee == null) {
return SwapFeeState.NotEnough()
}
val percentsToFeeIncrease = BigDecimal.ONE
return when (val feePaidCurrency = getFeePaidCurrency(userWalletId, fromTokenStatus.currency)) {
return when (val feePaidCurrency = getFeePaidCurrency(fromTokenStatus.currency)) {
FeePaidCurrency.Coin -> {
val nativeTokenBalance = userWalletManager.getNativeTokenBalance(
networkId,
@ -1994,21 +1970,13 @@ internal class SwapInteractorImpl @Inject constructor(
swapAmount: SwapAmount? = null,
spenderAddress: String,
): String {
return getSelectedWalletSyncUseCase().fold(
ifRight = { userWallet ->
repository.getApproveData(
userWalletId = userWallet.walletId,
networkId = networkId,
derivationPath = derivationPath,
currency = fromToken,
amount = swapAmount?.value,
spenderAddress = spenderAddress,
)
},
ifLeft = {
Timber.e("Swap Error on getApproveData")
error("Swap Error on getApproveData")
},
return repository.getApproveData(
userWalletId = userWalletId,
networkId = networkId,
derivationPath = derivationPath,
currency = fromToken,
amount = swapAmount?.value,
spenderAddress = spenderAddress,
)
}
@ -2059,4 +2027,9 @@ internal class SwapInteractorImpl @Inject constructor(
private val normalDemoFee = "0.0002".toBigDecimal()
private val priorityDemoFee = "0.0003".toBigDecimal()
}
@AssistedFactory
interface Factory : SwapInteractor.Factory {
override fun create(selectedWalletId: UserWalletId): SwapInteractorImpl
}
}

View file

@ -1,79 +1,34 @@
package com.tangem.feature.swap.domain.di
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.GetCardTokensListUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.feature.swap.domain.*
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.lib.crypto.TransactionManager
import com.tangem.lib.crypto.UserWalletManager
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Qualifier
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class SwapDomainModule {
internal class SwapDomainModule {
@Provides
fun provideAllowPermissionsHandler(): AllowPermissionsHandler {
return AllowPermissionsHandlerImpl()
}
@Provides
@Singleton
fun provideSwapInteractor(
swapRepository: SwapRepository,
userWalletManager: UserWalletManager,
transactionManager: TransactionManager,
@SwapScope getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase,
@SwapScope sendTransactionUseCase: SendTransactionUseCase,
@SwapScope createTransactionUseCase: CreateTransactionUseCase,
createTransactionDataExtrasUseCase: CreateTransactionDataExtrasUseCase,
isDemoCardUseCase: IsDemoCardUseCase,
quotesRepository: QuotesRepository,
swapTransactionRepository: SwapTransactionRepository,
appCurrencyRepository: AppCurrencyRepository,
currencyChecksRepository: CurrencyChecksRepository,
initialToCurrencyResolver: InitialToCurrencyResolver,
currenciesRepository: CurrenciesRepository,
validateTransactionUseCase: ValidateTransactionUseCase,
estimateFeeUseCase: EstimateFeeUseCase,
): SwapInteractor {
return SwapInteractorImpl(
transactionManager = transactionManager,
userWalletManager = userWalletManager,
repository = swapRepository,
allowPermissionsHandler = AllowPermissionsHandlerImpl(),
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
getMultiCryptoCurrencyStatusUseCase = getCryptoCurrencyStatusUseCase,
sendTransactionUseCase = sendTransactionUseCase,
createTransactionUseCase = createTransactionUseCase,
createTransactionExtrasUseCase = createTransactionDataExtrasUseCase,
isDemoCardUseCase = isDemoCardUseCase,
quotesRepository = quotesRepository,
swapTransactionRepository = swapTransactionRepository,
appCurrencyRepository = appCurrencyRepository,
currencyChecksRepository = currencyChecksRepository,
currenciesRepository = currenciesRepository,
initialToCurrencyResolver = initialToCurrencyResolver,
demoConfig = DemoConfig(),
validateTransactionUseCase = validateTransactionUseCase,
estimateFeeUseCase = estimateFeeUseCase,
)
fun provideSwapInteractorFactory(factory: SwapInteractorImpl.Factory): SwapInteractor.Factory {
return factory
}
@Provides
@ -84,13 +39,6 @@ class SwapDomainModule {
)
}
@SwapScope
@Provides
@Singleton
fun providesGetSelectedWalletUseCase(userWalletsListManager: UserWalletsListManager): GetSelectedWalletSyncUseCase {
return GetSelectedWalletSyncUseCase(userWalletsListManager = userWalletsListManager)
}
@Provides
@Singleton
fun providesGetCryptoCurrencyStatusUseCase(
@ -109,58 +57,10 @@ class SwapDomainModule {
)
}
@SwapScope
@Provides
@Singleton
fun providesGetCardTokensListUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
): GetCardTokensListUseCase {
return GetCardTokensListUseCase(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
)
}
@SwapScope
@Provides
fun provideDemoCardUseCase(): IsDemoCardUseCase {
return IsDemoCardUseCase(config = DemoConfig())
}
@SwapScope
@Provides
@Singleton
fun provideCreateTransactionUseCase(transactionRepository: TransactionRepository): CreateTransactionUseCase {
return CreateTransactionUseCase(
transactionRepository = transactionRepository,
)
}
@SwapScope
@Provides
@Singleton
fun provideSendTransactionUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
transactionRepository: TransactionRepository,
walletManagersFacade: WalletManagersFacade,
): SendTransactionUseCase {
return SendTransactionUseCase(
demoConfig = DemoConfig(),
cardSdkConfigRepository = cardSdkConfigRepository,
transactionRepository = transactionRepository,
walletManagersFacade = walletManagersFacade,
)
}
@Provides
@Singleton
fun provideInitialToCurrencyResolver(
@SwapScope getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
swapTransactionRepository: SwapTransactionRepository,
): InitialToCurrencyResolver {
return DefaultInitialToCurrencyResolver(
@ -168,8 +68,4 @@ class SwapDomainModule {
swapTransactionRepository = swapTransactionRepository,
)
}
}
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class SwapScope
}

View file

@ -25,7 +25,6 @@ import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.analytics.SwapEvents
import com.tangem.feature.swap.domain.BlockchainInteractor
@ -65,7 +64,7 @@ typealias SuccessLoadedSwapData = Map<SwapProvider, SwapState.QuotesLoadedState>
@Suppress("LargeClass", "LongParameterList")
@HiltViewModel
internal class SwapViewModel @Inject constructor(
private val swapInteractor: SwapInteractor,
private val swapInteractorFactory: SwapInteractor.Factory,
private val blockchainInteractor: BlockchainInteractor,
private val dispatchers: CoroutineDispatcherProvider,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -79,7 +78,13 @@ internal class SwapViewModel @Inject constructor(
private val initialCryptoCurrency: CryptoCurrency = savedStateHandle.get<Bundle>(AppRoute.Swap.CURRENCY_BUNDLE_KEY)
?.unbundle(CryptoCurrency.serializer())
?: error("no expected parameter CryptoCurrency found`")
?: error("no expected parameter CryptoCurrency found")
private val userWalletId: UserWalletId = savedStateHandle.get<Bundle>(AppRoute.Swap.USER_WALLET_ID_KEY)
?.unbundle(UserWalletId.serializer())
?: error("no expected parameter UserWalletId found")
private val swapInteractor = swapInteractorFactory.create(userWalletId)
private lateinit var initialCryptoCurrencyStatus: CryptoCurrencyStatus
@ -126,15 +131,13 @@ internal class SwapViewModel @Inject constructor(
init {
viewModelScope.launch(dispatchers.io) {
swapInteractor.getSelectedWallet()?.let {
val cryptoCurrencyStatus =
getCryptoCurrencyStatusUseCase(it.walletId, initialCryptoCurrency.id).getOrNull()
if (cryptoCurrencyStatus == null) {
uiState = stateBuilder.addAlert(uiState = uiState, onClick = swapRouter::back)
} else {
initialCryptoCurrencyStatus = cryptoCurrencyStatus
initTokens()
}
val cryptoCurrencyStatus =
getCryptoCurrencyStatusUseCase(userWalletId, initialCryptoCurrency.id).getOrNull()
if (cryptoCurrencyStatus == null) {
uiState = stateBuilder.addAlert(uiState = uiState, onClick = swapRouter::back)
} else {
initialCryptoCurrencyStatus = cryptoCurrencyStatus
initTokens()
}
}
}
@ -191,7 +194,6 @@ internal class SwapViewModel @Inject constructor(
),
)
val userWalletId = swapInteractor.getSelectedWallet()?.walletId ?: return@launch
(dataState.fromCryptoCurrency?.currency as? CryptoCurrency.Coin)?.let {
subscribeToCoinBalanceUpdates(
userWalletId = userWalletId,
@ -707,8 +709,6 @@ internal class SwapViewModel @Inject constructor(
analyticsEventHandler.send(SwapEvents.ChooseTokenScreenResult(tokenChosen = true, token = it))
}
val userWalletId = swapInteractor.getSelectedWallet()?.walletId
if (foundToken != null) {
val fromToken: CryptoCurrencyStatus
val toToken: CryptoCurrencyStatus
@ -717,7 +717,7 @@ internal class SwapViewModel @Inject constructor(
toToken = initialCryptoCurrencyStatus
val newToken = fromToken.currency as? CryptoCurrency.Coin
if (userWalletId != null && newToken != null) {
if (newToken != null) {
subscribeToCoinBalanceUpdates(
userWalletId = userWalletId,
coin = newToken,
@ -729,7 +729,7 @@ internal class SwapViewModel @Inject constructor(
toToken = foundToken.currencyStatus
val newToken = toToken.currency as? CryptoCurrency.Coin
if (userWalletId != null && newToken != null) {
if (newToken != null) {
subscribeToCoinBalanceUpdates(
userWalletId = userWalletId,
coin = newToken,
@ -985,12 +985,10 @@ internal class SwapViewModel @Inject constructor(
}
},
onBuyClick = { currency ->
swapInteractor.getSelectedWallet()?.let { userWallet ->
swapRouter.openTokenDetails(
userWalletId = userWallet.walletId,
currency = currency,
)
}
swapRouter.openTokenDetails(
userWalletId = userWalletId,
currency = currency,
)
},
onRetryClick = {
startLoadingQuotesFromLastState()
@ -1162,20 +1160,18 @@ internal class SwapViewModel @Inject constructor(
}
private fun updateWalletBalance() {
swapInteractor.getSelectedWallet()?.let { userWallet ->
dataState.fromCryptoCurrency?.currency?.network?.let { network ->
viewModelScope.launch {
withContext(NonCancellable) {
updateForBalance(userWallet, network)
}
dataState.fromCryptoCurrency?.currency?.network?.let { network ->
viewModelScope.launch {
withContext(NonCancellable) {
updateForBalance(userWalletId, network)
}
}
}
}
private suspend fun updateForBalance(userWallet: UserWallet, network: Network) {
private suspend fun updateForBalance(userWalletId: UserWalletId, network: Network) {
updateDelayedCurrencyStatusUseCase(
userWalletId = userWallet.walletId,
userWalletId = userWalletId,
network = network,
delayMillis = UPDATE_BALANCE_DELAY_MILLIS,
refresh = true,

View file

@ -6,6 +6,7 @@ import androidx.paging.cachedIn
import arrow.core.getOrElse
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.routing.bundle.unbundle
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
@ -126,6 +127,7 @@ internal class TokenDetailsViewModel @Inject constructor(
tokenDetailsFeatureToggles: TokenDetailsFeatureToggles,
deepLinksRegistry: DeepLinksRegistry,
savedStateHandle: SavedStateHandle,
private val appRouter: AppRouter,
) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents {
private val userWalletId: UserWalletId = savedStateHandle.get<Bundle>(AppRoute.CurrencyDetails.USER_WALLET_ID_KEY)
@ -616,7 +618,7 @@ internal class TokenDetailsViewModel @Inject constructor(
if (handleUnavailabilityReason(unavailabilityReason)) return
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrency))
appRouter.push(AppRoute.Swap(currency = cryptoCurrency, userWalletId = userWalletId))
}
override fun onDismissDialog() {

View file

@ -77,7 +77,13 @@ internal class MultiWalletCurrencyActionsConverter(
is TokenActionsState.ActionState.Swap -> {
title = resourceReference(R.string.swapping_swap_action)
icon = R.drawable.ic_exchange_horizontal_24
action = { clickIntents.onSwapClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
action = {
clickIntents.onSwapClick(
cryptoCurrencyStatus = cryptoCurrencyStatus,
userWalletId = userWallet.walletId,
unavailabilityReason = ScenarioUnavailabilityReason.None,
)
}
}
is TokenActionsState.ActionState.CopyAddress -> {
title = resourceReference(R.string.common_copy_address)

View file

@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
import arrow.core.getOrElse
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
@ -57,7 +59,11 @@ interface WalletCurrencyActionsClickIntents {
fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
fun onSwapClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
userWalletId: UserWalletId,
unavailabilityReason: ScenarioUnavailabilityReason,
)
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
@ -95,6 +101,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val vibratorHapticManager: VibratorHapticManager,
private val clipboardManager: ClipboardManager,
private val getYieldUseCase: GetYieldUseCase,
private val appRouter: AppRouter,
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
override fun onSendClick(
@ -352,6 +359,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
override fun onSwapClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
userWalletId: UserWalletId,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
analyticsEventHandler.send(
@ -360,7 +368,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
if (handleUnavailabilityReason(unavailabilityReason)) return
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrencyStatus.currency))
appRouter.push(
AppRoute.Swap(
currency = cryptoCurrencyStatus.currency,
userWalletId = userWalletId,
),
)
}
override fun onExploreClick() {