Updated on 2026-08-14
This commit is contained in:
parent
248dab7303
commit
653cd7e72c
10 changed files with 121 additions and 238 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue