Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-12 12:48:33 +03:00
parent b0e07c0258
commit f5696659f2
5 changed files with 48 additions and 39 deletions

View file

@ -1,11 +1,11 @@
package com.tangem.tap.di.domain
import com.tangem.domain.dynamicaddresses.CreateConsolidationTransactionUseCase
import com.tangem.domain.dynamicaddresses.DisableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.GetDynamicAddressesStatusUseCase
import com.tangem.domain.dynamicaddresses.GetDynamicReceiveAddressUseCase
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
import com.tangem.domain.dynamicaddresses.IsDynamicAddressesConsolidationRequiredUseCase
import com.tangem.domain.dynamicaddresses.IsXpubSupportedUseCase
import com.tangem.domain.dynamicaddresses.repository.ConsolidationRepository
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
@ -31,10 +31,10 @@ internal object DynamicAddressesDomainModule {
@Provides
@Singleton
fun provideDisableDynamicAddressesUseCase(
fun provideIsDynamicAddressesConsolidationRequiredUseCase(
dynamicAddressesRepository: DynamicAddressesRepository,
): DisableDynamicAddressesUseCase {
return DisableDynamicAddressesUseCase(dynamicAddressesRepository)
): IsDynamicAddressesConsolidationRequiredUseCase {
return IsDynamicAddressesConsolidationRequiredUseCase(dynamicAddressesRepository)
}
@Provides

View file

@ -1,27 +0,0 @@
package com.tangem.domain.dynamicaddresses
import arrow.core.Either
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
class DisableDynamicAddressesUseCase(
private val dynamicAddressesRepository: DynamicAddressesRepository,
) {
/**
* Returns true when consolidation is required before disabling (non-base balances exist),
* or false when dynamic addresses were disabled immediately.
*/
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either<Throwable, Boolean> =
Either.catch {
val hasNonBaseBalances = dynamicAddressesRepository.hasNonBaseBalances(userWalletId, network)
if (!hasNonBaseBalances) {
dynamicAddressesRepository.disable(userWalletId, network)
return@catch false
}
true
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.domain.dynamicaddresses
import arrow.core.Either
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
/**
* Returns `true` if a consolidation transaction must be broadcast before
* [DynamicAddressesRepository.disable] is called (non-base balances exist).
*/
class IsDynamicAddressesConsolidationRequiredUseCase(
private val dynamicAddressesRepository: DynamicAddressesRepository,
) {
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either<Throwable, Boolean> =
Either.catch {
dynamicAddressesRepository.hasNonBaseBalances(userWalletId, network)
}
}

View file

@ -11,7 +11,7 @@ import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.common.ui.amountScreen.utils.getFiatString
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.dynamicaddresses.CreateConsolidationTransactionUseCase
import com.tangem.domain.dynamicaddresses.DisableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.IsDynamicAddressesConsolidationRequiredUseCase
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesError
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
@ -43,7 +43,7 @@ import kotlinx.coroutines.launch
@Suppress("LongParameterList", "LargeClass")
internal class DynamicAddressesDelegate @AssistedInject constructor(
private val enableDynamicAddressesUseCase: EnableDynamicAddressesUseCase,
private val disableDynamicAddressesUseCase: DisableDynamicAddressesUseCase,
private val isConsolidationRequiredUseCase: IsDynamicAddressesConsolidationRequiredUseCase,
private val createConsolidationTransactionUseCase: CreateConsolidationTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
@ -180,7 +180,7 @@ internal class DynamicAddressesDelegate @AssistedInject constructor(
private fun onDisableFlow(network: Network) {
coroutineScope.launch(dispatchers.main) {
disableDynamicAddressesUseCase(userWalletId, network).fold(
isConsolidationRequiredUseCase(userWalletId, network).fold(
ifLeft = { error ->
TangemLogger.e("Failed to check disable: ${error.message}")
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(

View file

@ -10,7 +10,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.dynamicaddresses.CreateConsolidationTransactionUseCase
import com.tangem.domain.dynamicaddresses.DisableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.IsDynamicAddressesConsolidationRequiredUseCase
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesError
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
@ -57,7 +57,7 @@ internal class DynamicAddressesDelegateTest {
private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxed = true)
private val enableDynamicAddressesUseCase: EnableDynamicAddressesUseCase = mockk()
private val disableDynamicAddressesUseCase: DisableDynamicAddressesUseCase = mockk()
private val isConsolidationRequiredUseCase: IsDynamicAddressesConsolidationRequiredUseCase = mockk()
private val createConsolidationTransactionUseCase: CreateConsolidationTransactionUseCase = mockk()
private val getFeeUseCase: GetFeeUseCase = mockk(relaxed = true)
private val sendTransactionUseCase: SendTransactionUseCase = mockk(relaxed = true)
@ -264,7 +264,7 @@ internal class DynamicAddressesDelegateTest {
// GIVEN
every { dynamicAddressesRepository.getStatus(userWalletId, network) } returns
flowOf(DynamicAddressesStatus.ENABLED)
coEvery { disableDynamicAddressesUseCase(userWalletId, network) } returns false.right()
coEvery { isConsolidationRequiredUseCase(userWalletId, network) } returns false.right()
coEvery { dynamicAddressesRepository.disable(userWalletId, network) } returns Unit
val delegate = createDelegate(cryptoCurrencyStatus = cryptoCurrencyStatus)
@ -289,6 +289,22 @@ internal class DynamicAddressesDelegateTest {
}
}
@Test
fun `GIVEN ENABLED status AND no consolidation WHEN menu tapped without confirmation THEN repository disable is NOT called`() =
runTest {
every { dynamicAddressesRepository.getStatus(userWalletId, network) } returns
flowOf(DynamicAddressesStatus.ENABLED)
coEvery { isConsolidationRequiredUseCase(userWalletId, network) } returns false.right()
val delegate = createDelegate(cryptoCurrencyStatus = cryptoCurrencyStatus)
delegate.onDynamicAddressesClick()
// The simple disable sheet must be shown but no backend write must happen yet.
assertThat(delegate.bottomSheetConfig.value)
.isInstanceOf(DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation::class.java)
coVerify(exactly = 0) { dynamicAddressesRepository.disable(any(), any()) }
}
@Test
fun `GIVEN consolidation required AND fee fails WHEN load fee THEN NotEnoughFee with DynamicAddresses source is sent`() =
runTest {
@ -406,7 +422,7 @@ internal class DynamicAddressesDelegateTest {
private fun setupConsolidationFlow() {
every { dynamicAddressesRepository.getStatus(userWalletId, network) } returns
flowOf(DynamicAddressesStatus.ENABLED)
coEvery { disableDynamicAddressesUseCase(userWalletId, network) } returns true.right()
coEvery { isConsolidationRequiredUseCase(userWalletId, network) } returns true.right()
val address = NetworkAddress.Address(value = TEST_ADDRESS, type = NetworkAddress.Address.Type.Primary)
every { cryptoCurrencyStatus.value } returns mockk(relaxed = true) {
every { amount } returns BigDecimal.ONE
@ -419,7 +435,7 @@ internal class DynamicAddressesDelegateTest {
val scope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
return DynamicAddressesDelegate(
enableDynamicAddressesUseCase = enableDynamicAddressesUseCase,
disableDynamicAddressesUseCase = disableDynamicAddressesUseCase,
isConsolidationRequiredUseCase = isConsolidationRequiredUseCase,
createConsolidationTransactionUseCase = createConsolidationTransactionUseCase,
getFeeUseCase = getFeeUseCase,
sendTransactionUseCase = sendTransactionUseCase,