Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-16 17:45:25 +04:00
parent 0b81b905c3
commit 50332186fa
47 changed files with 38 additions and 408 deletions

View file

@ -1,13 +0,0 @@
package com.tangem.data.dynamicaddresses
import com.tangem.core.configtoggle.FeatureToggles
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
internal class DefaultDynamicAddressesFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager,
) : DynamicAddressesFeatureToggles {
override val isDynamicAddressesEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.DYNAMIC_ADDRESSES_ENABLED)
}

View file

@ -6,7 +6,6 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.dynamicaddresses.DynamicAddressesDerivationChecker
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
import com.tangem.domain.dynamicaddresses.DynamicAddressesSupportedBlockchains
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
import com.tangem.domain.dynamicaddresses.model.DynamicAddressesStatus
@ -35,7 +34,6 @@ internal class DefaultDynamicAddressesRepository(
private val walletAccountsSaver: WalletAccountsSaver,
private val accountsCRUDRepository: AccountsCRUDRepository,
private val walletManagersFacade: WalletManagersFacade,
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
private val getDerivedXpubUseCase: GetDerivedXpubUseCase,
private val dispatchers: CoroutineDispatcherProvider,
) : DynamicAddressesRepository {
@ -107,7 +105,6 @@ internal class DefaultDynamicAddressesRepository(
}
override fun hasFundsOnAdditionalAddresses(userWalletId: UserWalletId, network: Network): Flow<Boolean> {
if (!dynamicAddressesFeatureToggles.isDynamicAddressesEnabled) return flowOf(false)
if (!DynamicAddressesSupportedBlockchains.isSupportedByNetworkId(network.id.rawId.value)) return flowOf(false)
return getStatus(userWalletId, network)

View file

@ -2,7 +2,6 @@ package com.tangem.data.dynamicaddresses
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.getSyncOrNull
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
import com.tangem.domain.dynamicaddresses.DynamicAddressesSupportedBlockchains
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
import com.tangem.domain.dynamicaddresses.model.DynamicAddressesStatus
@ -22,14 +21,11 @@ import javax.inject.Singleton
@Singleton
class DynamicAddressesInitializer @Inject constructor(
private val dynamicAddressesRepository: DynamicAddressesRepository,
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
private val getDerivedXpubUseCase: GetDerivedXpubUseCase,
private val userWalletsListRepository: UserWalletsListRepository,
) {
suspend fun getXpubs(userWalletId: UserWalletId, networks: Set<Network>): Map<Network, String> {
if (!dynamicAddressesFeatureToggles.isDynamicAddressesEnabled) return emptyMap()
/*
* Dynamic addresses rely on the server-side wallet accounts list, which is populated only for
* multi-currency wallets. Single-currency wallets (Note, s2c, etc.) never populate it, so

View file

@ -1,13 +1,10 @@
package com.tangem.data.dynamicaddresses.di
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.data.common.account.WalletAccountsSaver
import com.tangem.data.dynamicaddresses.DefaultConsolidationRepository
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.data.dynamicaddresses.DefaultDynamicAddressesFeatureToggles
import com.tangem.data.dynamicaddresses.DefaultDynamicAddressesRepository
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
import com.tangem.domain.dynamicaddresses.repository.ConsolidationRepository
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
@ -30,7 +27,6 @@ internal object DynamicAddressesDataModule {
walletAccountsSaver: WalletAccountsSaver,
accountsCRUDRepository: AccountsCRUDRepository,
walletManagersFacade: WalletManagersFacade,
dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
getDerivedXpubUseCase: GetDerivedXpubUseCase,
dispatchers: CoroutineDispatcherProvider,
): DynamicAddressesRepository {
@ -39,20 +35,11 @@ internal object DynamicAddressesDataModule {
walletAccountsSaver = walletAccountsSaver,
accountsCRUDRepository = accountsCRUDRepository,
walletManagersFacade = walletManagersFacade,
dynamicAddressesFeatureToggles = dynamicAddressesFeatureToggles,
getDerivedXpubUseCase = getDerivedXpubUseCase,
dispatchers = dispatchers,
)
}
@Provides
@Singleton
fun provideDynamicAddressesFeatureToggles(
featureTogglesManager: FeatureTogglesManager,
): DynamicAddressesFeatureToggles {
return DefaultDynamicAddressesFeatureToggles(featureTogglesManager = featureTogglesManager)
}
@Provides
@Singleton
fun provideConsolidationRepository(

View file

@ -6,7 +6,6 @@ import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.data.common.account.WalletAccountsSaver
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
import com.tangem.domain.dynamicaddresses.GetDerivedXpubUseCase
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
@ -32,7 +31,6 @@ class DefaultDynamicAddressesRepositoryTest {
private val walletAccountsSaver: WalletAccountsSaver = mockk(relaxed = true)
private val accountsCRUDRepository: AccountsCRUDRepository = mockk(relaxed = true)
private val walletManagersFacade: WalletManagersFacade = mockk(relaxed = true)
private val featureToggles: DynamicAddressesFeatureToggles = mockk(relaxed = true)
private val getDerivedXpubUseCase: GetDerivedXpubUseCase = mockk(relaxed = true)
private val userWalletId: UserWalletId = mockk(relaxed = true)
@ -49,7 +47,7 @@ class DefaultDynamicAddressesRepositoryTest {
@BeforeEach
fun setUp() {
clearMocks(walletManagersFacade, featureToggles, getDerivedXpubUseCase, answers = false)
clearMocks(walletManagersFacade, getDerivedXpubUseCase, answers = false)
// Empty response → findToken returns null → getStatus emits DISABLED.
val emptyResponse = mockk<GetWalletAccountsResponse>(relaxed = true) {
every { accounts } returns emptyList()
@ -63,31 +61,14 @@ class DefaultDynamicAddressesRepositoryTest {
walletAccountsSaver = walletAccountsSaver,
accountsCRUDRepository = accountsCRUDRepository,
walletManagersFacade = walletManagersFacade,
dynamicAddressesFeatureToggles = featureToggles,
getDerivedXpubUseCase = getDerivedXpubUseCase,
dispatchers = dispatchers,
)
}
@Test
fun `GIVEN feature toggle off WHEN collect THEN probe is never called and flow emits false`() = runTest {
fun `GIVEN xpub is null WHEN collect THEN probe is not called AND cache is empty`() = runTest {
// GIVEN
every { featureToggles.isDynamicAddressesEnabled } returns false
// WHEN
val values = repository.hasFundsOnAdditionalAddresses(userWalletId, network).toList()
// THEN
assertThat(values).doesNotContain(true)
coVerify(exactly = 0) {
walletManagersFacade.probeHasFundsOnAdditionalAddresses(any(), any(), any())
}
}
@Test
fun `GIVEN toggle on AND xpub is null WHEN collect THEN probe is not called AND cache is empty`() = runTest {
// GIVEN
every { featureToggles.isDynamicAddressesEnabled } returns true
coEvery { getDerivedXpubUseCase(userWalletId, network) } returns null
// WHEN
@ -105,7 +86,6 @@ class DefaultDynamicAddressesRepositoryTest {
@Test
fun `GIVEN probe returns true WHEN collect THEN result is cached AND next collect skips probe`() = runTest {
// GIVEN
every { featureToggles.isDynamicAddressesEnabled } returns true
coEvery { getDerivedXpubUseCase(userWalletId, network) } returns XPUB
coEvery {
walletManagersFacade.probeHasFundsOnAdditionalAddresses(userWalletId, network, XPUB)
@ -126,7 +106,6 @@ class DefaultDynamicAddressesRepositoryTest {
@Test
fun `GIVEN probe returns false WHEN collect twice THEN probe runs each time`() = runTest {
// GIVEN
every { featureToggles.isDynamicAddressesEnabled } returns true
coEvery { getDerivedXpubUseCase(userWalletId, network) } returns XPUB
coEvery {
walletManagersFacade.probeHasFundsOnAdditionalAddresses(userWalletId, network, XPUB)
@ -145,7 +124,6 @@ class DefaultDynamicAddressesRepositoryTest {
@Test
fun `GIVEN cached true WHEN enable succeeds THEN cache is invalidated and next probe runs again`() = runTest {
// GIVEN — populate cache with a positive probe
every { featureToggles.isDynamicAddressesEnabled } returns true
coEvery { getDerivedXpubUseCase(userWalletId, network) } returns XPUB
coEvery {
walletManagersFacade.probeHasFundsOnAdditionalAddresses(userWalletId, network, XPUB)
@ -165,7 +143,6 @@ class DefaultDynamicAddressesRepositoryTest {
@Test
fun `GIVEN cached true WHEN disable succeeds THEN cache is invalidated and next probe runs again`() = runTest {
// GIVEN
every { featureToggles.isDynamicAddressesEnabled } returns true
coEvery { getDerivedXpubUseCase(userWalletId, network) } returns XPUB
coEvery {
walletManagersFacade.probeHasFundsOnAdditionalAddresses(userWalletId, network, XPUB)
@ -185,7 +162,6 @@ class DefaultDynamicAddressesRepositoryTest {
@Test
fun `GIVEN cache entry for one network WHEN invalidate other network THEN first entry is preserved`() = runTest {
// GIVEN — cache populated for `network`
every { featureToggles.isDynamicAddressesEnabled } returns true
coEvery { getDerivedXpubUseCase(userWalletId, network) } returns XPUB
coEvery {
walletManagersFacade.probeHasFundsOnAdditionalAddresses(userWalletId, network, XPUB)