Updated on 2026-08-14
This commit is contained in:
parent
8ec30bd373
commit
b2a199a568
10 changed files with 177 additions and 187 deletions
|
|
@ -210,4 +210,10 @@ internal object StakingDomainModule {
|
|||
fun provideGetActionRequirementAmountUseCase(): GetActionRequirementAmountUseCase {
|
||||
return GetActionRequirementAmountUseCase()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingIdFactory(walletManagersFacade: WalletManagersFacade): StakingIdFactory {
|
||||
return StakingIdFactory(walletManagersFacade = walletManagersFacade)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.domain.promo.PromoRepository
|
|||
import com.tangem.domain.quotes.QuotesRepository
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
|
||||
|
|
@ -405,6 +406,7 @@ internal object TokensDomainModule {
|
|||
singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
|
||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
): BaseCurrenciesStatusesOperations {
|
||||
return CachedCurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
|
|
@ -420,6 +422,7 @@ internal object TokensDomainModule {
|
|||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -439,6 +442,7 @@ internal object TokensDomainModule {
|
|||
singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
|
||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
): BaseCurrencyStatusOperations {
|
||||
return CachedCurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
|
|
@ -454,6 +458,7 @@ internal object TokensDomainModule {
|
|||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
||||
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,9 +42,8 @@ internal object YieldBalanceSupplierModule {
|
|||
listOf(
|
||||
"single_yield_balance",
|
||||
params.userWalletId.stringValue,
|
||||
params.currencyId.value,
|
||||
params.network.id.rawId,
|
||||
params.network.id.derivationPath,
|
||||
params.stakingId.integrationId,
|
||||
params.stakingId.address,
|
||||
)
|
||||
.joinToString(separator = "_")
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package com.tangem.data.staking.single
|
|||
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
|
||||
|
|
@ -24,7 +22,7 @@ import timber.log.Timber
|
|||
*
|
||||
* @property params params
|
||||
* @property multiYieldBalanceSupplier multi yield balance supplier
|
||||
* @property stakingIdFactory factory for creating [StakingID]
|
||||
* @property analyticsExceptionHandler analytics exception handler
|
||||
* @property dispatchers dispatchers
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -32,20 +30,17 @@ import timber.log.Timber
|
|||
internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
|
||||
@Assisted private val params: SingleYieldBalanceProducer.Params,
|
||||
private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : SingleYieldBalanceProducer {
|
||||
|
||||
override val fallback: YieldBalance by lazy {
|
||||
YieldBalance.Error(
|
||||
integrationId = stakingIdFactory.createIntegrationId(currencyId = params.currencyId),
|
||||
address = null,
|
||||
integrationId = params.stakingId.integrationId,
|
||||
address = params.stakingId.address,
|
||||
)
|
||||
}
|
||||
|
||||
private var stakingId: StakingID? = null
|
||||
|
||||
override fun produce(): Flow<YieldBalance> {
|
||||
Timber.i("Producing yield balance for params:\n$params")
|
||||
|
||||
|
|
@ -53,12 +48,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
|
|||
params = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId),
|
||||
)
|
||||
.mapNotNull { balances ->
|
||||
val currentStakingId = getStakingId()
|
||||
|
||||
if (currentStakingId == null) {
|
||||
Timber.i("Staking ID is null for params: $params")
|
||||
return@mapNotNull YieldBalance.Unsupported
|
||||
}
|
||||
val currentStakingId = params.stakingId
|
||||
|
||||
val currentBalances = balances.filter { it.getStakingId() == currentStakingId }
|
||||
|
||||
|
|
@ -86,34 +76,16 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
|
|||
currentBalances.first()
|
||||
}
|
||||
} else {
|
||||
val balance = currentBalances.firstOrNull()
|
||||
val balance = currentBalances.firstOrNull() ?: return@mapNotNull null
|
||||
|
||||
if (balance != null) {
|
||||
Timber.i("Yield balance found for $currentStakingId:\n$balance")
|
||||
balance
|
||||
} else {
|
||||
Timber.i("No yield balance found for $currentStakingId:\n${YieldBalance.Unsupported}")
|
||||
YieldBalance.Unsupported
|
||||
}
|
||||
Timber.i("Yield balance found for $currentStakingId:\n$balance")
|
||||
balance
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
private suspend fun getStakingId(): StakingID? {
|
||||
val saved = stakingId
|
||||
|
||||
if (saved != null) return saved
|
||||
|
||||
return stakingIdFactory.create(
|
||||
userWalletId = params.userWalletId,
|
||||
currencyId = params.currencyId,
|
||||
network = params.network,
|
||||
)
|
||||
.also { stakingId = it }
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : SingleYieldBalanceProducer.Factory {
|
||||
override fun create(params: SingleYieldBalanceProducer.Params): DefaultSingleYieldBalanceProducer
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package com.tangem.data.staking.single
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.common.test.utils.getEmittedValues
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.data.staking.toDomain
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
|
|
@ -15,40 +12,49 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
|
|||
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
|
||||
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.*
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class DefaultSingleYieldBalanceProducerTest {
|
||||
|
||||
private val params = SingleYieldBalanceProducer.Params(
|
||||
userWalletId = UserWalletId(stringValue = "011"),
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
stakingId = tonId,
|
||||
)
|
||||
|
||||
private val multiNetworkStatusSupplier = mockk<MultiYieldBalanceSupplier>()
|
||||
private val stakingIdFactory = mockk<StakingIdFactory>()
|
||||
private val analyticsExceptionHandler = mockk<AnalyticsExceptionHandler>(relaxUnitFun = true)
|
||||
private val dispatchers = TestingCoroutineDispatcherProvider()
|
||||
|
||||
private val producer = DefaultSingleYieldBalanceProducer(
|
||||
params = params,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
multiYieldBalanceSupplier = multiNetworkStatusSupplier,
|
||||
analyticsExceptionHandler = analyticsExceptionHandler,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(multiNetworkStatusSupplier, analyticsExceptionHandler)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that flow is mapped for data from params`() = runTest {
|
||||
fun `flow is mapped for data from params`() = runTest {
|
||||
// Arrange
|
||||
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
|
||||
|
||||
val expected = flowOf(
|
||||
val multiFlow = flowOf(
|
||||
setOf(
|
||||
balance,
|
||||
MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId).toDomain(),
|
||||
|
|
@ -56,97 +62,89 @@ internal class DefaultSingleYieldBalanceProducerTest {
|
|||
)
|
||||
|
||||
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns expected
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
|
||||
|
||||
val actual = producer.produce()
|
||||
// Act
|
||||
val actual = getEmittedValues(flow = producer.produce())
|
||||
|
||||
verify { multiNetworkStatusSupplier(multiParams) }
|
||||
Truth.assertThat(actual).hasSize(1)
|
||||
Truth.assertThat(actual).containsExactly(balance)
|
||||
|
||||
val values = getEmittedValues(flow = actual)
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
|
||||
Truth.assertThat(values.size).isEqualTo(1)
|
||||
Truth.assertThat(values).isEqualTo(listOf(balance))
|
||||
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that flow is updated if yield balance is updated`() = runTest {
|
||||
val expected = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
|
||||
fun `flow is updated if yield balance is updated`() = runTest {
|
||||
// Arrange
|
||||
val multiFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
|
||||
|
||||
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns expected
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
|
||||
|
||||
val actual = producer.produceWithFallback()
|
||||
val producerFlow = producer.produceWithFallback()
|
||||
|
||||
verify { multiNetworkStatusSupplier(multiParams) }
|
||||
|
||||
// first emit
|
||||
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
|
||||
expected.emit(value = setOf(balance))
|
||||
val updatedBalance = YieldBalance.Error(integrationId = tonId.integrationId, address = tonId.address)
|
||||
|
||||
val values1 = getEmittedValues(flow = actual)
|
||||
// Act (first emit)
|
||||
multiFlow.emit(value = setOf(balance))
|
||||
val actual1 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
// Assert (first emit)
|
||||
Truth.assertThat(actual1).hasSize(1)
|
||||
Truth.assertThat(actual1).containsExactly(balance)
|
||||
|
||||
Truth.assertThat(values1.size).isEqualTo(1)
|
||||
Truth.assertThat(values1).isEqualTo(listOf(balance))
|
||||
// Act (second emit)
|
||||
multiFlow.emit(value = setOf(updatedBalance))
|
||||
val actual2 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
// second emit
|
||||
val updatedStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = tonId.address)
|
||||
expected.emit(value = setOf(updatedStatus))
|
||||
// Assert (second emit)
|
||||
Truth.assertThat(actual2).hasSize(2)
|
||||
Truth.assertThat(actual2).containsExactly(balance, updatedBalance)
|
||||
|
||||
val values2 = getEmittedValues(flow = actual)
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
|
||||
Truth.assertThat(values2.size).isEqualTo(2)
|
||||
Truth.assertThat(values2).isEqualTo(listOf(balance, updatedStatus))
|
||||
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that flow is filtered the same status`() = runTest {
|
||||
val expected = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
|
||||
fun `flow is filtered the same status`() = runTest {
|
||||
// Arrange
|
||||
val multiFlow = MutableSharedFlow<Set<YieldBalance>>(replay = 2, extraBufferCapacity = 1)
|
||||
|
||||
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns expected
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
|
||||
|
||||
val actual = producer.produceWithFallback()
|
||||
val producerFlow = producer.produceWithFallback()
|
||||
|
||||
verify { multiNetworkStatusSupplier(multiParams) }
|
||||
|
||||
// first emit
|
||||
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
|
||||
expected.emit(value = setOf(balance))
|
||||
|
||||
val values1 = getEmittedValues(flow = actual)
|
||||
// Act (first emit)
|
||||
multiFlow.emit(value = setOf(balance))
|
||||
val actual1 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
// Assert (first emit)
|
||||
Truth.assertThat(actual1).hasSize(1)
|
||||
Truth.assertThat(actual1).containsExactly(balance)
|
||||
|
||||
Truth.assertThat(values1.size).isEqualTo(1)
|
||||
Truth.assertThat(values1).isEqualTo(listOf(balance))
|
||||
// Act (second emit)
|
||||
multiFlow.emit(value = setOf(balance))
|
||||
val actual2 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
// second emit
|
||||
expected.emit(value = setOf(balance))
|
||||
// Assert (second emit)
|
||||
Truth.assertThat(actual2).hasSize(1)
|
||||
Truth.assertThat(actual2).containsExactly(balance)
|
||||
|
||||
val values2 = getEmittedValues(flow = actual)
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
|
||||
Truth.assertThat(values2.size).isEqualTo(1)
|
||||
Truth.assertThat(values2).isEqualTo(listOf(balance))
|
||||
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test if flow throws exception`() = runTest {
|
||||
fun `flow throws exception`() = runTest {
|
||||
// Arrange
|
||||
val exception = IllegalStateException()
|
||||
|
||||
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
|
||||
|
||||
val innerFlow = MutableStateFlow(value = false)
|
||||
val expected = flow {
|
||||
val multiFlow = flow {
|
||||
if (innerFlow.value) {
|
||||
emit(setOf(balance))
|
||||
} else {
|
||||
|
|
@ -156,83 +154,52 @@ internal class DefaultSingleYieldBalanceProducerTest {
|
|||
.buffer(capacity = 5)
|
||||
|
||||
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns expected
|
||||
every { stakingIdFactory.createIntegrationId(currencyId = params.currencyId) } returns tonId.integrationId
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
|
||||
|
||||
val actual = producer.produceWithFallback()
|
||||
val producerFlow = producer.produceWithFallback()
|
||||
|
||||
verify { multiNetworkStatusSupplier(multiParams) }
|
||||
// Act (first emit)
|
||||
val actual1 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
val values1 = getEmittedValues(flow = actual)
|
||||
// Assert (first emit)
|
||||
val fallbackStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = "0x1")
|
||||
|
||||
coVerify(inverse = true) { stakingIdFactory.create(any(), any(), any()) }
|
||||
|
||||
Truth.assertThat(values1.size).isEqualTo(1)
|
||||
val fallbackStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = null)
|
||||
Truth.assertThat(values1).isEqualTo(listOf(fallbackStatus))
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
|
||||
Truth.assertThat(actual1).hasSize(1)
|
||||
Truth.assertThat(actual1).containsExactly(fallbackStatus)
|
||||
|
||||
// Act (second emit)
|
||||
innerFlow.emit(value = true)
|
||||
val actual2 = getEmittedValues(flow = producerFlow)
|
||||
|
||||
val values2 = getEmittedValues(flow = actual)
|
||||
Truth.assertThat(actual2).hasSize(1)
|
||||
Truth.assertThat(actual2).containsExactly(balance)
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
|
||||
Truth.assertThat(values2.size).isEqualTo(1)
|
||||
Truth.assertThat(values2).isEqualTo(listOf(balance))
|
||||
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test if flow doesn't contain network from params`() = runTest {
|
||||
fun `flow doesn't contain network from params`() = runTest {
|
||||
// Arrange
|
||||
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId).toDomain()
|
||||
|
||||
val yieldBalancesFlow = flowOf(setOf(balance))
|
||||
val multiFlow = flowOf(setOf(balance))
|
||||
|
||||
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns yieldBalancesFlow
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns multiFlow
|
||||
|
||||
val actual = producer.produce()
|
||||
val producerFlow = producer.produce()
|
||||
|
||||
verify { multiNetworkStatusSupplier(multiParams) }
|
||||
// Act
|
||||
val actual = getEmittedValues(flow = producerFlow)
|
||||
|
||||
val values = getEmittedValues(flow = actual)
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEmpty()
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
|
||||
val expected = YieldBalance.Unsupported
|
||||
Truth.assertThat(values.first()).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test if wallet manager facade returns null`() = runTest {
|
||||
val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain()
|
||||
|
||||
val yieldBalancesFlow = flowOf(setOf(balance))
|
||||
|
||||
val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId)
|
||||
every { multiNetworkStatusSupplier(multiParams) } returns yieldBalancesFlow
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns null
|
||||
|
||||
val actual = producer.produce()
|
||||
|
||||
verify { multiNetworkStatusSupplier(multiParams) }
|
||||
|
||||
val values = getEmittedValues(flow = actual)
|
||||
|
||||
coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) }
|
||||
|
||||
val expected = YieldBalance.Unsupported
|
||||
Truth.assertThat(values.first()).isEqualTo(expected)
|
||||
verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
val mocks = MockCryptoCurrencyFactory()
|
||||
|
||||
val ton = mocks.createCoin(Blockchain.TON)
|
||||
|
||||
val tonId = MockYieldBalanceWrapperDTOFactory.defaultStakingId
|
||||
val solanaId = StakingID(
|
||||
integrationId = "solana-sol-native-multivalidator-staking",
|
||||
|
|
|
|||
|
|
@ -42,12 +42,37 @@ class StakingIdFactory(
|
|||
userWalletId: UserWalletId,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network,
|
||||
): Either<Error, StakingID> {
|
||||
return createInternal(
|
||||
currencyId = currencyId,
|
||||
defaultAddressProvider = {
|
||||
walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [StakingID] for the given cryptocurrency and default address
|
||||
*
|
||||
* @param currencyId the identifier of the cryptocurrency
|
||||
* @param defaultAddress the default address for staking, can be null
|
||||
*/
|
||||
fun create(currencyId: CryptoCurrency.ID, defaultAddress: String?): Either<Error, StakingID> {
|
||||
return createInternal(
|
||||
currencyId = currencyId,
|
||||
defaultAddressProvider = { defaultAddress },
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun createInternal(
|
||||
currencyId: CryptoCurrency.ID,
|
||||
defaultAddressProvider: () -> String?,
|
||||
): Either<Error, StakingID> = either {
|
||||
val integrationId = StakingIntegrationID.create(currencyId = currencyId)
|
||||
|
||||
ensureNotNull(integrationId) { Error.UnsupportedCurrency }
|
||||
|
||||
val address = walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network)
|
||||
val address = defaultAddressProvider().takeUnless { it.isNullOrEmpty() }
|
||||
|
||||
ensureNotNull(address) { Error.UnableToGetAddress(integrationId = integrationId) }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package com.tangem.domain.staking.single
|
||||
|
||||
import com.tangem.domain.core.flow.FlowProducer
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
|
||||
/**
|
||||
* Producer of yield balance for selected wallet [UserWalletId]
|
||||
|
|
@ -15,16 +14,14 @@ interface SingleYieldBalanceProducer : FlowProducer<YieldBalance> {
|
|||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val currencyId: CryptoCurrency.ID,
|
||||
val network: Network,
|
||||
val stakingId: StakingID,
|
||||
) {
|
||||
|
||||
override fun toString(): String {
|
||||
return """
|
||||
SingleYieldBalanceProducer.Params(
|
||||
userWalletId = $userWalletId,
|
||||
currencyId = $currencyId,
|
||||
network = $network
|
||||
stakingId = $stakingId,
|
||||
)
|
||||
""".trimIndent()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@ dependencies {
|
|||
implementation(deps.jodatime)
|
||||
implementation(deps.reKotlin)
|
||||
|
||||
implementation(tangemDeps.blockchain) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
|
||||
/** Tests */
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit5)
|
||||
|
|
@ -61,7 +65,4 @@ dependencies {
|
|||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(projects.common.test)
|
||||
testImplementation(tangemDeps.blockchain) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import arrow.core.raise.Raise
|
|||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.recover
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.domain.core.utils.EitherFlow
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
|
@ -18,6 +19,9 @@ import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
|||
import com.tangem.domain.quotes.QuotesRepository
|
||||
import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
|
||||
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.staking.model.isStakingSupported
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
|
||||
|
|
@ -49,6 +53,7 @@ abstract class BaseCurrencyStatusOperations(
|
|||
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
|
||||
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
|
||||
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
) {
|
||||
|
||||
|
|
@ -82,7 +87,7 @@ abstract class BaseCurrencyStatusOperations(
|
|||
return getCurrencyStatusFlow(userWalletId = userWalletId, currency = currency)
|
||||
}
|
||||
|
||||
fun getCurrencyStatusFlow(
|
||||
suspend fun getCurrencyStatusFlow(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
includeQuotes: Boolean = true,
|
||||
|
|
@ -105,9 +110,24 @@ abstract class BaseCurrencyStatusOperations(
|
|||
|
||||
val statusFlow = getNetworkStatus(userWalletId = userWalletId, network = currency.network)
|
||||
|
||||
val yieldBalanceFlow = getYieldBalance(userWalletId = userWalletId, cryptoCurrency = currency)
|
||||
val isStakingSupported = currency.network.toBlockchain().isStakingSupported
|
||||
|
||||
return if (subscribeOnYieldBalance) {
|
||||
val yieldBalanceFlow = if (isStakingSupported) {
|
||||
val stakingId = stakingIdFactory.create(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
.getOrNull()
|
||||
|
||||
stakingId?.let {
|
||||
getYieldBalance(userWalletId = userWalletId, stakingId = it)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return if (subscribeOnYieldBalance && yieldBalanceFlow != null) {
|
||||
combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance ->
|
||||
currencyStatusProxyCreator.createCurrencyStatus(
|
||||
currency = currency,
|
||||
|
|
@ -334,15 +354,11 @@ abstract class BaseCurrencyStatusOperations(
|
|||
.bind()
|
||||
}
|
||||
|
||||
private fun getYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): EitherFlow<Error, YieldBalance> {
|
||||
private fun getYieldBalance(userWalletId: UserWalletId, stakingId: StakingID): EitherFlow<Error, YieldBalance> {
|
||||
return singleYieldBalanceSupplier(
|
||||
params = SingleYieldBalanceProducer.Params(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
network = cryptoCurrency.network,
|
||||
stakingId = stakingId,
|
||||
),
|
||||
)
|
||||
.map<YieldBalance, Either<Error, YieldBalance>> { it.right() }
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.tangem.domain.quotes.QuotesRepository
|
|||
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||
import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
|
||||
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
|
|
@ -56,6 +57,7 @@ class CachedCurrenciesStatusesOperations(
|
|||
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
) : BaseCurrenciesStatusesOperations,
|
||||
BaseCurrencyStatusOperations(
|
||||
|
|
@ -67,6 +69,7 @@ class CachedCurrenciesStatusesOperations(
|
|||
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
|
||||
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
|
||||
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
) {
|
||||
|
||||
|
|
@ -371,20 +374,19 @@ class CachedCurrenciesStatusesOperations(
|
|||
return channelFlow {
|
||||
val state = MutableStateFlow(emptyList<YieldBalance>())
|
||||
|
||||
cryptoCurrencies.onEach {
|
||||
val stakingIds = cryptoCurrencies.mapNotNullTo(hashSetOf()) {
|
||||
stakingIdFactory.create(userWalletId = userWalletId, currencyId = it.id, network = it.network)
|
||||
.getOrNull()
|
||||
}
|
||||
|
||||
stakingIds.onEach {
|
||||
launch {
|
||||
singleYieldBalanceSupplier(
|
||||
params = SingleYieldBalanceProducer.Params(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = it.id,
|
||||
network = it.network,
|
||||
),
|
||||
params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = it),
|
||||
)
|
||||
.onEach { balance ->
|
||||
state.update { loadedBalances ->
|
||||
loadedBalances.addOrReplace(balance) {
|
||||
it.integrationId == balance.integrationId && it.address == balance.address
|
||||
}
|
||||
loadedBalances.addOrReplace(balance) { balance.getStakingId() == it }
|
||||
}
|
||||
}
|
||||
.launchIn(scope = this)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue