Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-16 11:27:36 +04:00
parent 0598dd8e16
commit bbf13f1ae8
32 changed files with 53 additions and 142 deletions

View file

@ -45,15 +45,6 @@ interface TangemTechApi {
@GET("v1/geo") @GET("v1/geo")
suspend fun getUserCountryCode(): GeoResponse suspend fun getUserCountryCode(): GeoResponse
@GET("v1/user-tokens/{user-id}")
suspend fun getUserTokens(@Path(value = "user-id") userId: String): ApiResponse<UserTokensResponse>
@PUT("v1/user-tokens/{user-id}")
suspend fun saveUserTokens(
@Path(value = "user-id") userId: String,
@Body userTokens: UserTokensResponse,
): ApiResponse<Unit>
@PUT("/v1/wallets/{walletId}/tokens") @PUT("/v1/wallets/{walletId}/tokens")
suspend fun saveTokens( suspend fun saveTokens(
@Path(value = "walletId") userId: String, @Path(value = "walletId") userId: String,
@ -138,14 +129,8 @@ interface TangemTechApi {
@PATCH("v1/user-wallets/wallets/{wallet_id}") @PATCH("v1/user-wallets/wallets/{wallet_id}")
suspend fun updateWallet(@Path("wallet_id") walletId: String, @Body body: WalletBody): ApiResponse<Unit> suspend fun updateWallet(@Path("wallet_id") walletId: String, @Body body: WalletBody): ApiResponse<Unit>
@POST("v1/user-wallets/wallets/create-and-connect-by-appuid/{application_id}")
suspend fun associateApplicationIdWithWallets(
@Path("application_id") applicationId: String,
@Body body: List<WalletIdBody>,
): ApiResponse<Unit>
@PUT("/v1/user-wallets/applications/{application_id}/wallets") @PUT("/v1/user-wallets/applications/{application_id}/wallets")
suspend fun associateApplicationIdWithWalletsV2( suspend fun associateApplicationIdWithWallets(
@Path("application_id") applicationId: String, @Path("application_id") applicationId: String,
@Body body: AssociateApplicationIdWithWalletsBody, @Body body: AssociateApplicationIdWithWalletsBody,
): ApiResponse<Unit> ): ApiResponse<Unit>

View file

@ -11,23 +11,6 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
*/ */
class UserTokensBackwardCompatibility { class UserTokensBackwardCompatibility {
fun applyCompatibilityAndGetUpdated(userTokensResponse: UserTokensResponse): UserTokensResponse {
return userTokensResponse.copy(
tokens = userTokensResponse.tokens.map { token ->
val oldSavedId = NETWORKS_TO_OLD_SAVED_IDS[token.networkId]
if (oldSavedId != null && token.id == oldSavedId) {
Blockchain.fromNetworkId(token.networkId)?.let { blockchain ->
token.copy(
id = blockchain.toCoinId(),
)
} ?: token
} else {
token
}
},
)
}
fun applyCompatibilityAndGetUpdated(tokens: List<UserTokensResponse.Token>): List<UserTokensResponse.Token> { fun applyCompatibilityAndGetUpdated(tokens: List<UserTokensResponse.Token>): List<UserTokensResponse.Token> {
return tokens.map { token -> return tokens.map { token ->
val oldSavedId = NETWORKS_TO_OLD_SAVED_IDS[token.networkId] val oldSavedId = NETWORKS_TO_OLD_SAVED_IDS[token.networkId]

View file

@ -362,7 +362,7 @@ internal class DefaultWalletsRepository(
override suspend fun associateWallets(applicationId: String, wallets: List<UserWallet>) = override suspend fun associateWallets(applicationId: String, wallets: List<UserWallet>) =
withContext(dispatchers.io) { withContext(dispatchers.io) {
val associateApplicationIdWithWallets: suspend () -> ApiResponse<Unit> = { val associateApplicationIdWithWallets: suspend () -> ApiResponse<Unit> = {
tangemTechApi.associateApplicationIdWithWalletsV2( tangemTechApi.associateApplicationIdWithWallets(
applicationId = applicationId, applicationId = applicationId,
body = AssociateApplicationIdWithWalletsBody( body = AssociateApplicationIdWithWalletsBody(
walletIds = wallets.map { it.walletId.stringValue }.distinct(), walletIds = wallets.map { it.walletId.stringValue }.distinct(),

View file

@ -186,7 +186,7 @@ class DefaultWalletsRepositoryTest {
inner class AssociateWallets { inner class AssociateWallets {
@Test @Test
fun `should convert and send to API V2`() = runTest { fun `should convert and send to associateApplicationIdWithWallets`() = runTest {
// Arrange // Arrange
val applicationId = "test_app_id" val applicationId = "test_app_id"
val wallet1Id = "1234567890abcdef" val wallet1Id = "1234567890abcdef"
@ -202,7 +202,7 @@ class DefaultWalletsRepositoryTest {
) )
coEvery { coEvery {
tangemTechApi.associateApplicationIdWithWalletsV2(eq(applicationId), any()) tangemTechApi.associateApplicationIdWithWallets(eq(applicationId), any())
} returns ApiResponse.Success(Unit) } returns ApiResponse.Success(Unit)
// Act // Act
@ -210,7 +210,7 @@ class DefaultWalletsRepositoryTest {
// Assert // Assert
coVerify(exactly = 1) { coVerify(exactly = 1) {
tangemTechApi.associateApplicationIdWithWalletsV2( tangemTechApi.associateApplicationIdWithWallets(
applicationId = eq(applicationId), applicationId = eq(applicationId),
body = match { body -> body = match { body ->
body.walletIds.size == 2 && body.walletIds.size == 2 &&

View file

@ -3,13 +3,11 @@ package com.tangem.domain.account.status.di
import com.tangem.domain.account.repository.AccountsCRUDRepository import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.usecase.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.account.status.usecase.* import com.tangem.domain.account.status.usecase.*
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner
import com.tangem.domain.account.supplier.SingleAccountListSupplier import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.express.ExpressServiceFetcher import com.tangem.domain.express.ExpressServiceFetcher
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
@ -23,6 +21,7 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -76,10 +75,10 @@ internal object AccountStatusUseCaseModule {
@Provides @Provides
@Singleton @Singleton
fun provideGetWalletTotalBalanceUseCaseV2( fun provideGetWalletTotalBalanceUseCase(
multiAccountStatusListSupplier: MultiAccountStatusListSupplier, multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
): GetWalletTotalBalanceUseCaseV2 { ): GetWalletTotalBalanceUseCase {
return GetWalletTotalBalanceUseCaseV2( return GetWalletTotalBalanceUseCase(
multiAccountStatusListSupplier = multiAccountStatusListSupplier, multiAccountStatusListSupplier = multiAccountStatusListSupplier,
) )
} }

View file

@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.map
* *
* @param multiAccountStatusListSupplier Supplier that provides the status list for multiple accounts. * @param multiAccountStatusListSupplier Supplier that provides the status list for multiple accounts.
*/ */
class GetWalletTotalBalanceUseCaseV2( class GetWalletTotalBalanceUseCase(
private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier, private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
) { ) {

View file

@ -1 +0,0 @@
/build

View file

@ -1,9 +0,0 @@
plugins {
alias(deps.plugins.kotlin.jvm)
alias(deps.plugins.kotlin.serialization)
id("configuration")
}
dependencies {
implementation(deps.kotlin.serialization)
}

View file

@ -1,26 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>BooleanPropertyNaming:TokenActionsState.kt$TokenActionsState.ActionState.Swap$val showBadge: Boolean</ID>
<ID>CanBeNonNullable:BaseActionsFactory.kt$BaseActionsFactory$requirementsDeferred: Deferred&lt;AssetRequirementsCondition?&gt;?</ID>
<ID>CanBeNonNullable:CommonActionsFactory.kt$CommonActionsFactory$swapUnavailableReasonDeferred: Deferred&lt;ScenarioUnavailabilityReason&gt;?</ID>
<ID>ExplicitCollectionElementAccessMethod:GetWalletTotalBalanceUseCase.kt$GetWalletTotalBalanceUseCase$walletBalanceCache.put(userWalletId, content)</ID>
<ID>MultilineLambdaItParameter:BaseCurrencyStatusOperations.kt$BaseCurrencyStatusOperations${ singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(rawCurrencyId = it)) .firstOrNull() }</ID>
<ID>MultilineLambdaItParameter:BaseCurrencyStatusOperations.kt$BaseCurrencyStatusOperations${ val exception = IllegalStateException("$it") Error.DataError(exception) }</ID>
<ID>MultilineLambdaItParameter:FetchCurrencyStatusUseCase.kt$FetchCurrencyStatusUseCase${ when (it) { is StakingIdFactory.Error.UnableToGetAddress -&gt; raise(IllegalStateException("$it")) StakingIdFactory.Error.UnsupportedCurrency -&gt; Unit.right() } return@either }</ID>
<ID>MultilineLambdaItParameter:GetBalanceNotEnoughForFeeWarningUseCase.kt$GetBalanceNotEnoughForFeeWarningUseCase${ it is CryptoCurrency.Token &amp;&amp; it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) &amp;&amp; it.network.derivationPath == tokenStatus.currency.network.derivationPath }</ID>
<ID>MultilineLambdaItParameter:GetCryptoCurrencyActionsUseCase.kt$GetCryptoCurrencyActionsUseCase${ TokenActionsState( walletId = userWallet.walletId, cryptoCurrencyStatus = cryptoCurrencyStatus, states = it.toList(), ) }</ID>
<ID>MultilineLambdaItParameter:GetWalletTotalBalanceUseCase.kt$GetWalletTotalBalanceUseCase${ Timber.e("failed to load balances with error: $it") TotalFiatBalance.Failed }</ID>
<ID>MultilineLambdaItParameter:PriceChangeCalculator.kt$PriceChangeCalculator${ val weight = it.value.fiatAmount.orZero().divide(balance, 2, RoundingMode.HALF_UP) val priceChange = it.value.priceChange.orZero() weight * priceChange }</ID>
<ID>MultilineLambdaItParameter:WalletBalanceFetcher.kt$WalletBalanceFetcher${ val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it) if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) { Timber.e("Unable to get staking ID for user wallet $userWalletId and currency ${it.id}") } stakingId }</ID>
<ID>NamedArguments:ApplyTokenListSortingUseCase.kt$ApplyTokenListSortingUseCase$saveTokens(userWalletId, currencies, isGrouped, isSortedByBalance)</ID>
<ID>NoNameShadowing:WalletBalanceFetcher.kt$WalletBalanceFetcher${ it is StakingIdFactory.Error.UnableToGetAddress }</ID>
<ID>NullableBooleanCheck:GetCurrencyCheckUseCase.kt$GetCurrencyCheckUseCase$recipientAddress?.let { currencyChecksRepository.checkIfAccountFunded( userWalletId, network, recipientAddress, ) } ?: false</ID>
<ID>SuspendFunWithFlowReturnType:BaseCurrencyStatusOperations.kt$BaseCurrencyStatusOperations$suspend</ID>
<ID>SuspendFunWithFlowReturnType:GetNetworkCoinStatusUseCase.kt$GetNetworkCoinStatusUseCase$suspend</ID>
<ID>SuspendFunWithFlowReturnType:GetSingleCryptoCurrencyStatusUseCase.kt$GetSingleCryptoCurrencyStatusUseCase$suspend</ID>
<ID>UnnecessaryAbstractClass:MultiWalletCryptoCurrenciesSupplier.kt$MultiWalletCryptoCurrenciesSupplier$MultiWalletCryptoCurrenciesSupplier</ID>
<ID>UnsafeCallOnNullableType:CommonActionsFactory.kt$CommonActionsFactory$swapUnavailableReasonDeferred!!</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,13 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ObjectExtendsThrowable:RemoveCurrencyError.kt$RemoveCurrencyError$HasLinkedTokens : RemoveCurrencyError</ID>
<ID>UseEmptyCounterpart:PromoAnalyticsEvent.kt$PromoAnalyticsEvent$mapOf()</ID>
<ID>UseEmptyCounterpart:TokenExchangeAnalyticsEvent.kt$TokenExchangeAnalyticsEvent$mapOf()</ID>
<ID>UseEmptyCounterpart:TokenOnrampAnalyticsEvent.kt$TokenOnrampAnalyticsEvent$mapOf()</ID>
<ID>UseEmptyCounterpart:TokenReceiveAnalyticsEvent.kt$TokenReceiveAnalyticsEvent$mapOf()</ID>
<ID>UseEmptyCounterpart:TokenReceiveNewAnalyticsEvent.kt$TokenReceiveNewAnalyticsEvent$mapOf()</ID>
<ID>UseEmptyCounterpart:TokenScreenAnalyticsEvent.kt$TokenScreenAnalyticsEvent$mapOf()</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -5,7 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
sealed class PromoAnalyticsEvent( sealed class PromoAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category = "Promotion", event = event, params = params) { ) : AnalyticsEvent(category = "Promotion", event = event, params = params) {
data class NoticePromotionBanner( data class NoticePromotionBanner(
private val source: AnalyticsParam.ScreensSources, private val source: AnalyticsParam.ScreensSources,

View file

@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
class TokenExchangeAnalyticsEvent( class TokenExchangeAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent("Token", event, params) { ) : AnalyticsEvent("Token", event, params) {
class CexTxStatusOpened(token: String, provider: String) : TokenScreenAnalyticsEvent( class CexTxStatusOpened(token: String, provider: String) : TokenScreenAnalyticsEvent(

View file

@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
sealed class TokenOnrampAnalyticsEvent( sealed class TokenOnrampAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent("Onramp", event, params) { ) : AnalyticsEvent("Onramp", event, params) {
class OnrampStatusOpened(tokenSymbol: String, provider: String, fiatCurrency: String) : TokenOnrampAnalyticsEvent( class OnrampStatusOpened(tokenSymbol: String, provider: String, fiatCurrency: String) : TokenOnrampAnalyticsEvent(

View file

@ -5,7 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
sealed class TokenReceiveAnalyticsEvent( sealed class TokenReceiveAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent("Token / Receive", event, params) { ) : AnalyticsEvent("Token / Receive", event, params) {
class ReceiveScreenOpened(token: String) : TokenReceiveAnalyticsEvent( class ReceiveScreenOpened(token: String) : TokenReceiveAnalyticsEvent(

View file

@ -9,7 +9,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
sealed class TokenReceiveNewAnalyticsEvent( sealed class TokenReceiveNewAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent("Token / Receive", event, params) { ) : AnalyticsEvent("Token / Receive", event, params) {
class ReceiveScreenOpened( class ReceiveScreenOpened(

View file

@ -14,7 +14,7 @@ import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
*/ */
sealed class TokenScreenAnalyticsEvent( sealed class TokenScreenAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent("Token", event, params) { ) : AnalyticsEvent("Token", event, params) {
/** Legacy event. It has a unique category, but it also is sent on TokenScreen */ /** Legacy event. It has a unique category, but it also is sent on TokenScreen */

View file

@ -1,7 +0,0 @@
package com.tangem.domain.tokens.model.remove
sealed class RemoveCurrencyError : Throwable() {
data class DataError(override val cause: Throwable) : RemoveCurrencyError()
object HasLinkedTokens : RemoveCurrencyError()
}

View file

@ -1,7 +0,0 @@
package com.tangem.domain.tokens.model.staking
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.staking.model.stakekit.Yield
fun Yield.getCurrentToken(rawCurrencyId: CryptoCurrency.RawID?) =
tokens.firstOrNull { rawCurrencyId?.value == it.coinGeckoId } ?: token

View file

@ -89,10 +89,10 @@ class GetBalanceNotEnoughForFeeWarningUseCase(
) )
.orEmpty() .orEmpty()
val token = tokens.find { val token = tokens.find { currency ->
it is CryptoCurrency.Token && currency is CryptoCurrency.Token &&
it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) && currency.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) &&
it.network.derivationPath == tokenStatus.currency.network.derivationPath currency.network.derivationPath == tokenStatus.currency.network.derivationPath
} }
return if (token != null) { return if (token != null) {

View file

@ -106,11 +106,11 @@ class GetCryptoCurrencyActionsUseCase(
} }
} }
} }
.map { .map { states ->
TokenActionsState( TokenActionsState(
walletId = userWallet.walletId, walletId = userWallet.walletId,
cryptoCurrencyStatus = cryptoCurrencyStatus, cryptoCurrencyStatus = cryptoCurrencyStatus,
states = it.toList(), states = states.toList(),
) )
} }
.flowOn(dispatchers.default) .flowOn(dispatchers.default)

View file

@ -36,13 +36,15 @@ class GetCurrencyCheckUseCase(
currencyStatus = feeCurrencyStatus, currencyStatus = feeCurrencyStatus,
balanceAfterTransaction = feeCurrencyBalanceAfterTransaction ?: BigDecimal.ZERO, balanceAfterTransaction = feeCurrencyBalanceAfterTransaction ?: BigDecimal.ZERO,
) )
val isAccountFunded = recipientAddress?.let { val isAccountFunded = if (recipientAddress != null) {
currencyChecksRepository.checkIfAccountFunded( currencyChecksRepository.checkIfAccountFunded(
userWalletId, userWalletId = userWalletId,
network, network = network,
recipientAddress, address = recipientAddress,
) )
} ?: false } else {
false
}
val utxoAmountLimit = if (currency is CryptoCurrency.Coin && amount != null && fee != null) { val utxoAmountLimit = if (currency is CryptoCurrency.Coin && amount != null && fee != null) {
currencyChecksRepository.checkUtxoAmountLimit( currencyChecksRepository.checkUtxoAmountLimit(
userWalletId = userWalletId, userWalletId = userWalletId,

View file

@ -14,6 +14,7 @@ import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer as Producer
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
@Suppress("UnnecessaryAbstractClass")
abstract class MultiWalletCryptoCurrenciesSupplier( abstract class MultiWalletCryptoCurrenciesSupplier(
override val factory: FlowProducer.Factory<Producer.Params, Producer>, override val factory: FlowProducer.Factory<Producer.Params, Producer>,
override val keyCreator: (Producer.Params) -> String, override val keyCreator: (Producer.Params) -> String,

View file

@ -137,6 +137,7 @@ internal open class BaseActionsFactory(
* @param isAddressAvailable indicates whether the address is available * @param isAddressAvailable indicates whether the address is available
* @param requirementsDeferred a deferred object containing the asset requirements condition * @param requirementsDeferred a deferred object containing the asset requirements condition
*/ */
@Suppress("CanBeNonNullable")
protected suspend fun ActionAvailabilityBuilder.addReceiveAction( protected suspend fun ActionAvailabilityBuilder.addReceiveAction(
isAddressAvailable: Boolean, isAddressAvailable: Boolean,
requirementsDeferred: Deferred<AssetRequirementsCondition?>?, requirementsDeferred: Deferred<AssetRequirementsCondition?>?,

View file

@ -140,6 +140,7 @@ internal class CommonActionsFactory(
} }
} }
@Suppress("CanBeNonNullable")
private suspend fun createSwapAction( private suspend fun createSwapAction(
userWallet: UserWallet, userWallet: UserWallet,
cryptoCurrencyStatus: CryptoCurrencyStatus, cryptoCurrencyStatus: CryptoCurrencyStatus,
@ -153,7 +154,7 @@ internal class CommonActionsFactory(
if (!isMultiCurrency) { if (!isMultiCurrency) {
return ActionState.Swap( return ActionState.Swap(
unavailabilityReason = ScenarioUnavailabilityReason.SingleWallet, unavailabilityReason = ScenarioUnavailabilityReason.SingleWallet,
showBadge = false, shouldShowBadge = false,
) )
} }
@ -161,21 +162,23 @@ internal class CommonActionsFactory(
cryptoCurrency.isCustom -> { cryptoCurrency.isCustom -> {
ActionState.Swap( ActionState.Swap(
unavailabilityReason = ScenarioUnavailabilityReason.CustomToken(cryptoCurrency.name), unavailabilityReason = ScenarioUnavailabilityReason.CustomToken(cryptoCurrency.name),
showBadge = false, shouldShowBadge = false,
) )
} }
cryptoCurrencyStatus.value is CryptoCurrencyStatus.NoQuote -> { cryptoCurrencyStatus.value is CryptoCurrencyStatus.NoQuote -> {
ActionState.Swap( ActionState.Swap(
unavailabilityReason = ScenarioUnavailabilityReason.TokenNoQuotes(cryptoCurrency.name), unavailabilityReason = ScenarioUnavailabilityReason.TokenNoQuotes(cryptoCurrency.name),
showBadge = false, shouldShowBadge = false,
) )
} }
else -> { else -> {
val reason = swapUnavailableReasonDeferred!!.await() val reason = requireNotNull(swapUnavailableReasonDeferred) {
"swapUnavailableReasonDeferred must not be null for available swap action"
}.await()
return ActionState.Swap( return ActionState.Swap(
unavailabilityReason = reason, unavailabilityReason = reason,
showBadge = reason == ScenarioUnavailabilityReason.None && shouldShowSwapStories, shouldShowBadge = reason == ScenarioUnavailabilityReason.None && shouldShowSwapStories,
) )
} }
} }

View file

@ -150,7 +150,7 @@ internal class OutdatedDataActionsFactory(
sources.networkSource == StatusSource.ONLY_CACHE -> ScenarioUnavailabilityReason.UsedOutdatedData sources.networkSource == StatusSource.ONLY_CACHE -> ScenarioUnavailabilityReason.UsedOutdatedData
else -> ScenarioUnavailabilityReason.DataLoading // CACHE source always when loading else -> ScenarioUnavailabilityReason.DataLoading // CACHE source always when loading
}, },
showBadge = false, shouldShowBadge = false,
) )
} }

View file

@ -64,7 +64,7 @@ internal class UnreachableActionsFactory(
ActionState.Send(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable), ActionState.Send(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable),
ActionState.Swap( ActionState.Swap(
unavailabilityReason = ScenarioUnavailabilityReason.Unreachable, unavailabilityReason = ScenarioUnavailabilityReason.Unreachable,
showBadge = false, shouldShowBadge = false,
), ),
ActionState.Sell(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable), ActionState.Sell(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable),
ActionState.Stake(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable, option = null), ActionState.Stake(unavailabilityReason = ScenarioUnavailabilityReason.Unreachable, option = null),

View file

@ -29,7 +29,7 @@ data class TokenActionsState(
data class Swap( data class Swap(
override val unavailabilityReason: ScenarioUnavailabilityReason, override val unavailabilityReason: ScenarioUnavailabilityReason,
val showBadge: Boolean, val shouldShowBadge: Boolean,
) : ActionState() ) : ActionState()
data class Send(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() data class Send(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()

View file

@ -50,9 +50,9 @@ object PriceChangeCalculator {
return createZeroPriceChange(source = walletTotalFiatBalance.source).lceContent() return createZeroPriceChange(source = walletTotalFiatBalance.source).lceContent()
} }
val total = statuses.sumOf { val total = statuses.sumOf { status ->
val weight = it.value.fiatAmount.orZero().divide(balance, 2, RoundingMode.HALF_UP) val weight = status.value.fiatAmount.orZero().divide(balance, 2, RoundingMode.HALF_UP)
val priceChange = it.value.priceChange.orZero() val priceChange = status.value.priceChange.orZero()
weight * priceChange weight * priceChange
}.stripTrailingZeros() }.stripTrailingZeros()

View file

@ -189,11 +189,11 @@ class WalletBalanceFetcher internal constructor(
userWalletId: UserWalletId, userWalletId: UserWalletId,
currencies: Set<CryptoCurrency>, currencies: Set<CryptoCurrency>,
): Either<Throwable, Unit> = either { ): Either<Throwable, Unit> = either {
val maybeStakingIds = currencies.map { val maybeStakingIds = currencies.map { currency ->
val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it) val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = currency)
if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) { if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) {
Timber.e("Unable to get staking ID for user wallet $userWalletId and currency ${it.id}") Timber.e("Unable to get staking ID for user wallet $userWalletId and currency ${currency.id}")
} }
stakingId stakingId

View file

@ -111,7 +111,7 @@ internal class PortfolioTokenUMConverter(
if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) {
when (action) { when (action) {
is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy
is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(action.showBadge) is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(action.shouldShowBadge)
is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive
is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake
is TokenActionsState.ActionState.YieldMode -> QuickActionUM.YieldMode(action.apy) is TokenActionsState.ActionState.YieldMode -> QuickActionUM.YieldMode(action.apy)

View file

@ -54,7 +54,7 @@ internal class TokenDetailsActionButtonsConverter(
TokenDetailsActionButton.Swap( TokenDetailsActionButton.Swap(
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None, dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = { clickIntents.onSwapClick(action.unavailabilityReason) }, onClick = { clickIntents.onSwapClick(action.unavailabilityReason) },
showBadge = action.showBadge, showBadge = action.shouldShowBadge,
) )
} }
else -> { else -> {

View file

@ -6,7 +6,7 @@ import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.SnackbarMessage import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.account.status.usecase.GetWalletTotalBalanceUseCaseV2 import com.tangem.domain.account.status.usecase.GetWalletTotalBalanceUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.error.SelectedAppCurrencyError import com.tangem.domain.appcurrency.error.SelectedAppCurrencyError
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.*
@Suppress("LongParameterList") @Suppress("LongParameterList")
internal class DefaultUserWalletsFetcher @AssistedInject constructor( internal class DefaultUserWalletsFetcher @AssistedInject constructor(
getWalletsUseCase: GetWalletsUseCase, getWalletsUseCase: GetWalletsUseCase,
private val getWalletTotalBalanceUseCaseV2: GetWalletTotalBalanceUseCaseV2, private val getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
@Assisted private val onWalletClick: (UserWalletId) -> Unit, @Assisted private val onWalletClick: (UserWalletId) -> Unit,
@ -99,7 +99,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
// We should not load balances in auth mode // We should not load balances in auth mode
flowOf(Lce.Loading(walletIds.associateWith { TotalFiatBalance.Loading })) flowOf(Lce.Loading(walletIds.associateWith { TotalFiatBalance.Loading }))
} else { } else {
getWalletTotalBalanceUseCaseV2(userWalletIds = walletIds) getWalletTotalBalanceUseCase(userWalletIds = walletIds)
} }
} }