Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-07 21:29:03 +04:00
commit 63133bb8fb
117 changed files with 1915 additions and 1265 deletions

View file

@ -10,7 +10,6 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -70,7 +69,6 @@ internal object ManageTokensDomainModule {
customTokensRepository: CustomTokensRepository,
walletManagersFacade: WalletManagersFacade,
currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository,
derivationsRepository: DerivationsRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
@ -83,7 +81,6 @@ internal object ManageTokensDomainModule {
customTokensRepository = customTokensRepository,
walletManagersFacade = walletManagersFacade,
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
derivationsRepository = derivationsRepository,
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,

View file

@ -11,7 +11,6 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import dagger.Module
@ -70,7 +69,6 @@ object MarketsDomainModule {
derivationsRepository: DerivationsRepository,
marketsTokenRepository: MarketsTokenRepository,
currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -82,7 +80,6 @@ object MarketsDomainModule {
derivationsRepository = derivationsRepository,
marketsTokenRepository = marketsTokenRepository,
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,

View file

@ -37,7 +37,6 @@ internal object TokensDomainModule {
@Singleton
fun provideAddCryptoCurrenciesUseCase(
currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -47,7 +46,6 @@ internal object TokensDomainModule {
): AddCryptoCurrenciesUseCase {
return AddCryptoCurrenciesUseCase(
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
@ -62,7 +60,6 @@ internal object TokensDomainModule {
fun provideFetchTokenListUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteFetcher: MultiQuoteFetcher,
@ -71,7 +68,6 @@ internal object TokensDomainModule {
): FetchTokenListUseCase {
return FetchTokenListUseCase(
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
@ -121,8 +117,8 @@ internal object TokensDomainModule {
fun provideGetCurrencyUseCase(
baseCurrencyStatusOperations: BaseCurrencyStatusOperations,
dispatchers: CoroutineDispatcherProvider,
): GetCurrencyStatusUpdatesUseCase {
return GetCurrencyStatusUpdatesUseCase(
): GetSingleCryptoCurrencyStatusUseCase {
return GetSingleCryptoCurrencyStatusUseCase(
currencyStatusOperations = baseCurrencyStatusOperations,
dispatchers = dispatchers,
)
@ -162,24 +158,11 @@ internal object TokensDomainModule {
)
}
@Provides
@Singleton
fun provideGetPrimaryCurrencyUseCase(
currencyStatusOperations: BaseCurrencyStatusOperations,
dispatchers: CoroutineDispatcherProvider,
): GetPrimaryCurrencyStatusUpdatesUseCase {
return GetPrimaryCurrencyStatusUpdatesUseCase(
currencyStatusOperations = currencyStatusOperations,
dispatchers = dispatchers,
)
}
@Provides
@Singleton
fun provideFetchCurrencyStatusUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
multiQuoteFetcher: MultiQuoteFetcher,
@ -188,7 +171,6 @@ internal object TokensDomainModule {
): FetchCurrencyStatusUseCase {
return FetchCurrencyStatusUseCase(
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
@ -203,7 +185,6 @@ internal object TokensDomainModule {
fun provideFetchCardTokenListUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteFetcher: MultiQuoteFetcher,
@ -212,7 +193,6 @@ internal object TokensDomainModule {
): FetchCardTokenListUseCase {
return FetchCardTokenListUseCase(
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
@ -222,14 +202,6 @@ internal object TokensDomainModule {
)
}
@Provides
@Singleton
fun providesGetCryptoCurrencyStatusSyncUseCase(
currencyStatusOperations: BaseCurrencyStatusOperations,
): GetCryptoCurrencyStatusSyncUseCase {
return GetCryptoCurrencyStatusSyncUseCase(currencyStatusOperations)
}
@Provides
@Singleton
fun provideGetCryptoCurrencyUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrencyUseCase {
@ -328,14 +300,10 @@ internal object TokensDomainModule {
@Provides
@Singleton
fun provideUpdateDelayedCurrencyStatusUseCase(
networksRepository: NetworksRepository,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): UpdateDelayedNetworkStatusUseCase {
return UpdateDelayedNetworkStatusUseCase(
networksRepository = networksRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -413,7 +381,6 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
quotesRepositoryV2: QuotesRepositoryV2,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
@ -427,7 +394,6 @@ internal object TokensDomainModule {
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
quotesRepositoryV2 = quotesRepositoryV2,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
@ -447,7 +413,6 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
quotesRepositoryV2: QuotesRepositoryV2,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
@ -461,7 +426,6 @@ internal object TokensDomainModule {
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
quotesRepositoryV2 = quotesRepositoryV2,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,

View file

@ -4,9 +4,7 @@ import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.usecase.*
@ -43,7 +41,6 @@ internal object TransactionDomainModule {
transactionRepository: TransactionRepository,
walletManagersFacade: WalletManagersFacade,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): SendTransactionUseCase {
return SendTransactionUseCase(
demoConfig = DemoConfig(),
@ -51,7 +48,6 @@ internal object TransactionDomainModule {
transactionRepository = transactionRepository,
walletManagersFacade = walletManagersFacade,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -61,17 +57,13 @@ internal object TransactionDomainModule {
cardSdkConfigRepository: CardSdkConfigRepository,
walletManagersFacade: WalletManagersFacade,
currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): AssociateAssetUseCase {
return AssociateAssetUseCase(
cardSdkConfigRepository = cardSdkConfigRepository,
walletManagersFacade = walletManagersFacade,
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
}

View file

@ -7,9 +7,6 @@ internal class DefaultTokensFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager,
) : TokensFeatureToggles {
override val isNetworksLoadingRefactoringEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "NETWORKS_LOADING_REFACTORING_ENABLED")
override val isQuotesLoadingRefactoringEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "QUOTES_LOADING_REFACTORING_ENABLED")

View file

@ -13,6 +13,7 @@ import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
@ -64,14 +65,18 @@ internal class DefaultLegacyWalletConnectRepositoryFacade constructor(
if (isNewWc) stub.updateSessions() else legacy.updateSessions()
}
override fun pair(uri: String, source: SourceType) {
override fun pair(userWalletId: UserWalletId, uri: String, source: SourceType) {
val src = when (source) {
SourceType.QR -> WcPairRequest.Source.QR
SourceType.DEEPLINK -> WcPairRequest.Source.DEEPLINK
SourceType.CLIPBOARD -> WcPairRequest.Source.CLIPBOARD
SourceType.ETC -> WcPairRequest.Source.ETC
}
if (isNewWc) wcPairService.pair(WcPairRequest(uri, src)) else legacy.pair(uri, source)
if (isNewWc) {
wcPairService.pair(WcPairRequest(uri = uri, source = src, userWalletId = userWalletId))
} else {
legacy.pair(userWalletId = userWalletId, uri = uri, source = source)
}
}
override fun disconnect(topic: String) {
@ -110,7 +115,7 @@ internal class LegacyWalletConnectRepositoryStub : LegacyWalletConnectRepository
override fun updateSessions() = Unit
override fun pair(uri: String, source: SourceType) = Unit
override fun pair(userWalletId: UserWalletId, uri: String, source: SourceType) = Unit
override fun disconnect(topic: String) = Unit
@ -405,7 +410,7 @@ internal class DefaultLegacyWalletConnectRepository(
this.userNamespaces = userNamespaces
}
override fun pair(uri: String, source: SourceType) {
override fun pair(userWalletId: UserWalletId, uri: String, source: SourceType) {
analyticsHandler.send(WalletConnect.NewSessionInitiated(source = source))
WalletKit.pair(
params = Wallet.Params.Pair(uri),

View file

@ -1,6 +1,7 @@
package com.tangem.tap.domain.walletconnect2.domain
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.walletconnect2.domain.models.*
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
import kotlinx.coroutines.flow.Flow
@ -19,7 +20,7 @@ interface LegacyWalletConnectRepository {
fun updateSessions()
fun pair(uri: String, source: SourceType)
fun pair(userWalletId: UserWalletId, uri: String, source: SourceType)
fun disconnect(topic: String)

View file

@ -12,6 +12,7 @@ import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsReposit
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.filterNotNull
@ -139,7 +140,11 @@ class WalletConnectInteractor(
if (deeplinkStack.empty()) return
val lastDeeplink = deeplinkStack.pop()
val action = WalletConnectAction
.OpenSession(lastDeeplink, WalletConnectAction.OpenSession.SourceType.DEEPLINK)
.OpenSession(
wcUri = lastDeeplink,
source = WalletConnectAction.OpenSession.SourceType.DEEPLINK,
userWalletId = UserWalletId(userWalletId),
)
store.dispatchOnMain(action)
}.onFailure {
Timber.e("WC deeplink handling failed. $it")
@ -393,7 +398,11 @@ class WalletConnectInteractor(
}
if (isWalletConnectReadyForDeepLinks) {
val action = WalletConnectAction.OpenSession(deeplink, WalletConnectAction.OpenSession.SourceType.DEEPLINK)
val action = WalletConnectAction.OpenSession(
wcUri = deeplink,
source = WalletConnectAction.OpenSession.SourceType.DEEPLINK,
userWalletId = UserWalletId(userWalletId),
)
store.dispatchOnMain(action)
} else {
deeplinkStack.push(deeplink)

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents
@ -14,6 +15,7 @@ sealed class WalletConnectAction : Action {
data class OpenSession(
val wcUri: String,
val source: SourceType,
val userWalletId: UserWalletId,
) : WalletConnectAction() {
enum class SourceType { QR, DEEPLINK, CLIPBOARD, ETC }
}

View file

@ -63,7 +63,11 @@ class WalletConnectMiddleware {
is WalletConnectAction.OpenSession -> {
val index = action.wcUri.indexOf("@")
when (action.wcUri[index + 1]) {
'2' -> walletConnectRepository.pair(uri = action.wcUri, source = action.source)
'2' -> walletConnectRepository.pair(
uri = action.wcUri,
source = action.source,
userWalletId = action.userWalletId,
)
'1' -> {
store.dispatchOnMain(WalletConnectAction.UnsupportedDappRequest)
store.dispatchOnMain(

View file

@ -22,10 +22,10 @@ import org.rekotlin.StoreSubscriber
@Suppress("UnusedPrivateMember")
internal class DefaultWalletConnectComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: Unit,
@Assisted params: WalletConnectComponent.Params,
) : WalletConnectComponent, AppComponentContext by appComponentContext, StoreSubscriber<WalletConnectState> {
private val model: WalletConnectModel = getOrCreateModel()
private val model: WalletConnectModel = getOrCreateModel(params)
private var screenState: MutableState<WalletConnectScreenState> =
mutableStateOf(model.updateState(store.state.walletConnectState))
@ -65,6 +65,9 @@ internal class DefaultWalletConnectComponent @AssistedInject constructor(
@AssistedFactory
interface Factory : WalletConnectComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultWalletConnectComponent
override fun create(
context: AppComponentContext,
params: WalletConnectComponent.Params,
): DefaultWalletConnectComponent
}
}

View file

@ -4,10 +4,12 @@ import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent
import com.tangem.tap.store
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toImmutableList
@ -22,13 +24,22 @@ import javax.inject.Inject
internal class WalletConnectModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
paramsContainer: ParamsContainer,
) : Model() {
private val params = paramsContainer.require<WalletConnectComponent.Params>()
init {
modelScope.launch {
listenToQrScanningUseCase(SourceType.WALLET_CONNECT)
.getOrElse { emptyFlow() }
.map { WalletConnectAction.OpenSession(it, WalletConnectAction.OpenSession.SourceType.QR) }
.map {
WalletConnectAction.OpenSession(
wcUri = it,
source = WalletConnectAction.OpenSession.SourceType.QR,
userWalletId = params.userWalletId,
)
}
.collect { store.dispatch(it) }
}
}

View file

@ -2,7 +2,9 @@ package com.tangem.tap.features.details.ui.walletconnect.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
interface WalletConnectComponent : ComposableContentComponent {
interface Factory : ComponentFactory<Unit, WalletConnectComponent>
data class Params(val userWalletId: UserWalletId)
interface Factory : ComponentFactory<Params, WalletConnectComponent>
}

View file

@ -291,13 +291,13 @@ internal class ChildFactory @Inject constructor(
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
createComponentChild(
context = context,
params = Unit,
params = RedesignedWalletConnectComponent.Params(route.userWalletId),
componentFactory = redesignedWalletConnectComponentFactory,
)
} else {
createComponentChild(
context = context,
params = Unit,
params = WalletConnectComponent.Params(route.userWalletId),
componentFactory = walletConnectComponentFactory,
)
}

View file

@ -122,7 +122,7 @@ sealed class AppRoute(val path: String) : Route {
}
@Serializable
data object WalletConnectSessions : AppRoute(path = "/wallet_connect_sessions")
data class WalletConnectSessions(val userWalletId: UserWalletId) : AppRoute(path = "/wallet_connect_sessions")
@Serializable
data class QrScanning(val source: Source) : AppRoute(path = "/$source/qr_scanning${source.path}") {

View file

@ -41,7 +41,7 @@
},
{
"name": "NEW_ATTESTATION_ENABLED",
"version": "undefined"
"version": "5.24.0"
},
{
"name": "SEND_V2_ENABLED",
@ -51,10 +51,6 @@
"name": "WALLET_CONNECT_REDESIGN_ENABLED",
"version": "undefined"
},
{
"name": "NETWORKS_LOADING_REFACTORING_ENABLED",
"version": "5.23.0"
},
{
"name": "QUOTES_LOADING_REFACTORING_ENABLED",
"version": "5.24.0"

View file

@ -278,7 +278,7 @@
<string name="exchange_tokens_unavailable_tokens_header">Kann nicht gegen %s ausgetauscht werden</string>
<string name="express_by_provider">Bereitgestellt von</string>
<string name="express_cex_status_button_title">Status</string>
<string name="express_choose_providers_subtitle">Tangem bietet Token-Swaps über Drittanbieter gemäß den jeweiligen Bedingungen des jeweiligen Anbieters an.</string>
<string name="express_choose_providers_subtitle">Anbieter erleichtern Transaktionen</string>
<string name="express_choose_providers_title">Anbieter</string>
<string name="express_error_code">Es ist ein Fehler aufgetreten. Code: %s</string>
<string name="express_error_provider_amount_roundup">Fehler %1$s. Der ausgewählte Anbieter kann die angegebene Transaktion nicht verarbeiten. Bitte runde den Wert auf %2$s auf oder änder diesen.</string>
@ -319,11 +319,11 @@
<string name="express_exchange_status_verifying">Verifizierung erforderlich</string>
<string name="express_exchange_status_waiting_tx_hash">Wartet auf Transaktions-Hash</string>
<string name="express_exchange_token_list_subtitle">Liste aller Token, die deiner Wallet hinzugefügt wurden</string>
<string name="express_fetch_best_rates">Beste Preise werden abgerufen …</string>
<string name="express_fetch_best_rates">Aktuelle Preise werden abgerufen ...</string>
<string name="express_floating_rate">Variabler Zinssatz</string>
<string name="express_legal_one_placeholder">Durch die Nutzung der Swap-Funktion erklärst du dich mit den folgenden Bedingungen des Anbieters einverstanden %s</string>
<string name="express_legal_two_placeholders">Durch die Nutzung der Swap-Funktionalität erklärst du dich mit des Anbieters %1$s und %2$s einverstanden</string>
<string name="express_more_providers_soon">Weitere Anbieter werden bald folgen.\nBleib dabei!</string>
<string name="express_more_providers_soon">Weitere Anbieter folgen in Kürze</string>
<string name="express_provider">Anbieter</string>
<string name="express_provider_best_rate">Bester Preis</string>
<string name="express_provider_max_amount">Verfügbar bis zu %s</string>
@ -419,6 +419,7 @@
<string name="markets_add_to_my_portfolio_unavailable_description">Dieses Asset wird derzeit in der Wallet nicht unterstützt.</string>
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">Dieses Asset ist für dieses Wallet nicht verfügbar</string>
<string name="markets_add_token">Hinzufügen</string>
<string name="markets_apy_placeholder">APY %s</string>
<string name="markets_available_networks">Verfügbare Netzwerke</string>
<string name="markets_common_my_portfolio">Mein Portfolio</string>
<string name="markets_common_title">Markt</string>
@ -448,6 +449,9 @@
<string name="markets_sort_by_top_gainers_title">Top-Gewinner</string>
<string name="markets_sort_by_top_losers_title">Top-Verlierer</string>
<string name="markets_sort_by_trending_title">Beliebt</string>
<string name="markets_staking_banner_description_placeholder">Staking ist der einfachste Weg, um Belohnungen für Deine Kryptowährung zu erhalten. %s</string>
<string name="markets_staking_banner_description_show_more">Mehr anzeigen</string>
<string name="markets_staking_banner_title">Verdiene bis zu %s APY</string>
<string name="markets_token_details_about_token_title">Über %s</string>
<plurals name="markets_token_details_amount_exchanges">
<item quantity="one">%d börse</item>
@ -530,6 +534,13 @@
<string name="nft_details_base_information">Basisinformationen</string>
<string name="nft_details_chain">Netzwerk</string>
<string name="nft_details_contract_address">Vertragsadresse</string>
<string name="nft_details_info_chain">Kontrollabschnitt</string>
<string name="nft_details_info_contract_address">Kontrollabschnitt</string>
<string name="nft_details_info_rarity_label">Kontrollabschnitt</string>
<string name="nft_details_info_rarity_rank">Kontrollabschnitt</string>
<string name="nft_details_info_token_address">Kontrollabschnitt</string>
<string name="nft_details_info_token_id">Kontrollabschnitt</string>
<string name="nft_details_info_token_standard">Kontrollabschnitt</string>
<string name="nft_details_last_sale_price">Letzter Verkaufspreis</string>
<string name="nft_details_rarity_label">Raritätsetikett</string>
<string name="nft_details_rarity_rank">Seltenheitsgrad</string>
@ -1051,6 +1062,8 @@
<string name="transaction_history_transaction_validator">Validierer: %s</string>
<string name="transfer_min_amount_error">Minimum %s</string>
<string name="transfer_notification_invalid_minimum_transaction_amount_text">Der Mindesttransaktionsbetrag beträgt %1$s.</string>
<string name="tron_will_be_send_token_fee_description">Die Netzwerkgebühren für beliebte Token im Tron-Netzwerk können höher sein. Das Staking von TRX kann helfen, die Transaktionskosten zu senken.</string>
<string name="tron_will_be_send_token_fee_title">Spare bei den Tron-Netzwerkgebühren</string>
<string name="try_to_load_data_again_button_title">Versuche es erneut</string>
<string name="twin_error_same_card">Du hast dieselbe Karte oder Ring gescannt. Um ein Zwillings-Wallet zu erstellen, musst du die Karte oder Ring mit der Nummer %d scannen.</string>
<string name="twin_error_wrong_twin">Du hast die falsche Doppelkarte oder Ring gescannt. Bitte versuche eine andere Karte oder Ring</string>
@ -1237,6 +1250,7 @@
<string name="wc_alert_connection_timeout_description">Zeitüberschreitungsfehler. Bitte versuche es später erneut.</string>
<string name="wc_alert_connection_timeout_title">WalletConnect konnte nicht hergestellt werden</string>
<string name="wc_alert_domain_issues_description">Diese Domäne kann nicht verifiziert werden. Überprüfe die Anfrage sorgfältig und bestätige diese dann.</string>
<string name="wc_alert_session_disconnected_description">Geh zurück zu Deinem Browser und stell erneut eine Verbindung über WalletConnect her.</string>
<string name="wc_alert_session_disconnected_title">WalletConnect-Sitzung wurde getrennt</string>
<string name="wc_alert_unknown_error_description">Fehlercode: %s. Wenn das Problem weiterhin besteht, wende Dich bitte an unseren Support.</string>
<string name="wc_alert_unknown_error_title">Wir haben einen unbekannten Fehler festgestellt.</string>
@ -1244,14 +1258,22 @@
<string name="wc_alert_unsupported_networks_title">Nicht unterstützte Netzwerke</string>
<string name="wc_alert_verified_domain_description">Tangem unterstützt ein erforderliches Netzwerk um %s.</string>
<string name="wc_alert_verified_domain_title">Verifizierte Domain</string>
<string name="wc_alert_wrong_card_description">Falsche Karte oder falscher Ring in der Tangem-App ausgewählt</string>
<string name="wc_alert_wrong_card_title">Wir haben eine Art Problem</string>
<string name="wc_common_connect">Verbinden</string>
<string name="wc_common_network">Netzwerk</string>
<string name="wc_common_networks">Netzwerke</string>
<string name="wc_common_wallet">Wallet</string>
<string name="wc_connected_networks">Verbundene Netzwerke</string>
<string name="wc_connection_reqeust_can_view_balance">Anzeige des Kontostands und der Aktivitäten in Deiner Wallet</string>
<string name="wc_connection_reqeust_cant_sign">Signiere die Transaktionen ohne eine Vorankündigung</string>
<string name="wc_connection_reqeust_request_approval">Genehmigung für Transaktionen anfordern</string>
<string name="wc_connection_reqeust_will_not">Wird nicht in der Lage sein,</string>
<string name="wc_connection_reqeust_would_like">Möchte</string>
<string name="wc_connection_request">Verbindungsanfrage</string>
<string name="wc_connections">Verbindungen</string>
<string name="wc_contents">Inhalt</string>
<string name="wc_copy_data_button_text">Daten kopieren</string>
<string name="wc_disconnect_all">Alle trennen</string>
<string name="wc_disconnect_all_alert_desc">Text über die Trennung aller dApps</string>
<string name="wc_disconnect_all_alert_title">Alle dApps trennen</string>
@ -1262,6 +1284,11 @@
<string name="wc_no_sessions_title">Keine Sitzungen</string>
<string name="wc_notification_security_risk_subtitle">Diese Domain wird von mehreren Sicherheitsanbietern als unsicher eingestuft. Verlasse diese umgehend, um Dein Vermögen zu schützen.</string>
<string name="wc_notification_security_risk_title">Bekanntes Sicherheitsrisiko</string>
<string name="wc_request_from">Anfrage von</string>
<string name="wc_signature_type">Art der Signatur</string>
<string name="wc_transaction_request">Transaktionsanfrage</string>
<string name="wc_transaction_request_title">Transaktionsanfrage</string>
<string name="wc_wallet_connect">Wallet verbinden</string>
<string name="welcome_interrupted_backup_alert_discard">Verwerfen</string>
<string name="welcome_interrupted_backup_alert_message">Du hast eine unterbrochene Sicherung. Möchtest du diese fortsetzen?</string>
<string name="welcome_interrupted_backup_alert_resume">Ja, fortsetzen</string>

View file

@ -527,6 +527,13 @@
<string name="nft_details_base_information">基本情報</string>
<string name="nft_details_chain">チェーン</string>
<string name="nft_details_contract_address">コントラクトアドレス</string>
<string name="nft_details_info_chain">スタブ</string>
<string name="nft_details_info_contract_address">スタブ</string>
<string name="nft_details_info_rarity_label">スタブ</string>
<string name="nft_details_info_rarity_rank">スタブ</string>
<string name="nft_details_info_token_address">スタブ</string>
<string name="nft_details_info_token_id">スタブ</string>
<string name="nft_details_info_token_standard">スタブ</string>
<string name="nft_details_last_sale_price">最終販売価格</string>
<string name="nft_details_rarity_label">レアリティ・ラベル</string>
<string name="nft_details_rarity_rank">レアリティ・ランク</string>
@ -1044,7 +1051,7 @@
<string name="transaction_history_transaction_validator">バリデーター: %s</string>
<string name="transfer_min_amount_error">最小%s</string>
<string name="transfer_notification_invalid_minimum_transaction_amount_text">最小取引金額は%1$sです。</string>
<string name="tron_will_be_send_token_fee_description">Tronネットワークの人気トークンの手数料は高めです。TRXをステーキングすると、より安く、あるいは無料で取引できます。</string>
<string name="tron_will_be_send_token_fee_description">Tronネットワークの人気トークンの手数料は高い可能性があります。TRXをステーキングすると、取引コストを削減できる可能性があります。</string>
<string name="tron_will_be_send_token_fee_title">Tronネットワーク手数料を節約</string>
<string name="try_to_load_data_again_button_title">もう一度やり直してください</string>
<string name="twin_error_same_card">同じカードをスキャンしました。ツインウォレットを作成するには、番号%dのカードをスキャンする必要があります。</string>
@ -1237,6 +1244,7 @@
<string name="wc_alert_verified_domain_title">検証済みドメイン</string>
<string name="wc_alert_wrong_card_description">Tangemアプリで誤ったカードまたはリングが選択されました</string>
<string name="wc_alert_wrong_card_title">問題が起きています</string>
<string name="wc_common_address">アドレス</string>
<string name="wc_common_connect">接続する</string>
<string name="wc_common_network">ネットワーク</string>
<string name="wc_common_networks">ネットワーク</string>
@ -1249,6 +1257,8 @@
<string name="wc_connection_reqeust_would_like">したい</string>
<string name="wc_connection_request">接続リクエスト</string>
<string name="wc_connections">接続</string>
<string name="wc_contents">内容</string>
<string name="wc_copy_data_button_text">データをコピー</string>
<string name="wc_disconnect_all">すべての接続を解除する</string>
<string name="wc_disconnect_all_alert_desc">すべてのdAppsの接続解除に関するテキスト</string>
<string name="wc_disconnect_all_alert_title">すべてのdAppを接続解除する</string>
@ -1259,6 +1269,11 @@
<string name="wc_no_sessions_title">セッションなし</string>
<string name="wc_notification_security_risk_subtitle">このドメインは複数のセキュリティプロバイダーから安全でないとの警告を受けています。あなたの資産を守るため、直ちにアクセスを中止してください。</string>
<string name="wc_notification_security_risk_title">既知のセキュリティリスク</string>
<string name="wc_request_from">リクエスト元</string>
<string name="wc_signature_type">署名タイプ</string>
<string name="wc_transaction_info_to_title">宛先</string>
<string name="wc_transaction_request">取引リクエスト</string>
<string name="wc_transaction_request_title">取引リクエスト</string>
<string name="wc_wallet_connect">ウォレットコネクト</string>
<string name="welcome_interrupted_backup_alert_discard">破棄</string>
<string name="welcome_interrupted_backup_alert_message">バックアップが中断されました。再開しますか?</string>

View file

@ -1305,6 +1305,7 @@
<string name="wc_alert_verified_domain_title">Verified domain</string>
<string name="wc_alert_wrong_card_description">Wrong card or ring selected in Tangem App</string>
<string name="wc_alert_wrong_card_title">We\'ve got some kind of problem</string>
<string name="wc_common_address">Address</string>
<string name="wc_common_connect">Connect</string>
<string name="wc_common_network">Network</string>
<string name="wc_common_networks">Networks</string>
@ -1331,6 +1332,7 @@
<string name="wc_notification_security_risk_title">Known security risk</string>
<string name="wc_request_from">Request from</string>
<string name="wc_signature_type">Signature Type</string>
<string name="wc_transaction_info_to_title">To</string>
<string name="wc_transaction_request">Transaction request</string>
<string name="wc_transaction_request_title">Transaction request</string>
<string name="wc_wallet_connect">Wallet connect</string>

View file

@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M9,0.547C8.407,0.547 7.867,0.701 7.283,0.956C6.721,1.201 6.073,1.562 5.271,2.008L2.073,3.788C1.611,4.045 1.214,4.374 0.939,4.825C0.662,5.278 0.547,5.793 0.547,6.357L0.547,6.411C0.546,6.602 0.546,6.825 0.6,7.027C0.733,7.53 1.055,7.86 1.448,8.035C1.663,8.13 1.887,8.175 2.088,8.194V13.66C1.167,14.188 0.547,15.181 0.547,16.318C0.547,16.945 1.055,17.453 1.682,17.453H8.23C8.644,17.453 8.98,17.117 8.98,16.703C8.98,16.289 8.644,15.953 8.23,15.953H2.09C2.254,15.267 2.872,14.757 3.608,14.757H8.23C8.644,14.757 8.98,14.421 8.98,14.007C8.98,13.593 8.644,13.257 8.23,13.257H6.669V8.21H15.572C15.834,8.21 16.205,8.19 16.553,8.035C16.946,7.86 17.268,7.53 17.401,7.027C17.455,6.825 17.454,6.602 17.454,6.411L17.454,6.357C17.454,5.793 17.339,5.278 17.062,4.825C16.787,4.374 16.39,4.045 15.928,3.788L12.73,2.008C11.928,1.562 11.28,1.201 10.718,0.956C10.134,0.701 9.594,0.547 9,0.547ZM5.968,3.337C6.81,2.868 7.395,2.543 7.883,2.33C8.356,2.124 8.685,2.047 9,2.047C9.316,2.047 9.645,2.124 10.118,2.33C10.606,2.543 11.191,2.868 12.033,3.337L15.198,5.098C15.506,5.269 15.679,5.437 15.782,5.606C15.883,5.772 15.954,6 15.954,6.357C15.954,6.489 15.954,6.563 15.951,6.617C15.95,6.635 15.95,6.646 15.949,6.652C15.948,6.657 15.946,6.66 15.945,6.663L15.942,6.664C15.923,6.673 15.884,6.686 15.817,6.695C15.75,6.705 15.67,6.71 15.572,6.71H2.429C2.331,6.71 2.251,6.705 2.184,6.695C2.116,6.686 2.078,6.673 2.059,6.664L2.056,6.663C2.055,6.66 2.053,6.657 2.052,6.652C2.051,6.646 2.051,6.635 2.05,6.617C2.047,6.563 2.047,6.489 2.047,6.357C2.047,6 2.118,5.772 2.219,5.606C2.322,5.437 2.495,5.269 2.802,5.098L5.968,3.337ZM3.608,13.257L3.588,13.257V8.21H5.169V13.257H3.608Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M10.396,15.788C10.103,16.081 10.103,16.556 10.396,16.849C10.689,17.142 11.164,17.142 11.457,16.849L16.849,11.456C17.142,11.163 17.142,10.689 16.849,10.396C16.556,10.103 16.081,10.103 15.788,10.396L10.396,15.788Z"
android:fillColor="#000000"/>
<path
android:pathData="M10.176,11.503C10.176,10.77 10.77,10.176 11.504,10.176C12.237,10.176 12.832,10.77 12.832,11.503C12.832,12.237 12.237,12.831 11.504,12.831C10.77,12.831 10.176,12.237 10.176,11.503ZM11.504,11.676C11.599,11.676 11.676,11.599 11.676,11.503C11.676,11.408 11.599,11.331 11.504,11.331C11.409,11.331 11.332,11.408 11.332,11.503C11.332,11.599 11.409,11.676 11.504,11.676Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M15.741,14.412C15.008,14.412 14.413,15.007 14.413,15.74C14.413,16.473 15.008,17.068 15.741,17.068C16.474,17.068 17.069,16.473 17.069,15.74C17.069,15.007 16.474,14.412 15.741,14.412ZM15.913,15.74C15.913,15.835 15.836,15.912 15.741,15.912C15.646,15.912 15.569,15.835 15.569,15.74C15.569,15.645 15.646,15.568 15.741,15.568C15.836,15.568 15.913,15.645 15.913,15.74Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M9.992,4.701C8.321,4.701 6.964,6.054 6.964,7.726C6.964,9.397 8.321,10.75 9.992,10.75C11.663,10.75 13.02,9.397 13.02,7.726C13.02,6.054 11.663,4.701 9.992,4.701ZM8.464,7.726C8.464,6.885 9.147,6.201 9.992,6.201C10.837,6.201 11.52,6.885 11.52,7.726C11.52,8.567 10.837,9.25 9.992,9.25C9.147,9.25 8.464,8.567 8.464,7.726Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M6.448,15.067C8.288,13.14 11.709,13.068 13.542,15.058C13.823,15.362 14.298,15.382 14.602,15.101C14.907,14.82 14.926,14.346 14.645,14.041C12.191,11.377 7.766,11.515 5.363,14.031C5.077,14.331 5.088,14.806 5.387,15.092C5.687,15.378 6.162,15.367 6.448,15.067Z"
android:fillColor="#000000"/>
<path
android:pathData="M10.056,0.606H9.942C7.953,0.606 6.384,0.606 5.157,0.771C3.897,0.941 2.886,1.296 2.091,2.092C1.295,2.887 0.94,3.898 0.77,5.158C0.605,6.385 0.605,7.954 0.605,9.943V10.057C0.605,12.046 0.605,13.615 0.77,14.842C0.94,16.102 1.295,17.113 2.091,17.908C2.886,18.704 3.897,19.059 5.157,19.229C6.384,19.394 7.953,19.394 9.942,19.394H10.056C12.045,19.394 13.614,19.394 14.841,19.229C16.101,19.059 17.112,18.704 17.907,17.908C18.703,17.113 19.058,16.102 19.228,14.842C19.393,13.615 19.393,12.046 19.393,10.057V9.943C19.393,7.954 19.393,6.385 19.228,5.158C19.058,3.898 18.703,2.887 17.907,2.092C17.112,1.296 16.101,0.941 14.841,0.771C13.614,0.606 12.045,0.606 10.056,0.606ZM3.152,3.153C3.622,2.682 4.263,2.405 5.357,2.258C6.472,2.108 7.941,2.106 9.999,2.106C12.057,2.106 13.526,2.108 14.641,2.258C15.735,2.405 16.376,2.682 16.846,3.153C17.317,3.623 17.594,4.264 17.741,5.358C17.891,6.473 17.893,7.941 17.893,10C17.893,12.059 17.891,13.527 17.741,14.642C17.594,15.736 17.317,16.377 16.846,16.847C16.376,17.318 15.735,17.595 14.641,17.742C13.526,17.892 12.057,17.894 9.999,17.894C7.941,17.894 6.472,17.892 5.357,17.742C4.263,17.595 3.622,17.318 3.152,16.847C2.681,16.377 2.404,15.736 2.257,14.642C2.107,13.527 2.105,12.059 2.105,10C2.105,7.941 2.107,6.473 2.257,5.358C2.404,4.264 2.681,3.623 3.152,3.153Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View file

@ -42,6 +42,7 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
@Suppress("TooManyFunctions")
internal object WalletConnectDataModule {
@Provides
@ -86,6 +87,7 @@ internal object WalletConnectDataModule {
dispatchers: CoroutineDispatcherProvider,
legacyStore: WalletConnectSessionsRepository,
getWallets: GetWalletsUseCase,
associateNetworks: AssociateNetworksDelegate,
): DefaultWcSessionsManager {
val scope = CoroutineScope(SupervisorJob() + dispatchers.io)
return DefaultWcSessionsManager(
@ -93,6 +95,7 @@ internal object WalletConnectDataModule {
dispatchers = dispatchers,
legacyStore = legacyStore,
getWallets = getWallets,
associateNetworks = associateNetworks,
scope = scope,
)
}
@ -121,12 +124,12 @@ internal object WalletConnectDataModule {
@Singleton
fun wcEthNetwork(
@SdkMoshi moshi: Moshi,
excludedBlockchains: ExcludedBlockchains,
sessionsManager: WcSessionsManager,
factories: WcEthNetwork.Factories,
namespaceConverter: WcEthNetwork.NamespaceConverter,
): WcEthNetwork = WcEthNetwork(
moshi = moshi,
excludedBlockchains = excludedBlockchains,
namespaceConverter = namespaceConverter,
sessionsManager = sessionsManager,
factories = factories,
)
@ -135,7 +138,7 @@ internal object WalletConnectDataModule {
@Singleton
fun wcSolanaNetwork(
@SdkMoshi moshi: Moshi,
excludedBlockchains: ExcludedBlockchains,
namespaceConverter: WcSolanaNetwork.NamespaceConverter,
sessionsManager: WcSessionsManager,
factories: WcSolanaNetwork.Factories,
walletManager: UserWalletManager,
@ -143,28 +146,28 @@ internal object WalletConnectDataModule {
moshi = moshi,
sessionsManager = sessionsManager,
factories = factories,
excludedBlockchains = excludedBlockchains,
namespaceConverter = namespaceConverter,
walletManager = walletManager,
)
@Provides
@Singleton
fun caipNamespaceDelegate(
diHelperBox: DiHelperBox,
namespaceConverters: Set<@JvmSuppressWildcards WcNamespaceConverter>,
walletManagersFacade: WalletManagersFacade,
): CaipNamespaceDelegate = CaipNamespaceDelegate(
namespaceConverters = diHelperBox.converters,
namespaceConverters = namespaceConverters,
walletManagersFacade = walletManagersFacade,
)
@Provides
@Singleton
fun associateNetworksDelegate(
diHelperBox: DiHelperBox,
namespaceConverters: Set<@JvmSuppressWildcards WcNamespaceConverter>,
getWallets: GetWalletsUseCase,
currenciesRepository: CurrenciesRepository,
): AssociateNetworksDelegate = AssociateNetworksDelegate(
namespaceConverters = diHelperBox.converters,
namespaceConverters = namespaceConverters,
getWallets = getWallets,
currenciesRepository = currenciesRepository,
)
@ -176,10 +179,16 @@ internal object WalletConnectDataModule {
ethNetwork,
solanaNetwork,
),
converters = setOf(
ethNetwork,
solanaNetwork,
),
)
@Provides
@Singleton
fun namespaceConverters(
ethNamespaceConverter: WcEthNetwork.NamespaceConverter,
solanaNamespaceConverter: WcSolanaNetwork.NamespaceConverter,
): Set<@JvmSuppressWildcards WcNamespaceConverter> = setOf(
ethNamespaceConverter,
solanaNamespaceConverter,
)
@Provides
@ -188,6 +197,20 @@ internal object WalletConnectDataModule {
return DefaultWcRequestUseCaseFactory(diHelperBox.handlers)
}
@Provides
@Singleton
fun wcEthNetworkNamespaceConverter(excludedBlockchains: ExcludedBlockchains): WcEthNetwork.NamespaceConverter {
return WcEthNetwork.NamespaceConverter(excludedBlockchains)
}
@Provides
@Singleton
fun wcSolanaNetworkNamespaceConverter(
excludedBlockchains: ExcludedBlockchains,
): WcSolanaNetwork.NamespaceConverter {
return WcSolanaNetwork.NamespaceConverter(excludedBlockchains)
}
@Provides
@Singleton
fun providesWcDisconnectUseCase(sessionsManager: WcSessionsManager): WcDisconnectUseCase {
@ -195,7 +218,6 @@ internal object WalletConnectDataModule {
}
internal class DiHelperBox(
val converters: Set<WcNamespaceConverter>,
val handlers: Set<WcRequestToUseCaseConverter>,
)
}

View file

@ -22,12 +22,10 @@ import jakarta.inject.Inject
internal class WcEthNetwork(
private val moshi: Moshi,
private val excludedBlockchains: ExcludedBlockchains,
private val sessionsManager: WcSessionsManager,
private val factories: Factories,
) : WcRequestToUseCaseConverter, WcNamespaceConverter {
override val namespaceKey: NamespaceKey = NamespaceKey("eip155")
private val namespaceConverter: NamespaceConverter,
) : WcRequestToUseCaseConverter {
override fun toWcMethodName(request: WcSdkSessionRequest): WcEthMethodName? {
val methodKey = request.request.method
@ -39,7 +37,7 @@ internal class WcEthNetwork(
val name = toWcMethodName(request) ?: return null
val method: WcEthMethod = name.toMethod(request) ?: return null
val session = sessionsManager.findSessionByTopic(request.topic) ?: return null
val network = toNetwork(request.chainId.orEmpty(), session.wallet) ?: return null
val network = namespaceConverter.toNetwork(request.chainId.orEmpty(), session.wallet) ?: return null
val accountAddress = when (method) {
is WcEthMethod.MessageSign -> method.account
is WcEthMethod.SendTransaction -> method.transaction.from
@ -100,24 +98,31 @@ internal class WcEthNetwork(
return WcEthMethod.SignTypedData(params = params, account = account, dataForSign = data)
}
override fun toNetwork(chainId: String, wallet: UserWallet): Network? {
return toNetwork(chainId, wallet, excludedBlockchains)
}
internal class NamespaceConverter constructor(
private val excludedBlockchains: ExcludedBlockchains,
) : WcNamespaceConverter {
override fun toBlockchain(chainId: CAIP2): Blockchain? {
if (chainId.namespace != namespaceKey.key) return null
val ethChainId = chainId.reference.toIntOrNull() ?: return null
return Blockchain.fromChainId(ethChainId)
}
override val namespaceKey: NamespaceKey = NamespaceKey("eip155")
override fun toCAIP2(network: Network): CAIP2? {
val blockchain = Blockchain.fromId(network.id.value)
if (!blockchain.isEvm()) return null
val chainId = blockchain.getChainId() ?: return null
return CAIP2(
namespace = namespaceKey.key,
reference = chainId.toString(),
)
override fun toNetwork(chainId: String, wallet: UserWallet): Network? {
return toNetwork(chainId, wallet, excludedBlockchains)
}
override fun toBlockchain(chainId: CAIP2): Blockchain? {
if (chainId.namespace != namespaceKey.key) return null
val ethChainId = chainId.reference.toIntOrNull() ?: return null
return Blockchain.fromChainId(ethChainId)
}
override fun toCAIP2(network: Network): CAIP2? {
val blockchain = Blockchain.fromId(network.id.value)
if (!blockchain.isEvm()) return null
val chainId = blockchain.getChainId() ?: return null
return CAIP2(
namespace = namespaceKey.key,
reference = chainId.toString(),
)
}
}
internal class Factories @Inject constructor(

View file

@ -26,11 +26,9 @@ internal class WcSolanaNetwork(
private val moshi: Moshi,
private val sessionsManager: WcSessionsManager,
private val factories: Factories,
private val excludedBlockchains: ExcludedBlockchains,
private val namespaceConverter: NamespaceConverter,
private val walletManager: UserWalletManager,
) : WcNamespaceConverter, WcRequestToUseCaseConverter {
override val namespaceKey: NamespaceKey = NamespaceKey("solana")
) : WcRequestToUseCaseConverter {
override fun toWcMethodName(request: WcSdkSessionRequest): WcSolanaMethodName? {
val methodKey = request.request.method
@ -42,7 +40,7 @@ internal class WcSolanaNetwork(
val name = toWcMethodName(request) ?: return null
val method: WcSolanaMethod = name.toMethod(request) ?: return null
val session = sessionsManager.findSessionByTopic(request.topic) ?: return null
val network = toNetwork(request.chainId.orEmpty(), session.wallet) ?: return null
val network = namespaceConverter.toNetwork(request.chainId.orEmpty(), session.wallet) ?: return null
val accountAddress = getAccountAddress(network)
val context = WcMethodUseCaseContext(
session = session,
@ -66,31 +64,38 @@ internal class WcSolanaNetwork(
}
}
override fun toBlockchain(chainId: CAIP2): Blockchain? {
if (chainId.namespace != namespaceKey.key) return null
return when (chainId.reference) {
MAINNET_CHAIN_ID -> Blockchain.Solana
TESTNET_CHAIN_ID -> Blockchain.SolanaTestnet
else -> null
}
}
internal class NamespaceConverter @Inject constructor(
private val excludedBlockchains: ExcludedBlockchains,
) : WcNamespaceConverter {
override fun toNetwork(chainId: String, wallet: UserWallet): Network? {
return toNetwork(chainId, wallet, excludedBlockchains)
}
override val namespaceKey: NamespaceKey = NamespaceKey("solana")
override fun toCAIP2(network: Network): CAIP2? {
val blockchain = Blockchain.fromId(network.id.value)
val chainId = when (blockchain) {
Blockchain.Solana -> MAINNET_CHAIN_ID
Blockchain.SolanaTestnet -> TESTNET_CHAIN_ID
else -> null
override fun toBlockchain(chainId: CAIP2): Blockchain? {
if (chainId.namespace != namespaceKey.key) return null
return when (chainId.reference) {
MAINNET_CHAIN_ID -> Blockchain.Solana
TESTNET_CHAIN_ID -> Blockchain.SolanaTestnet
else -> null
}
}
override fun toNetwork(chainId: String, wallet: UserWallet): Network? {
return toNetwork(chainId, wallet, excludedBlockchains)
}
override fun toCAIP2(network: Network): CAIP2? {
val blockchain = Blockchain.fromId(network.id.value)
val chainId = when (blockchain) {
Blockchain.Solana -> MAINNET_CHAIN_ID
Blockchain.SolanaTestnet -> TESTNET_CHAIN_ID
else -> null
}
chainId ?: return null
return CAIP2(
namespace = namespaceKey.key,
reference = chainId,
)
}
chainId ?: return null
return CAIP2(
namespace = namespaceKey.key,
reference = chainId,
)
}
private fun WcSolanaMethodName.toMethod(request: WcSdkSessionRequest): WcSolanaMethod? {

View file

@ -1,6 +1,7 @@
package com.tangem.data.walletconnect.pair
import com.reown.walletkit.client.Wallet
import com.reown.walletkit.client.Wallet.Model.Namespace
import com.tangem.data.walletconnect.utils.WcNamespaceConverter
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
@ -16,6 +17,16 @@ internal class AssociateNetworksDelegate constructor(
private val currenciesRepository: CurrenciesRepository,
) {
suspend fun associate(wallet: UserWallet, namespaces: Map<String, Namespace.Session>): Set<Network> {
val walletNetworks = getWalletNetworks(wallet)
val namespacesSet = namespaces.values.flatMap { proposal -> proposal.chains ?: listOf() }.toSet()
return namespacesSet.mapNotNullTo(mutableSetOf()) { chainId ->
val wcNetwork = namespaceConverters
.firstNotNullOfOrNull { it.toNetwork(chainId, wallet) } ?: return@mapNotNullTo null
walletNetworks.find { network -> wcNetwork.id == network.id }
}
}
@Throws(WcPairError.UnsupportedNetworks::class)
suspend fun associate(sessionProposal: Wallet.Model.SessionProposal): Map<UserWallet, ProposalNetwork> {
val userWallets = getWallets.invokeSync().filter { it.isMultiCurrency }
@ -31,9 +42,7 @@ internal class AssociateNetworksDelegate constructor(
requiredNamespaces: Set<String>,
optionalNamespaces: Set<String>,
): ProposalNetwork {
val walletNetworks = currenciesRepository.getMultiCurrencyWalletCurrenciesSync(wallet.walletId)
.filterIsInstance<CryptoCurrency.Coin>()
.map { it.network }
val walletNetworks = getWalletNetworks(wallet)
val unknownRequired = mutableSetOf<String>()
val missingRequired = mutableSetOf<Network>()
@ -74,6 +83,11 @@ internal class AssociateNetworksDelegate constructor(
)
}
private suspend fun getWalletNetworks(wallet: UserWallet): List<Network> =
currenciesRepository.getMultiCurrencyWalletCurrenciesSync(wallet.walletId)
.filterIsInstance<CryptoCurrency.Coin>()
.map { it.network }
private fun Map<String, Wallet.Model.Namespace.Proposal>.setOfChainId(): Set<String> =
this.values.flatMap { proposal -> proposal.chains ?: listOf() }.toSet()

View file

@ -14,7 +14,6 @@ import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData
import com.tangem.domain.walletconnect.repository.WcSessionsManager
import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.domain.wallets.models.UserWallet
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -78,9 +77,11 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
sessionForApprove = sessionForApprove,
sdkSessionProposal = sdkSessionProposal,
).map { settledSession ->
val newSession = settledSession.session.toDomain(
val newSession = WcSession(
wallet = sessionForApprove.wallet,
sdkModel = WcSdkSessionConverter.convert(settledSession.session),
securityStatus = proposalState.dAppSession.securityStatus,
networks = sessionForApprove.network.toSet(),
)
sessionsManager.saveSession(newSession)
newSession
@ -141,14 +142,6 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
}
},)
private fun Wallet.Model.Session.toDomain(wallet: UserWallet, securityStatus: CheckDAppResult): WcSession {
return WcSession(
wallet = wallet,
sdkModel = WcSdkSessionConverter.convert(this),
securityStatus = securityStatus,
)
}
private sealed interface TerminalAction {
data class Approve(val sessionForApprove: WcSessionApprove) : TerminalAction
data object Reject : TerminalAction

View file

@ -6,6 +6,7 @@ import arrow.core.right
import com.domain.blockaid.models.dapp.CheckDAppResult
import com.reown.walletkit.client.Wallet
import com.reown.walletkit.client.WalletKit
import com.tangem.data.walletconnect.pair.AssociateNetworksDelegate
import com.tangem.data.walletconnect.utils.WcSdkObserver
import com.tangem.data.walletconnect.utils.WcSdkSessionConverter
import com.tangem.datasource.local.walletconnect.WalletConnectStore
@ -29,6 +30,7 @@ internal class DefaultWcSessionsManager(
private val legacyStore: WalletConnectSessionsRepository,
private val getWallets: GetWalletsUseCase,
private val dispatchers: CoroutineDispatcherProvider,
private val associateNetworks: AssociateNetworksDelegate,
private val scope: CoroutineScope,
) : WcSessionsManager, WcSdkObserver {
@ -76,10 +78,12 @@ internal class DefaultWcSessionsManager(
?: return@withContext null
val sdkSession = WalletKit.getActiveSessionByTopic(topic) ?: return@withContext null
val wallet = storedSession.wallet
val networks = associateNetworks.associate(wallet, sdkSession.namespaces)
WcSession(
wallet = wallet,
sdkModel = WcSdkSessionConverter.convert(sdkSession),
securityStatus = storedSession.securityStatus,
networks = networks,
)
}
@ -114,7 +118,7 @@ internal class DefaultWcSessionsManager(
return mustSaveInNewStore.isNotEmpty()
}
private fun associate(
private suspend fun associate(
inSdk: List<Wallet.Model.Session>,
inStore: Set<WcSessionDTO>,
wallets: List<UserWallet>,
@ -122,7 +126,13 @@ internal class DefaultWcSessionsManager(
val wcSessions = inStore.mapNotNull { session ->
val wallet = wallets.find { it.walletId == session.walletId } ?: return@mapNotNull null
val sdkSession = inSdk.find { it.topic == session.topic } ?: return@mapNotNull null
WcSession(wallet = wallet, sdkModel = WcSdkSessionConverter.convert(sdkSession), session.securityStatus)
val networks = associateNetworks.associate(wallet, sdkSession.namespaces)
WcSession(
wallet = wallet,
sdkModel = WcSdkSessionConverter.convert(sdkSession),
securityStatus = session.securityStatus,
networks = networks,
)
}
return wcSessions
}

View file

@ -20,6 +20,7 @@ import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.walletconnect.repository.WcSessionsManager
import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import com.tangem.domain.wallets.models.UserWalletId
import io.mockk.coEvery
import io.mockk.coVerifyOrder
import io.mockk.mockk
@ -91,6 +92,7 @@ internal class DefaultWcPairUseCaseTest {
wallet = sessionForApprove.wallet,
sdkModel = WcSdkSessionConverter.convert(this),
securityStatus = CheckDAppResult.SAFE,
networks = setOf(),
)
private fun useCaseFactory() = DefaultWcPairUseCase(
@ -99,7 +101,7 @@ internal class DefaultWcPairUseCaseTest {
caipNamespaceDelegate = caipNamespaceDelegate,
sdkDelegate = sdkDelegate,
blockAidVerifier = blockAidVerifier,
pairRequest = WcPairRequest(url, source),
pairRequest = WcPairRequest(userWalletId = UserWalletId(""), uri = url, source = source),
)
@Before

View file

@ -14,7 +14,6 @@ import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
@ -24,7 +23,6 @@ class SaveManagedTokensUseCase(
private val customTokensRepository: CustomTokensRepository,
private val walletManagersFacade: WalletManagersFacade,
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val derivationsRepository: DerivationsRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
@ -102,20 +100,12 @@ class SaveManagedTokensUseCase(
val networkToUpdate = currenciesToAdd.map { it.network }
.subtract(existingCurrencies.map { it.network }.toSet())
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(
userWalletId = userWalletId,
networks = networksToUpdate + networkToUpdate,
),
)
} else {
networksRepository.getNetworkStatusesSync(
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(
userWalletId = userWalletId,
networks = networksToUpdate + networkToUpdate,
refresh = true,
)
}
),
)
}
private suspend fun refreshUpdatedYieldBalances(

View file

@ -12,7 +12,6 @@ import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
@ -30,7 +29,6 @@ class SaveMarketTokensUseCase(
private val derivationsRepository: DerivationsRepository,
private val marketsTokenRepository: MarketsTokenRepository,
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -83,20 +81,12 @@ class SaveMarketTokensUseCase(
}
private suspend fun refreshUpdatedNetworks(userWalletId: UserWalletId, addedCurrencies: List<CryptoCurrency>) {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(
userWalletId = userWalletId,
networks = addedCurrencies.map(CryptoCurrency::network).toSet(),
),
)
} else {
networksRepository.getNetworkStatusesSync(
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(
userWalletId = userWalletId,
networks = addedCurrencies.map(CryptoCurrency::network).toSet(),
refresh = true,
)
}
),
)
}
private suspend fun refreshUpdatedYieldBalances(

View file

@ -8,9 +8,9 @@ import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.EncryptionMode
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.common.card.FirmwareVersion as SdkFirmwareVersion
import com.tangem.operations.attestation.Attestation
import java.util.Date
import com.tangem.common.card.FirmwareVersion as SdkFirmwareVersion
// TODO: Move to :domain:card:models
/**
@ -189,6 +189,8 @@ data class CardDTO(
minor != other.minor -> minor.compareTo(other.minor)
else -> patch.compareTo(other.patch)
}
fun toSdkFirmwareVersion() = SdkFirmwareVersion(major = major, minor = minor)
}
@JsonClass(generateAdapter = true)

View file

@ -12,7 +12,6 @@ import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
@ -29,7 +28,6 @@ import kotlinx.coroutines.coroutineScope
@Suppress("LongParameterList")
class AddCryptoCurrenciesUseCase(
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -139,26 +137,13 @@ class AddCryptoCurrenciesUseCase(
}
?.network
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(
userWalletId = userWalletId,
networks = setOfNotNull(networksToUpdate, networkToUpdate),
),
)
} else {
catch(
{
networksRepository.getNetworkStatusesSync(
userWalletId = userWalletId,
networks = setOfNotNull(networksToUpdate, networkToUpdate),
refresh = true,
)
},
) {
raise(it)
}
}
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(
userWalletId = userWalletId,
networks = setOfNotNull(networksToUpdate, networkToUpdate),
),
)
.bind()
}
private suspend fun refreshUpdatedYieldBalances(userWalletId: UserWalletId, addedCurrency: CryptoCurrency) {

View file

@ -13,7 +13,6 @@ import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
@ -23,7 +22,6 @@ import kotlinx.coroutines.coroutineScope
@Suppress("LongParameterList")
class FetchCardTokenListUseCase(
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -41,7 +39,6 @@ class FetchCardTokenListUseCase(
fetchNetworksStatuses(
userWalletId = userWalletId,
networks = currencies.mapTo(destination = hashSetOf(), transform = CryptoCurrency::network),
refresh = refresh,
)
}
val fetchQuotes = async {
@ -80,19 +77,12 @@ class FetchCardTokenListUseCase(
private suspend fun Raise<TokenListError>.fetchNetworksStatuses(
userWalletId: UserWalletId,
networks: Set<Network>,
refresh: Boolean,
) {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks),
)
.mapLeft { TokenListError.DataError(it) }
} else {
catch(
block = { networksRepository.getNetworkStatusesSync(userWalletId, networks, refresh) },
catch = { raise(TokenListError.DataError(it)) },
)
}
multiNetworkStatusFetcher(
MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks),
)
.mapLeft(TokenListError::DataError)
.bind()
}
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>, refresh: Boolean) {

View file

@ -13,7 +13,6 @@ import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
@ -26,14 +25,12 @@ import kotlinx.coroutines.coroutineScope
* by providing a specific currency ID or fetching the status of the primary currency.
*
* @param currenciesRepository The repository for retrieving currency-related data.
* @param networksRepository The repository for retrieving network-related data.
* @param quotesRepository The repository for retrieving cryptocurrency quotes.
*/
// TODO: Add tests
@Suppress("LongParameterList")
class FetchCurrencyStatusUseCase(
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
@ -86,7 +83,7 @@ class FetchCurrencyStatusUseCase(
refresh: Boolean,
) = coroutineScope {
val fetchStatus = async {
fetchNetworkStatus(userWalletId, currency.network, refresh)
fetchNetworkStatus(userWalletId, currency.network)
}
val fetchQuote = async {
fetchQuote(currency.id, refresh)
@ -120,23 +117,12 @@ class FetchCurrencyStatusUseCase(
}
}
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(
userWalletId: UserWalletId,
network: Network,
refresh: Boolean,
) {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = network),
)
.mapLeft { CurrencyStatusError.DataError(it) }
} else {
catch(
block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(network), refresh) },
) {
raise(CurrencyStatusError.DataError(it))
}
}
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = network),
)
.mapLeft { CurrencyStatusError.DataError(it) }
.bind()
}
private suspend fun Raise<CurrencyStatusError>.fetchQuote(currencyId: CryptoCurrency.ID, refresh: Boolean) {

View file

@ -15,7 +15,6 @@ import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
@ -27,7 +26,6 @@ import kotlinx.coroutines.coroutineScope
* network statuses, and quotes for tokens associated with a user's wallet.
*
* @param currenciesRepository The repository for retrieving currency-related data.
* @param networksRepository The repository for retrieving network-related data.
* @param quotesRepository The repository for retrieving cryptocurrency quotes.
* @param stakingRepository The repository for retrieving staking-related data.
*/
@ -35,7 +33,6 @@ import kotlinx.coroutines.coroutineScope
@Suppress("LongParameterList")
class FetchTokenListUseCase(
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -71,7 +68,6 @@ class FetchTokenListUseCase(
fetchNetworksStatuses(
userWalletId = userWalletId,
networks = currencies.mapTo(hashSetOf()) { it.network },
refresh = mode.refreshNetworksStatuses,
)
}
val fetchQuotes = async {
@ -111,23 +107,12 @@ class FetchTokenListUseCase(
private suspend fun Raise<TokenListError>.fetchNetworksStatuses(
userWalletId: UserWalletId,
networks: Set<Network>,
refresh: Boolean,
) {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
if (refresh) {
multiNetworkStatusFetcher(
params = MultiNetworkStatusFetcher.Params(userWalletId, networks),
)
.mapLeft { TokenListError.DataError(it) }
.bind()
}
} else {
catch(
block = { networksRepository.getNetworkStatusesSync(userWalletId, networks, refresh) },
) {
raise(TokenListError.DataError(it))
}
}
multiNetworkStatusFetcher(
params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks),
)
.mapLeft(TokenListError::DataError)
.bind()
}
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean) {
@ -175,25 +160,21 @@ class FetchTokenListUseCase(
*/
enum class RefreshMode(
internal val refreshCurrencies: Boolean,
internal val refreshNetworksStatuses: Boolean,
internal val refreshQuotes: Boolean,
internal val refreshYieldBalances: Boolean,
) {
NONE(
refreshCurrencies = false,
refreshNetworksStatuses = false,
refreshQuotes = false,
refreshYieldBalances = false,
),
FULL(
refreshCurrencies = true,
refreshNetworksStatuses = true,
refreshQuotes = true,
refreshYieldBalances = true,
),
SKIP_CURRENCIES(
refreshCurrencies = false,
refreshNetworksStatuses = true,
refreshQuotes = true,
refreshYieldBalances = true,
),

View file

@ -1,30 +0,0 @@
package com.tangem.domain.tokens
import arrow.core.Either
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.wallets.models.UserWalletId
class GetCryptoCurrencyStatusSyncUseCase(
private val currencyStatusOperations: BaseCurrencyStatusOperations,
) {
// multi-currency
suspend operator fun invoke(
userWalletId: UserWalletId,
cryptoCurrencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean = false,
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return currencyStatusOperations.getCurrencyStatusSync(userWalletId, cryptoCurrencyId, isSingleWalletWithTokens)
.mapLeft { error -> error.mapToCurrencyError() }
}
// single-currency
suspend operator fun invoke(userWalletId: UserWalletId): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return currencyStatusOperations.getPrimaryCurrencyStatusSync(userWalletId)
.mapLeft { error -> error.mapToCurrencyError() }
}
}

View file

@ -1,62 +0,0 @@
package com.tangem.domain.tokens
import arrow.core.Either
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
/**
* Use case for fetching the status of a cryptocurrency associated with a user wallet.
*
*/
class GetCurrencyStatusUpdatesUseCase(
private val currencyStatusOperations: BaseCurrencyStatusOperations,
private val dispatchers: CoroutineDispatcherProvider,
) {
/**
* Invokes the use case.
*
* @param userWalletId The unique identifier of the user's wallet.
* @param currencyId The unique identifier of the cryptocurrency.
* @param isSingleWalletWithTokens Indicates whether the user wallet contains only one token on card (old cards)
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
operator fun invoke(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(
getCurrencyStatus(
userWalletId = userWalletId,
currencyId = currencyId,
isSingleWalletWithTokens = isSingleWalletWithTokens,
),
)
}.flowOn(dispatchers.io)
}
private suspend fun getCurrencyStatus(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
val currencyFlow = currencyStatusOperations.getCurrencyStatusFlow(
userWalletId = userWalletId,
currencyId = currencyId,
isSingleWalletWithTokens = isSingleWalletWithTokens,
)
return currencyFlow.map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
}
}
}

View file

@ -1,17 +1,25 @@
package com.tangem.domain.tokens
import arrow.core.Either
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
class GetCryptoCurrencyStatusesSyncUseCase(
class GetMultiCryptoCurrencyStatusUseCase(
private val currencyStatusOperations: BaseCurrencyStatusOperations,
) {
suspend operator fun invoke(userWalletId: UserWalletId): Either<TokenListError, List<CryptoCurrencyStatus>> {
/**
* Returns synchronously list of cryptocurrency statuses for Multi-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
suspend fun invokeMultiWalletSync(userWalletId: UserWalletId): Either<TokenListError, List<CryptoCurrencyStatus>> {
return currencyStatusOperations.getCurrenciesStatusesSync(userWalletId)
.mapLeft { error -> error.mapToTokenListError() }
}

View file

@ -1,42 +0,0 @@
package com.tangem.domain.tokens
import arrow.core.Either
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
/**
* Use case for fetching the status of the primary cryptocurrency associated with a user wallet.
*
* @property dispatchers Provides coroutine dispatchers.
*/
class GetPrimaryCurrencyStatusUpdatesUseCase(
private val dispatchers: CoroutineDispatcherProvider,
private val currencyStatusOperations: BaseCurrencyStatusOperations,
) {
/**
* Invokes the use case.
*
* @param userWalletId The unique identifier of the user's wallet.
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
operator fun invoke(userWalletId: UserWalletId): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(getPrimaryCurrency(userWalletId))
}.flowOn(dispatchers.io)
}
private suspend fun getPrimaryCurrency(
userWalletId: UserWalletId,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return currencyStatusOperations.getPrimaryCurrencyStatusFlow(userWalletId).map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
}
}
}

View file

@ -0,0 +1,106 @@
package com.tangem.domain.tokens
import arrow.core.Either
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
/**
* Use case for fetching the status of a cryptocurrency associated with a user wallet.
*
*/
class GetSingleCryptoCurrencyStatusUseCase(
private val currencyStatusOperations: BaseCurrencyStatusOperations,
private val dispatchers: CoroutineDispatcherProvider,
) {
/**
* Returns cryptocurrency status flow for Multi-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @param currencyId The unique identifier of the cryptocurrency.
* @param isSingleWalletWithTokens Indicates whether the user wallet contains only one token on card (old cards)
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
fun invokeMultiWallet(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(
getCurrencyStatus(
userWalletId = userWalletId,
currencyId = currencyId,
isSingleWalletWithTokens = isSingleWalletWithTokens,
),
)
}.flowOn(dispatchers.io)
}
/**
* Returns cryptocurrency status flow for primary currency for Single-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
fun invokeSingleWallet(userWalletId: UserWalletId): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(
currencyStatusOperations.getPrimaryCurrencyStatusFlow(userWalletId).map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
},
)
}.flowOn(dispatchers.io)
}
/**
* Returns synchronously cryptocurrency status for Multi-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @param cryptoCurrencyId The unique identifier of the cryptocurrency.
* @param isSingleWalletWithTokens Indicates whether the user wallet contains only one token on card (old cards)
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
suspend fun invokeMultiWalletSync(
userWalletId: UserWalletId,
cryptoCurrencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean = false,
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return currencyStatusOperations.getCurrencyStatusSync(userWalletId, cryptoCurrencyId, isSingleWalletWithTokens)
.mapLeft { error -> error.mapToCurrencyError() }
}
/**
* Returns synchronously cryptocurrency status for primary currency for Single-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
suspend fun invokeSingleWalletSync(userWalletId: UserWalletId): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return currencyStatusOperations.getPrimaryCurrencyStatusSync(userWalletId)
.mapLeft { error -> error.mapToCurrencyError() }
}
private suspend fun getCurrencyStatus(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
val currencyFlow = currencyStatusOperations.getCurrencyStatusFlow(
userWalletId = userWalletId,
currencyId = currencyId,
isSingleWalletWithTokens = isSingleWalletWithTokens,
)
return currencyFlow.map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
}
}
}

View file

@ -7,8 +7,6 @@ package com.tangem.domain.tokens
*/
interface TokensFeatureToggles {
val isNetworksLoadingRefactoringEnabled: Boolean
val isQuotesLoadingRefactoringEnabled: Boolean
val isStakingLoadingRefactoringEnabled: Boolean

View file

@ -2,12 +2,10 @@ package com.tangem.domain.tokens
import arrow.core.Either
import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.delay
@ -15,14 +13,9 @@ import kotlinx.coroutines.delay
* Use case responsible for fetching currency status information, including network status
* and quotes for a given cryptocurrency. It provides methods to fetch currency status either
* by providing a specific currency ID or fetching the status of the primary currency.
*
* @param networksRepository The repository for retrieving network-related data.
*/
class UpdateDelayedNetworkStatusUseCase(
private val networksRepository: NetworksRepository,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val tokensFeatureToggles: TokensFeatureToggles,
) {
/**
@ -30,37 +23,24 @@ class UpdateDelayedNetworkStatusUseCase(
*
* @param userWalletId The ID of the user's wallet.
* @param network Network of the cryptocurrency.
* @param refresh Indicates whether to force a refresh of the status data.
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
*/
suspend operator fun invoke(
userWalletId: UserWalletId,
network: Network,
delayMillis: Long = 0L,
refresh: Boolean = false,
): Either<CurrencyStatusError, Unit> {
delay(delayMillis)
return either {
fetchNetworkStatus(userWalletId, network, refresh)
fetchNetworkStatus(userWalletId, network)
}
}
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(
userWalletId: UserWalletId,
network: Network,
refresh: Boolean,
) {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = network),
)
.mapLeft { CurrencyStatusError.DataError(it) }
} else {
catch(
block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(network), refresh) },
) {
raise(CurrencyStatusError.DataError(it))
}
}
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = network),
)
.mapLeft(CurrencyStatusError::DataError)
.bind()
}
}

View file

@ -22,7 +22,6 @@ import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.*
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator
import com.tangem.domain.wallets.models.UserWalletId
@ -33,7 +32,6 @@ import kotlinx.coroutines.flow.*
*
* @property currenciesRepository repository for currencies
* @property quotesRepository repository for quotes
* @property networksRepository repository for networks
* @property stakingRepository repository for staking
*
[REDACTED_AUTHOR]
@ -43,7 +41,6 @@ abstract class BaseCurrencyStatusOperations(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val quotesRepositoryV2: QuotesRepositoryV2,
private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
@ -203,24 +200,14 @@ abstract class BaseCurrencyStatusOperations(
?.right()
?: Error.EmptyQuotes.left()
val networkStatuses = if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(
userWalletId = userWalletId,
network = currency.network,
),
)
.firstOrNull()
.right()
} else {
networksRepository.getNetworkStatusesSync(
val networkStatuses = singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(
userWalletId = userWalletId,
networks = setOf(currency.network),
refresh = false,
)
.firstOrNull { it.network == currency.network }
.right()
}
network = currency.network,
),
)
.firstOrNull()
.right()
val yieldBalances = getYieldBalanceSync(userWalletId, currency)
@ -270,7 +257,7 @@ abstract class BaseCurrencyStatusOperations(
val nonEmptyCurrencies =
currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId).toNonEmptyListOrNull()
?: return emptyList<CryptoCurrencyStatus>().right()
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
val (_, currenciesIds) = getIds(nonEmptyCurrencies)
val rawIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet()
val quotes = if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
@ -279,16 +266,13 @@ abstract class BaseCurrencyStatusOperations(
quotesRepository.getQuotesSync(rawIds, false).right()
}
val networkStatuses = if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
multiNetworkStatusSupplier(
params = MultiNetworkStatusProducer.Params(userWalletId = userWalletId),
)
.firstOrNull()
.orEmpty()
.right()
} else {
networksRepository.getNetworkStatusesSync(userWalletId, networks, false).right()
}
val networkStatuses = multiNetworkStatusSupplier(
params = MultiNetworkStatusProducer.Params(userWalletId = userWalletId),
)
.firstOrNull()
.orEmpty()
.right()
val yieldBalances = getYieldBalancesSync(userWalletId, nonEmptyCurrencies)
return currencyStatusProxyCreator.createCurrenciesStatuses(
@ -326,25 +310,11 @@ abstract class BaseCurrencyStatusOperations(
)
}
val networkStatus = if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(
userWalletId = userWalletId,
network = currency.network,
),
)
.firstOrNull()
.right()
} else {
catch(
block = {
networksRepository.getNetworkStatusesSync(userWalletId, setOf(currency.network))
.firstOrNull { it.network == currency.network }
.right()
},
catch = { Error.DataError(it).left() },
)
}
val networkStatus = singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = currency.network),
)
.firstOrNull()
.right()
val yieldBalances = getYieldBalanceSync(userWalletId, currency)

View file

@ -30,7 +30,6 @@ import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.*
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.utils.extractAddress
import com.tangem.domain.wallets.models.UserWalletId
@ -43,7 +42,6 @@ class CachedCurrenciesStatusesOperations(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
quotesRepositoryV2: QuotesRepositoryV2,
private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
@ -58,7 +56,6 @@ class CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
quotesRepositoryV2 = quotesRepositoryV2,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
@ -156,7 +153,7 @@ class CachedCurrenciesStatusesOperations(
combine(
flow = getQuotes(currenciesIds),
flow2 = getNetworksStatuses(userWalletId, networks),
flow2 = getNetworkStatusesUpdates(userWalletId, networks),
flow3 = getYieldBalances(userWalletId, currencies),
flow4 = fetchingState.map {
val state = it[userWalletId] ?: return@map false
@ -184,13 +181,9 @@ class CachedCurrenciesStatusesOperations(
coroutineScope {
awaitAll(
async {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
multiNetworkStatusFetcher(
params = MultiNetworkStatusFetcher.Params(userWalletId, networks),
)
} else {
networksRepository.fetchNetworkStatuses(userWalletId, networks)
}
multiNetworkStatusFetcher(
params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks),
)
},
async {
val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId }
@ -327,44 +320,12 @@ class CachedCurrenciesStatusesOperations(
userWalletId: UserWalletId,
network: Network,
): EitherFlow<Error, Set<NetworkStatus>> {
return if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = network),
)
.map<NetworkStatus, Either<Error, Set<NetworkStatus>>> { setOf(it).right() }
.distinctUntilChanged()
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
} else {
networksRepository.getNetworkStatusesUpdates(userWalletId, setOf(network))
.map<Set<NetworkStatus>, Either<Error, Set<NetworkStatus>>> { it.right() }
.retryWhen { cause, _ ->
emit(Error.DataError(cause).left())
// adding delay before retry to avoid spam when flow restarted
delay(RETRY_DELAY)
true
}
.distinctUntilChanged()
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
}
}
private fun getNetworksStatuses(
userWalletId: UserWalletId,
networks: NonEmptySet<Network>,
): EitherFlow<TokenListError, Set<NetworkStatus>> {
return if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
getNetworkStatusesUpdates(userWalletId, networks)
} else {
networksRepository.getNetworkStatusesUpdates(userWalletId, networks)
.map<Set<NetworkStatus>, Either<TokenListError, Set<NetworkStatus>>> { it.right() }
.retryWhen { cause, _ ->
emit(TokenListError.DataError(cause).left())
// adding delay before retry to avoid spam when flow restarted
delay(RETRY_DELAY)
true
}
.distinctUntilChanged()
}
return singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = network),
)
.map<NetworkStatus, Either<Error, Set<NetworkStatus>>> { setOf(it).right() }
.distinctUntilChanged()
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
}
private fun getYieldBalances(

View file

@ -1,197 +0,0 @@
package com.tangem.domain.tokens
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.tangem.domain.core.error.DataError
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.mock.MockNetworks
import com.tangem.domain.tokens.mock.MockQuotes
import com.tangem.domain.tokens.mock.MockTokens
import com.tangem.domain.tokens.mock.MockTokensStates
import com.tangem.domain.tokens.model.*
import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations
import com.tangem.domain.tokens.repository.MockCurrenciesRepository
import com.tangem.domain.tokens.repository.MockNetworksRepository
import com.tangem.domain.tokens.repository.MockQuotesRepository
import com.tangem.domain.tokens.repository.MockStakingRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
import java.math.BigDecimal
internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
private val dispatchers = TestingCoroutineDispatcherProvider()
private val userWalletId = UserWalletId(value = null)
@Test
fun `when all data received then token should be received`() = runTest {
// Given
val expectedResult = MockTokensStates.loadedTokensStates.first().right()
val useCase = getUseCase()
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
@Test
fun `when token getting failed then error should be received`() = runTest {
// Given
val expectedResult = CurrencyStatusError.DataError(DataError.NetworkError.NoInternetConnection).left()
val useCase = getUseCase(token = DataError.NetworkError.NoInternetConnection.left())
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
@Test
fun `when quotes getting failed then currency with no quote status should be received`() = runTest {
// Given
val expectedResult = MockTokensStates.noQuotesTokensStatuses.first().right()
val useCase = getUseCase(quotes = flowOf(DataError.NetworkError.NoInternetConnection.left()))
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
@Test
fun `when networks statuses getting failed then error should be received`() = runTest {
// Given
val expectedResult = CurrencyStatusError.DataError(DataError.NetworkError.NoInternetConnection).left()
val useCase = getUseCase(statuses = flowOf(DataError.NetworkError.NoInternetConnection.left()))
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
@Test
fun `when networks statuses flow is empty then error should be received`() = runTest {
val expectedResult = CurrencyStatusError.UnableToCreateCurrency.left()
val useCase = getUseCase(statuses = flowOf())
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
@Test
fun `when quotes flow is empty then no quote status should be received`() = runTest {
val expectedResult = MockTokensStates.noQuotesTokensStatuses.first().right()
val useCase = getUseCase(quotes = flowOf(emptySet<Quote>().right()))
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
@Test
fun `when quotes are empty and statuses are verified then token without quote should be received`() = runTest {
val expectedResult = with(MockTokensStates.tokenState1) {
copy(
value = CryptoCurrencyStatus.NoQuote(
amount = BigDecimal.TEN,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
yieldBalance = null,
sources = CryptoCurrencyStatus.Sources(),
),
)
}
.right()
val useCase = getUseCase(
statuses = flowOf(MockNetworks.verifiedNetworksStatuses.right()),
quotes = flowOf(emptySet<Quote>().right()),
)
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
@Test
fun `when quotes are loaded and statuses are empty then error be received`() = runTest {
val expectedResult = CurrencyStatusError.UnableToCreateCurrency.left()
val useCase = getUseCase(
statuses = flowOf(emptySet<NetworkStatus>().right()),
quotes = flowOf(MockQuotes.quotes.right()),
)
// When
val result = useCase(userWalletId).first()
// Then
assertEquals(expectedResult, result)
}
private fun getUseCase(
token: Either<DataError, CryptoCurrency> = MockTokens.token1.right(),
removeCurrencyResult: Either<DataError, Unit> = Unit.right(),
quotes: Flow<Either<DataError, Set<Quote>>> = flowOf(MockQuotes.quotes.right()),
statuses: Flow<Either<DataError, Set<NetworkStatus>>> = flowOf(MockNetworks.verifiedNetworksStatuses.right()),
) = GetPrimaryCurrencyStatusUpdatesUseCase(
currencyStatusOperations = CachedCurrenciesStatusesOperations(
currenciesRepository = MockCurrenciesRepository(
sortTokensResult = Unit.right(),
removeCurrencyResult = removeCurrencyResult,
token = token,
tokens = flowOf(),
isGrouped = flowOf(),
isSortedByBalance = flowOf(),
),
quotesRepository = MockQuotesRepository(quotes),
networksRepository = MockNetworksRepository(statuses),
stakingRepository = MockStakingRepository(),
tokensFeatureToggles = object : TokensFeatureToggles {
override val isNetworksLoadingRefactoringEnabled: Boolean = false
override val isQuotesLoadingRefactoringEnabled: Boolean = false
override val isStakingLoadingRefactoringEnabled: Boolean = false
},
singleNetworkStatusSupplier = mockk(),
multiNetworkStatusFetcher = mockk(),
multiNetworkStatusSupplier = mockk(),
multiQuoteFetcher = mockk(),
singleQuoteSupplier = mockk(),
quotesRepositoryV2 = mockk(),
singleYieldBalanceSupplier = mockk(),
multiYieldBalanceFetcher = mockk(),
),
dispatchers = dispatchers,
)
}

View file

@ -7,12 +7,10 @@ import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.networks.single.SingleNetworkStatusProducer
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyAmountStatus
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.transaction.error.AssociateAssetError
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
@ -23,9 +21,7 @@ class AssociateAssetUseCase(
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val walletManagersFacade: WalletManagersFacade,
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
private val tokensFeatureToggles: TokensFeatureToggles,
) {
suspend operator fun invoke(
@ -59,20 +55,13 @@ class AssociateAssetUseCase(
}
private suspend fun isBalanceZero(userWalletId: UserWalletId, currency: CryptoCurrency): Boolean {
val networkStatus = if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(
userWalletId = userWalletId,
network = currency.network,
),
)
.firstOrNull()
} else {
networksRepository.getNetworkStatusesSync(
val networkStatus = singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(
userWalletId = userWalletId,
networks = setOf(currency.network),
).find { it.network == currency.network }
}
network = currency.network,
),
)
.firstOrNull()
val networkCoinAmountStatus = (networkStatus?.value as? NetworkStatus.Verified)
?.amounts

View file

@ -19,7 +19,6 @@ import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.error.SendTransactionError
@ -33,7 +32,6 @@ class SendTransactionUseCase(
private val transactionRepository: TransactionRepository,
private val walletManagersFacade: WalletManagersFacade,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val tokensFeatureToggles: TokensFeatureToggles,
) {
suspend operator fun invoke(
txsData: List<TransactionData>,
@ -80,16 +78,15 @@ class SendTransactionUseCase(
}
cardSdkConfigRepository.setLinkedTerminal(linkedTerminal)
return sendResult
.onRight {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
network = network,
),
)
}
singleNetworkStatusFetcher(
params = SingleNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
network = network,
),
)
}
.fold(
ifRight = { result -> result.hashes.right() },

View file

@ -1,11 +1,13 @@
package com.tangem.domain.walletconnect.model
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.serialization.Serializable
@Serializable
data class WcPairRequest(
val uri: String,
val source: Source,
val userWalletId: UserWalletId,
) {
enum class Source { QR, DEEPLINK, CLIPBOARD, ETC }
}

View file

@ -1,11 +1,13 @@
package com.tangem.domain.walletconnect.model
import com.domain.blockaid.models.dapp.CheckDAppResult
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSession
import com.tangem.domain.wallets.models.UserWallet
data class WcSession(
val wallet: UserWallet,
val networks: Set<Network>,
val sdkModel: WcSdkSession,
val securityStatus: CheckDAppResult,
)

View file

@ -1,5 +1,6 @@
package com.tangem.domain.wallets.usecase
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.TwinCardNumber
@ -30,11 +31,19 @@ class GetCardImageUseCase(
* @param cardId card id
* @param cardPublicKey card public key
*/
suspend operator fun invoke(cardId: String, cardPublicKey: ByteArray, size: ArtworkSize): ArtworkModel {
suspend operator fun invoke(
cardId: String,
cardPublicKey: ByteArray,
manufacturerName: String,
firmwareVersion: FirmwareVersion,
size: ArtworkSize,
): ArtworkModel {
return if (cardSdkFeatureToggles.isNewArtworkLoadingEnabled) {
val result = cardArtworksProvider.getArtwork(
cardId = cardId,
cardPublicKey = cardPublicKey,
manufacturerName = manufacturerName,
firmwareVersion = firmwareVersion,
size = size,
)
when (result) {

View file

@ -5,6 +5,7 @@ import androidx.compose.ui.Modifier
import com.tangem.core.decompose.navigation.DummyRouter
import com.tangem.core.navigation.url.DummyUrlOpener
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.details.component.DetailsComponent
import com.tangem.features.details.entity.DetailsFooterUM
import com.tangem.features.details.entity.DetailsUM
@ -18,7 +19,12 @@ internal class PreviewDetailsComponent : DetailsComponent {
private val previewBlocks = runBlocking {
ItemsBuilder(
router = DummyRouter(),
).buildAll(isWalletConnectAvailable = true, onSupportClick = {}, onBuyClick = {})
).buildAll(
isWalletConnectAvailable = true,
userWalletId = UserWalletId(""),
onSupportClick = {},
onBuyClick = {},
)
}
private val previewFooter = DetailsFooterUM(

View file

@ -80,6 +80,7 @@ internal class DetailsModel @Inject constructor(
items = MutableStateFlow(
itemsBuilder.buildAll(
isWalletConnectAvailable = isWalletConnectAvailable,
userWalletId = params.userWalletId,
onSupportClick = ::sendFeedback,
onBuyClick = ::onBuyClick,
),

View file

@ -6,6 +6,7 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.details.entity.DetailsItemUM
import com.tangem.features.details.impl.BuildConfig
import com.tangem.features.details.impl.R
@ -19,20 +20,21 @@ internal class ItemsBuilder @Inject constructor(private val router: Router) {
fun buildAll(
isWalletConnectAvailable: Boolean,
userWalletId: UserWalletId,
onSupportClick: () -> Unit,
onBuyClick: () -> Unit,
): ImmutableList<DetailsItemUM> = buildList {
buildWalletConnectBlock(isWalletConnectAvailable)?.let(::add)
buildWalletConnectBlock(isWalletConnectAvailable, userWalletId)?.let(::add)
buildUserWalletListBlock().let(::add)
buildShopBlock(onBuyClick).let(::add)
buildSettingsBlock().let(::add)
buildSupportBlock(onSupportClick).let(::add)
}.toImmutableList()
private fun buildWalletConnectBlock(isWalletConnectAvailable: Boolean): DetailsItemUM? {
private fun buildWalletConnectBlock(isWalletConnectAvailable: Boolean, userWalletId: UserWalletId): DetailsItemUM? {
return if (isWalletConnectAvailable) {
DetailsItemUM.WalletConnect(
onClick = { router.push(AppRoute.WalletConnectSessions) },
onClick = { router.push(AppRoute.WalletConnectSessions(userWalletId)) },
)
} else {
null

View file

@ -87,6 +87,8 @@ internal class UserWalletsFetcher @Inject constructor(
wallets.forEach { wallet ->
val artwork = getCardImageUseCase(
cardId = wallet.cardId,
manufacturerName = wallet.scanResponse.card.manufacturer.name,
firmwareVersion = wallet.scanResponse.card.firmwareVersion.toSdkFirmwareVersion(),
cardPublicKey = wallet.scanResponse.card.cardPublicKey,
size = ArtworkSize.SMALL,
)

View file

@ -15,10 +15,6 @@ import com.tangem.features.nft.collections.NFTCollectionsComponent
import com.tangem.features.nft.collections.entity.NFTCollectionsStateUM
import com.tangem.features.nft.collections.entity.NFTCollectionsUM
import com.tangem.features.nft.collections.entity.transformer.*
import com.tangem.features.nft.collections.entity.transformer.ChangeCollectionExpandedStateTransformer
import com.tangem.features.nft.collections.entity.transformer.ToggleSearchBarTransformer
import com.tangem.features.nft.collections.entity.transformer.UpdateDataStateTransformer
import com.tangem.features.nft.collections.entity.transformer.UpdateSearchQueryTransformer
import com.tangem.features.nft.impl.R
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*

View file

@ -1,5 +1,7 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.child
import com.tangem.common.card.Card.Manufacturer
import com.tangem.common.card.FirmwareVersion
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
import com.tangem.features.onboarding.v2.multiwallet.impl.MultiWalletInnerNavigationState
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
@ -18,6 +20,8 @@ class MultiWalletChildParams(
data class BackupCardInfo(
val cardId: String,
val cardPublicKey: ByteArray,
val manufacturer: Manufacturer,
val firmwareVersion: FirmwareVersion,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true

View file

@ -206,6 +206,8 @@ class MultiWalletBackupModel @Inject constructor(
val backupCardInfo = MultiWalletChildParams.Backup.BackupCardInfo(
cardId = result.data.cardId,
cardPublicKey = result.data.cardPublicKey,
manufacturer = result.data.manufacturer,
firmwareVersion = result.data.firmwareVersion,
)
params.backups.update {
it.copy(

View file

@ -86,6 +86,8 @@ internal class OnboardingMultiWalletModel @Inject constructor(
cardId = backup.card2.cardId,
cardPublicKey = backup.card2.cardPublicKey,
size = ArtworkSize.LARGE,
manufacturerName = backup.card2.manufacturer.name,
firmwareVersion = backup.card2.firmwareVersion,
)
_uiState.update {
it.copy(artwork2 = ArtworkUM(artwork.verifiedArtwork, artwork.defaultUrl))
@ -97,6 +99,8 @@ internal class OnboardingMultiWalletModel @Inject constructor(
cardId = backup.card3.cardId,
cardPublicKey = backup.card3.cardPublicKey,
size = ArtworkSize.LARGE,
manufacturerName = backup.card3.manufacturer.name,
firmwareVersion = backup.card3.firmwareVersion,
)
_uiState.update {
it.copy(artwork3 = ArtworkUM(artwork.verifiedArtwork, artwork.defaultUrl))
@ -170,6 +174,8 @@ internal class OnboardingMultiWalletModel @Inject constructor(
cardId = params.scanResponse.card.cardId,
cardPublicKey = params.scanResponse.card.cardPublicKey,
size = ArtworkSize.LARGE,
manufacturerName = params.scanResponse.card.manufacturer.name,
firmwareVersion = params.scanResponse.card.firmwareVersion.toSdkFirmwareVersion(),
)
_uiState.update {

View file

@ -16,7 +16,7 @@ import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
@ -39,7 +39,7 @@ import javax.inject.Inject
internal class OnboardingNoteTopUpModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val userWalletBuilderFactory: UserWalletBuilder.Factory,
private val getLegacyTopUpUrlUseCase: GetLegacyTopUpUrlUseCase,
@ -137,8 +137,11 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
private fun observeCryptoCurrencyStatus() {
val userWalletId = userWallet?.walletId ?: return
getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWalletId).map { it.getOrNull() }.filterNotNull()
.onEach(::applyCryptoCurrencyStatusToState).launchIn(modelScope)
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWalletId = userWalletId)
.map { it.getOrNull() }
.filterNotNull()
.onEach(::applyCryptoCurrencyStatusToState)
.launchIn(modelScope)
}
private fun applyCryptoCurrencyStatusToState(status: CryptoCurrencyStatus) {

View file

@ -94,6 +94,8 @@ internal class OnboardingNoteModel @Inject constructor(
cardId = cardInfo.cardId,
cardPublicKey = cardInfo.cardPublicKey,
size = ArtworkSize.LARGE,
manufacturerName = cardInfo.manufacturer.name,
firmwareVersion = cardInfo.firmwareVersion.toSdkFirmwareVersion(),
)
_uiState.update {
it.copy(cardArtwork = ArtworkUM(artwork.verifiedArtwork, artwork.defaultUrl))

View file

@ -7,18 +7,18 @@ import com.tangem.common.core.TangemError
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.toWrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
@ -34,7 +34,7 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
@ -77,7 +77,7 @@ internal class OnboardingTwinModel @Inject constructor(
private val tangemSdkManager: TangemSdkManager,
private val issuersConfigStorage: IssuersConfigStorage,
private val cardRepository: CardRepository,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val getLegacyTopUpUrlUseCase: GetLegacyTopUpUrlUseCase,
private val urlOpener: UrlOpener,
@ -337,16 +337,16 @@ internal class OnboardingTwinModel @Inject constructor(
setLoading(false)
}
val cryptoCurrencyStatus =
getPrimaryCurrencyStatusUpdatesUseCase.invoke(userWallet.walletId).firstOrNull()?.getOrNull()
?: run {
setLoading(false)
Timber.e("Unable to get currency status")
return@coroutineScope
}
val cryptoCurrencyStatus = getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId)
.firstOrNull()?.getOrNull()
?: run {
setLoading(false)
Timber.e("Unable to get currency status")
return@coroutineScope
}
launch {
getPrimaryCurrencyStatusUpdatesUseCase.invoke(userWallet.walletId)
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId)
.collect {
it.onRight { status ->
applyCryptoCurrencyStatusToState(status)

View file

@ -14,7 +14,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.onramp.GetHotCryptoUseCase
import com.tangem.domain.onramp.model.HotCryptoCurrency
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.features.onramp.hottokens.HotCryptoComponent
import com.tangem.features.onramp.hottokens.converter.HotTokenItemStateConverter
@ -45,7 +45,7 @@ internal class HotCryptoModel @Inject constructor(
paramsContainer: ParamsContainer,
getHotCryptoUseCase: GetHotCryptoUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
@ -103,7 +103,10 @@ internal class HotCryptoModel @Inject constructor(
private fun onSuccessAdding(id: CryptoCurrency.ID) {
modelScope.launch {
getCryptoCurrencyStatusSyncUseCase(userWalletId = params.userWalletId, cryptoCurrencyId = id)
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
userWalletId = params.userWalletId,
cryptoCurrencyId = id,
)
.onRight {
bottomSheetNavigation.dismiss()
params.onTokenClick(it)

View file

@ -52,7 +52,6 @@ internal class SendBalanceUpdater @AssistedInject constructor(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
delayMillis = delay,
refresh = true,
)
}

View file

@ -23,9 +23,8 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
@ -72,8 +71,7 @@ internal class SendModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
@ -244,13 +242,13 @@ internal class SendModel @Inject constructor(
isMultiCurrency: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return if (isMultiCurrency) {
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = isSingleWalletWithToken,
)
} else {
getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWalletId)
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWalletId = userWalletId)
}
}

View file

@ -19,7 +19,7 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -60,7 +60,7 @@ internal class NFTSendModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val createNFTTransferTransactionUseCase: CreateNFTTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
@ -183,7 +183,7 @@ internal class NFTSendModel @Inject constructor(
}
private fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean) {
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = isSingleWalletWithToken,

View file

@ -84,7 +84,7 @@ import kotlin.properties.Delegates
internal class SendModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
@ -325,13 +325,13 @@ internal class SendModel @Inject constructor(
isMultiCurrency: Boolean,
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return if (isMultiCurrency) {
getCryptoCurrencyStatusSyncUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
isSingleWalletWithTokens = isSingleWalletWithToken,
)
} else {
getCryptoCurrencyStatusSyncUseCase(userWalletId = userWalletId)
getSingleCryptoCurrencyStatusUseCase.invokeSingleWalletSync(userWalletId = userWalletId)
}
}
@ -1048,7 +1048,6 @@ internal class SendModel @Inject constructor(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
delayMillis = BALANCE_UPDATE_DELAY,
refresh = true,
)
},
).awaitAll()

View file

@ -93,7 +93,7 @@ internal class StakingModel @Inject constructor(
private val stateController: StakingStateController,
override val dispatchers: CoroutineDispatcherProvider,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
@ -920,7 +920,7 @@ internal class StakingModel @Inject constructor(
)
},
)
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = cryptoCurrencyId,
isSingleWalletWithTokens = false,

View file

@ -83,7 +83,6 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
userWalletId = userWallet.walletId,
network = cryptoCurrencyStatus.currency.network,
delayMillis = delay,
refresh = true,
)
}

View file

@ -18,7 +18,7 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.quotes.QuotesRepositoryV2
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.GetCurrencyCheckUseCase
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.*
@ -54,7 +54,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
private val userWalletManager: UserWalletManager,
private val repository: SwapRepository,
private val allowPermissionsHandler: AllowPermissionsHandler,
private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase,
private val getMultiCryptoCurrencyStatusUseCase: GetMultiCryptoCurrencyStatusUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val createTransactionUseCase: CreateTransactionUseCase,
@ -92,7 +92,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
}
override suspend fun getTokensDataState(currency: CryptoCurrency): TokensDataStateExpress {
val walletCurrencyStatuses = getMultiCryptoCurrencyStatusUseCase(userWalletId)
val walletCurrencyStatuses = getMultiCryptoCurrencyStatusUseCase.invokeMultiWalletSync(userWalletId)
.getOrElse { emptyList() }
val walletCurrencyStatusesExceptInitial = walletCurrencyStatuses

View file

@ -1,6 +1,6 @@
package com.tangem.feature.swap.domain.di
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.feature.swap.domain.*
import dagger.Module
@ -28,8 +28,8 @@ internal class SwapDomainModule {
@Singleton
fun providesGetCryptoCurrencyStatusUseCase(
currencyStatusOperations: BaseCurrencyStatusOperations,
): GetCryptoCurrencyStatusesSyncUseCase {
return GetCryptoCurrencyStatusesSyncUseCase(currencyStatusOperations)
): GetMultiCryptoCurrencyStatusUseCase {
return GetMultiCryptoCurrencyStatusUseCase(currencyStatusOperations)
}
@Provides

View file

@ -34,7 +34,10 @@ import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
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
@ -86,9 +89,8 @@ internal class SwapModel @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val analyticsErrorEventHandler: AnalyticsErrorHandler,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
@ -98,7 +100,7 @@ internal class SwapModel @Inject constructor(
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val shouldShowStoriesUseCase: ShouldShowStoriesUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val getUserCountryUseCase: GetUserCountryUseCase,
getUserCountryUseCase: GetUserCountryUseCase,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
swapInteractorFactory: SwapInteractor.Factory,
private val urlOpener: UrlOpener,
@ -176,8 +178,12 @@ internal class SwapModel @Inject constructor(
}
modelScope.launch(dispatchers.io) {
val fromStatus = getCryptoCurrencyStatusUseCase(userWalletId, initialCurrencyFrom.id).getOrNull()
val toStatus = initialCurrencyTo?.let { getCryptoCurrencyStatusUseCase(userWalletId, it.id).getOrNull() }
val fromStatus =
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(userWalletId, initialCurrencyFrom.id)
.getOrNull()
val toStatus = initialCurrencyTo?.let {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(userWalletId, it.id).getOrNull()
}
val wallet = getUserWalletUseCase(userWalletId).getOrNull()
if (fromStatus == null || wallet == null) {
@ -861,7 +867,7 @@ internal class SwapModel @Inject constructor(
) {
Timber.d("Subscribe to ${coin.id} balance updates")
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = coin.id,
isSingleWalletWithTokens = false,
@ -1311,7 +1317,6 @@ internal class SwapModel @Inject constructor(
userWalletId = userWalletId,
network = network,
delayMillis = UPDATE_BALANCE_DELAY_MILLIS,
refresh = true,
)
}

View file

@ -91,7 +91,7 @@ import javax.inject.Inject
@ModelScoped
internal class TokenDetailsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
@ -117,7 +117,6 @@ internal class TokenDetailsModel @Inject constructor(
private val analyticsEventsHandler: AnalyticsEventHandler,
private val vibratorHapticManager: VibratorHapticManager,
private val clipboardManager: ClipboardManager,
private val getCryptoCurrencySyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
private val onrampFeatureToggles: OnrampFeatureToggles,
private val shareManager: ShareManager,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
@ -219,7 +218,7 @@ internal class TokenDetailsModel @Inject constructor(
private fun initButtons() {
// we need also init buttons before start all loading to avoid buttons blocking
modelScope.launch {
val currentCryptoCurrencyStatus = getCryptoCurrencySyncUseCase.invoke(
val currentCryptoCurrencyStatus = getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
isSingleWalletWithTokens = false,
@ -300,7 +299,7 @@ internal class TokenDetailsModel @Inject constructor(
private fun subscribeOnCurrencyStatusUpdates() {
modelScope.launch(dispatchers.main) {
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
@ -364,7 +363,6 @@ internal class TokenDetailsModel @Inject constructor(
updateDelayedCurrencyStatusUseCase(
userWalletId = userWalletId,
network = toCryptoCurrency.network,
refresh = true,
)
}
}

View file

@ -8,7 +8,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.txhistory.models.TxHistoryStateError
@ -39,7 +39,7 @@ internal class TxHistoryModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val urlOpener: UrlOpener,
@ -165,7 +165,7 @@ internal class TxHistoryModel @Inject constructor(
val userWallet: UserWallet = requireNotNull(getUserWalletUseCase(params.userWalletId).getOrNull()) {
"User wallet not found"
}
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = params.userWalletId,
currencyId = params.currency.id,
isSingleWalletWithTokens = userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),

View file

@ -8,7 +8,7 @@ import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.TokensAction
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenActionsState
@ -67,7 +67,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
private val walletWarningsClickIntents: WalletWarningsClickIntentsImplementor,
private val onrampStatusFactory: OnrampStatusFactory,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase,
@ -162,7 +162,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
override fun onTransactionClick(txHash: String) {
modelScope.launch(dispatchers.main) {
val currency = getPrimaryCurrencyStatusUpdatesUseCase.unwrap(
val currency = getSingleCryptoCurrencyStatusUseCase.unwrap(
userWalletId = stateHolder.getSelectedWalletId(),
)
?.currency

View file

@ -34,7 +34,7 @@ import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.IsCryptoCurrencyCoinCouldHideUseCase
import com.tangem.domain.tokens.RemoveCurrencyUseCase
import com.tangem.domain.tokens.legacy.TradeCryptoAction
@ -110,7 +110,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val walletManagersFacade: WalletManagersFacade,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
private val getExploreUrlUseCase: GetExploreUrlUseCase,
@ -489,7 +489,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
val userWalletId = stateHolder.getSelectedWalletId()
modelScope.launch(dispatchers.main) {
val currencyStatus = getPrimaryCurrencyStatusUpdatesUseCase.unwrap(userWalletId) ?: return@launch
val currencyStatus = getSingleCryptoCurrencyStatusUseCase.unwrap(userWalletId) ?: return@launch
when (val addresses = currencyStatus.value.networkAddress) {
is NetworkAddress.Selectable -> {

View file

@ -19,9 +19,6 @@ import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
import com.tangem.domain.tokens.FetchTokenListUseCase
import com.tangem.domain.tokens.FetchTokenListUseCase.RefreshMode
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
@ -91,7 +88,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
private val walletEventSender: WalletEventSender,
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
private val fetchTokenListUseCase: FetchTokenListUseCase,
private val setCardWasScannedUseCase: SetCardWasScannedUseCase,
private val neverToSuggestRateAppUseCase: NeverToSuggestRateAppUseCase,
private val remindToRateAppLaterUseCase: RemindToRateAppLaterUseCase,
@ -105,7 +101,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val urlOpener: UrlOpener,
private val tokensFeatureToggles: TokensFeatureToggles,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val appRouter: AppRouter,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
@ -148,22 +143,13 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
).fold(
ifLeft = { Timber.e(it, "Failed to derive public keys") },
ifRight = {
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
multiNetworkStatusFetcher(
params = MultiNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
networks = missedAddressCurrencies.map(CryptoCurrency::network).toSet(),
),
)
} else {
fetchTokenListUseCase(
multiNetworkStatusFetcher(
params = MultiNetworkStatusFetcher.Params(
userWalletId = userWallet.walletId,
mode = RefreshMode.SKIP_CURRENCIES,
currencies = missedAddressCurrencies,
).onLeft {
Timber.e("Unable to refresh token list: $it")
}
}
networks = missedAddressCurrencies.map(CryptoCurrency::network).toSet(),
),
)
.onLeft { Timber.e("Unable to refresh token list: $it") }
},
)
}

View file

@ -7,7 +7,7 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.StatusSource
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
@ -22,7 +22,7 @@ import javax.inject.Inject
@ModelScoped
internal class GetSingleWalletWarningsFactory @Inject constructor(
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
@ -36,7 +36,7 @@ internal class GetSingleWalletWarningsFactory @Inject constructor(
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
return combine(
flow = getPrimaryCurrencyStatusUpdatesUseCase(userWallet.walletId),
flow = getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId),
flow2 = isReadyToShowRateAppUseCase().conflate(),
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
flow4 = getWalletsUseCase().conflate(),

View file

@ -1,7 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import arrow.core.Either
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
@ -20,8 +20,8 @@ internal fun GetSelectedWalletSyncUseCase.unwrap(): UserWallet? {
)
}
internal suspend fun GetPrimaryCurrencyStatusUpdatesUseCase.unwrap(userWalletId: UserWalletId): CryptoCurrencyStatus? {
return this(userWalletId)
internal suspend fun GetSingleCryptoCurrencyStatusUseCase.unwrap(userWalletId: UserWalletId): CryptoCurrencyStatus? {
return invokeSingleWallet(userWalletId)
.conflate()
.distinctUntilChanged()
.filter(Either<CurrencyStatusError, CryptoCurrencyStatus>::isRight)
@ -35,11 +35,11 @@ internal suspend fun GetPrimaryCurrencyStatusUpdatesUseCase.unwrap(userWalletId:
)
}
internal suspend fun GetPrimaryCurrencyStatusUpdatesUseCase.collectLatest(
internal suspend fun GetSingleCryptoCurrencyStatusUseCase.collectLatest(
userWalletId: UserWalletId,
onRight: suspend (CryptoCurrencyStatus) -> Unit,
) {
this(userWalletId = userWalletId)
invokeSingleWallet(userWalletId = userWalletId)
.conflate()
.distinctUntilChanged()
.collectLatest { maybeStatus ->

View file

@ -6,7 +6,7 @@ import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
@ -29,7 +29,7 @@ internal class SingleWalletContentLoader(
private val clickIntents: WalletClickIntents,
private val isRefresh: Boolean,
private val stateHolder: WalletStateController,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
private val getSingleWalletWarningsFactory: GetSingleWalletWarningsFactory,
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
@ -48,7 +48,7 @@ internal class SingleWalletContentLoader(
PrimaryCurrencySubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
setWalletWithFundsFoundUseCase = setWalletWithFundsFoundUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
analyticsEventHandler = analyticsEventHandler,
@ -57,7 +57,7 @@ internal class SingleWalletContentLoader(
userWallet = userWallet,
stateHolder = stateHolder,
clickIntents = clickIntents,
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
getCryptoCurrencyActionsUseCase = getCryptoCurrencyActionsUseCase,
),
SingleWalletNotificationsSubscriber(
@ -78,7 +78,7 @@ internal class SingleWalletContentLoader(
clickIntents = clickIntents,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
analyticsEventHandler = analyticsEventHandler,
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
),
@ -87,7 +87,7 @@ internal class SingleWalletContentLoader(
isRefresh = isRefresh,
stateHolder = stateHolder,
clickIntents = clickIntents,
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
txHistoryItemsCountUseCase = txHistoryItemsCountUseCase,
txHistoryItemsUseCase = txHistoryItemsUseCase,
),

View file

@ -7,7 +7,7 @@ import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
@ -22,7 +22,7 @@ import javax.inject.Inject
@Suppress("LongParameterList")
internal class SingleWalletContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateController,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
private val getSingleWalletWarningsFactory: GetSingleWalletWarningsFactory,
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
@ -42,7 +42,7 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
clickIntents = clickIntents,
isRefresh = isRefresh,
stateHolder = stateHolder,
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
getCryptoCurrencyActionsUseCase = getCryptoCurrencyActionsUseCase,
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
setWalletWithFundsFoundUseCase = setWalletWithFundsFoundUseCase,

View file

@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
@ -23,7 +23,7 @@ import java.math.BigDecimal
internal class PrimaryCurrencySubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -33,7 +33,7 @@ internal class PrimaryCurrencySubscriber(
coroutineScope: CoroutineScope,
): Flow<Pair<Either<CurrencyStatusError, CryptoCurrencyStatus>, AppCurrency>> {
return combine(
flow = getPrimaryCurrencyStatusUpdatesUseCase(userWallet.walletId)
flow = getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId)
.conflate()
.distinctUntilChanged(),
flow2 = getSelectedAppCurrencyUseCase()

View file

@ -1,7 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -15,13 +15,13 @@ internal class SingleWalletButtonsSubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,
private val clickIntents: WalletClickIntents,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<TokenActionsState> {
return channelFlow {
getPrimaryCurrencyStatusUpdatesUseCase.collectLatest(userWalletId = userWallet.walletId) { status ->
getSingleCryptoCurrencyStatusUseCase.collectLatest(userWalletId = userWallet.walletId) { status ->
getCryptoCurrencyActionsUseCase(userWallet = userWallet, cryptoCurrencyStatus = status)
.conflate()
.distinctUntilChanged()

View file

@ -8,7 +8,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.onramp.model.cache.OnrampTransaction
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
@ -26,7 +26,7 @@ internal class SingleWalletExpressStatusesSubscriber(
private val clickIntents: WalletClickIntents,
private val analyticsEventHandler: AnalyticsEventHandler,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
) : WalletSubscriber() {
@ -35,7 +35,7 @@ internal class SingleWalletExpressStatusesSubscriber(
coroutineScope: CoroutineScope,
): Flow<Pair<Either<CurrencyStatusError, CryptoCurrencyStatus>, AppCurrency>> {
return combine(
flow = getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWallet.walletId)
flow = getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWalletId = userWallet.walletId)
.conflate()
.distinctUntilChanged(),
flow2 = getSelectedAppCurrencyUseCase()

View file

@ -5,7 +5,7 @@ import androidx.paging.cachedIn
import androidx.paging.map
import arrow.core.Either
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryListError
@ -35,14 +35,14 @@ internal class TxHistorySubscriber(
private val isRefresh: Boolean,
private val stateHolder: WalletStateController,
private val clickIntents: WalletClickIntents,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<PagingData<TxHistoryItem>> {
return flow {
getPrimaryCurrencyStatusUpdatesUseCase.collectLatest(userWalletId = userWallet.walletId) { status ->
getSingleCryptoCurrencyStatusUseCase.collectLatest(userWalletId = userWallet.walletId) { status ->
val maybeTxHistoryItemCount = txHistoryItemsCountUseCase(
userWalletId = userWallet.walletId,
currency = status.currency,

View file

@ -2,7 +2,9 @@ package com.tangem.features.walletconnect.components
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
interface WalletConnectEntryComponent : ComposableContentComponent {
interface Factory : ComponentFactory<Unit, WalletConnectEntryComponent>
data class Params(val userWalletId: UserWalletId)
interface Factory : ComponentFactory<Params, WalletConnectEntryComponent>
}

View file

@ -12,9 +12,8 @@ android {
}
dependencies {
implementation(projects.features.wallet.api)
implementation(projects.features.walletconnect.api)
implementation(projects.domain.walletConnect)
implementation(projects.domain.walletConnect.models)
/** Core */
implementation(projects.core.configToggles)
@ -24,13 +23,22 @@ dependencies {
implementation(projects.common.ui)
/** Domain models */
implementation(projects.domain.appCurrency.models)
implementation(projects.domain.balanceHiding.models)
implementation(projects.domain.blockaid.models)
implementation(projects.domain.models)
implementation(projects.domain.qrScanning.models)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
implementation(projects.domain.walletConnect)
implementation(projects.domain.walletConnect.models)
/** Domain */
implementation(projects.domain.appCurrency)
implementation(projects.domain.balanceHiding)
implementation(projects.domain.qrScanning)
implementation(projects.domain.tokens)
implementation(projects.domain.wallets)
/** DI */
implementation(deps.hilt.android)

View file

@ -13,15 +13,17 @@ import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.connections.model.WcConnectionsModel
import com.tangem.features.walletconnect.connections.routes.WcConnectionsBottomSheetConfig
import com.tangem.features.walletconnect.connections.ui.WcConnectionsContent
internal class ConnectionsComponent(
appComponentContext: AppComponentContext,
params: Params,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val model: WcConnectionsModel = getOrCreateModel()
private val model: WcConnectionsModel = getOrCreateModel(params)
private val bottomSheetSlot = childSlot(
source = model.bottomSheetNavigation,
serializer = null,
@ -52,4 +54,6 @@ internal class ConnectionsComponent(
)
}
}
data class Params(val userWalletId: UserWalletId)
}

View file

@ -22,7 +22,7 @@ import dagger.assisted.AssistedInject
@Suppress("UnusedPrivateMember")
internal class DefaultWalletConnectEntryComponent @AssistedInject constructor(
@Assisted private val appComponentContext: AppComponentContext,
@Assisted params: Unit,
@Assisted private val params: WalletConnectEntryComponent.Params,
) : AppComponentContext by appComponentContext, WalletConnectEntryComponent {
private val contentNavigation = StackNavigation<ConnectionsInnerRoute>()
@ -47,12 +47,18 @@ internal class DefaultWalletConnectEntryComponent @AssistedInject constructor(
config: ConnectionsInnerRoute,
componentContext: ComponentContext,
): ComposableContentComponent = when (config) {
ConnectionsInnerRoute.Connections -> ConnectionsComponent(childByContext(componentContext))
ConnectionsInnerRoute.Connections -> ConnectionsComponent(
appComponentContext = childByContext(componentContext),
params = ConnectionsComponent.Params(params.userWalletId),
)
ConnectionsInnerRoute.QrScan -> TODO("[REDACTED_JIRA]")
}
@AssistedFactory
interface Factory : WalletConnectEntryComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultWalletConnectEntryComponent
override fun create(
context: AppComponentContext,
params: WalletConnectEntryComponent.Params,
): DefaultWalletConnectEntryComponent
}
}

View file

@ -1,13 +1,12 @@
package com.tangem.features.walletconnect.connections.components
import androidx.compose.animation.animateContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.stack.Children
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.tangem.core.decompose.context.AppComponentContext
@ -18,8 +17,10 @@ import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes
import com.tangem.features.walletconnect.impl.R
@ -30,9 +31,8 @@ internal class WcAppInfoContainerComponent(
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val model: WcAppInfoModel = getOrCreateModel(params = params)
private val contentNavigation = StackNavigation<WcAppInfoRoutes>()
private val contentStack = childStack(
source = contentNavigation,
source = model.contentNavigation,
serializer = WcAppInfoRoutes.serializer(),
initialConfiguration = WcAppInfoRoutes.AppInfo,
childFactory = ::screenChild,
@ -51,10 +51,17 @@ internal class WcAppInfoContainerComponent(
onDismissRequest = ::dismiss,
content = content.active.configuration,
),
containerColor = TangemTheme.colors.background.tertiary,
onBack = ::contentBack,
containerColor = when (content.active.configuration) {
is WcAppInfoRoutes.Alert,
is WcAppInfoRoutes.SelectNetworks,
is WcAppInfoRoutes.AppInfo,
-> TangemTheme.colors.background.tertiary
is WcAppInfoRoutes.SelectWallet -> TangemTheme.colors.background.primary
},
title = { config -> Title(route = config) },
content = {
Children(stack = content, animation = stackAnimation()) { child ->
Children(modifier = Modifier.animateContentSize(), stack = content) { child ->
child.instance.Content(modifier = Modifier)
}
},
@ -64,7 +71,12 @@ internal class WcAppInfoContainerComponent(
@Composable
private fun Title(route: WcAppInfoRoutes) {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.wc_wallet_connect),
title = when (route) {
is WcAppInfoRoutes.Alert -> null
WcAppInfoRoutes.AppInfo -> resourceReference(R.string.wc_wallet_connect)
WcAppInfoRoutes.SelectNetworks -> TODO("[REDACTED_TASK_KEY]")
WcAppInfoRoutes.SelectWallet -> stringReference("Choose wallet")
},
startIconRes = when (route) {
is WcAppInfoRoutes.AppInfo, is WcAppInfoRoutes.Alert -> null
is WcAppInfoRoutes.SelectNetworks, is WcAppInfoRoutes.SelectWallet -> R.drawable.ic_back_24
@ -88,19 +100,25 @@ internal class WcAppInfoContainerComponent(
}
private fun contentBack() {
contentNavigation.pop()
if (contentStack.value.active.configuration == WcAppInfoRoutes.AppInfo) {
dismiss()
} else {
model.contentNavigation.pop()
}
}
private fun screenChild(config: WcAppInfoRoutes, componentContext: ComponentContext): ComposableContentComponent =
when (config) {
is WcAppInfoRoutes.AppInfo -> WcAppInfoComponent(
appComponentContext = childByContext(componentContext),
model = model,
)
private fun screenChild(config: WcAppInfoRoutes, componentContext: ComponentContext): ComposableContentComponent {
val appComponentContext = childByContext(componentContext)
return when (config) {
is WcAppInfoRoutes.AppInfo -> WcAppInfoComponent(appComponentContext = appComponentContext, model = model)
is WcAppInfoRoutes.Alert -> TODO()
WcAppInfoRoutes.SelectNetworks -> TODO()
WcAppInfoRoutes.SelectWallet -> TODO()
WcAppInfoRoutes.SelectWallet -> WcSelectWalletComponent(
appComponentContext = appComponentContext,
model = model,
)
}
}
data class Params(val wcUrl: String, val source: WcPairRequest.Source)
data class Params(val userWalletId: UserWalletId, val wcUrl: String, val source: WcPairRequest.Source)
}

View file

@ -0,0 +1,130 @@
package com.tangem.features.walletconnect.connections.components
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.common.ui.userwallet.UserWalletItem
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
internal class WcSelectWalletComponent(
appComponentContext: AppComponentContext,
private val model: WcAppInfoModel,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
@Composable
override fun Content(modifier: Modifier) {
val walletsState by model.walletsUiState.collectAsStateWithLifecycle()
WcSelectWalletContent(
modifier = modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
wallets = walletsState.wallets,
selectedWalletId = walletsState.selectedUserWallet.walletId,
)
}
}
@Composable
private fun WcSelectWalletContent(
wallets: ImmutableList<UserWalletItemUM>,
selectedWalletId: UserWalletId,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
wallets.fastForEach { state ->
key(state.id) {
val baseModifier = Modifier
.clip(RoundedCornerShape(14.dp))
.clickable(onClick = state.onClick)
val itemModifier = if (state.id == selectedWalletId) {
baseModifier.border(
width = 1.dp,
color = TangemTheme.colors.text.accent,
shape = RoundedCornerShape(14.dp),
)
} else {
baseModifier
}
UserWalletItem(
modifier = itemModifier,
state = state,
blockColors = TangemBlockCardColors.copy(
containerColor = Color.Unspecified,
disabledContainerColor = Color.Unspecified,
),
)
}
}
}
}
@Composable
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO)
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun WcSelectWalletContent_Preview() {
TangemThemePreview {
WcSelectWalletContent(
modifier = Modifier
.background(TangemTheme.colors.background.tertiary)
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
selectedWalletId = UserWalletId("user_wallet_1".encodeToByteArray()),
wallets = persistentListOf(
UserWalletItemUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = stringReference("Tangem 2.0"),
information = stringReference("42 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_2".encodeToByteArray()),
name = stringReference("Tangem White"),
information = stringReference("24 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Bitcoin"),
information = stringReference("1 token"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_4".encodeToByteArray()),
name = stringReference("Tangem 1.0"),
information = stringReference("21 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
),
)
}
}

View file

@ -26,6 +26,7 @@ internal sealed class WcAppInfoUM : TangemBottomSheetConfigContent {
val appSubtitle: String,
val notification: WcAppInfoSecurityNotification?,
val walletName: String,
val onWalletClick: () -> Unit,
val networksInfo: WcNetworksInfo,
override val connectButtonConfig: WcPrimaryButtonConfig,
override val onDismiss: () -> Unit,

View file

@ -0,0 +1,10 @@
package com.tangem.features.walletconnect.connections.entity
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.domain.wallets.models.UserWallet
import kotlinx.collections.immutable.ImmutableList
internal data class WcAppInfoWalletUM(
val wallets: ImmutableList<UserWalletItemUM>,
val selectedUserWallet: UserWallet,
)

View file

@ -1,47 +1,98 @@
package com.tangem.features.walletconnect.connections.model
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.pushNew
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.walletconnect.connections.components.WcAppInfoContainerComponent
import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
import com.tangem.features.walletconnect.connections.entity.WcAppInfoWalletUM
import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
import com.tangem.features.walletconnect.connections.model.transformers.WcAppInfoTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcAppInfoWalletChangedTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcConnectButtonProgressTransformer
import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes
import com.tangem.features.walletconnect.connections.utils.WcUserWalletsFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.*
import javax.inject.Inject
import kotlin.properties.Delegates
import com.tangem.utils.transformer.update as transformerUpdate
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class WcAppInfoModel @Inject constructor(
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
wcPairUseCaseFactory: WcPairUseCase.Factory,
private val router: Router,
getWalletsUseCase: GetWalletsUseCase,
getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
messageSender: UiMessageSender,
getCardImageUseCase: GetCardImageUseCase,
paramsContainer: ParamsContainer,
) : Model() {
private val params: WcAppInfoContainerComponent.Params = paramsContainer.require()
private val wcPairUseCase = wcPairUseCaseFactory.create(WcPairRequest(uri = params.wcUrl, source = params.source))
private val wcPairUseCase = wcPairUseCaseFactory.create(
WcPairRequest(
userWalletId = params.userWalletId,
uri = params.wcUrl,
source = params.source,
),
)
private val userWalletsFetcher = WcUserWalletsFetcher(
getWalletsUseCase = getWalletsUseCase,
getWalletTotalBalanceUseCase = getWalletTotalBalanceUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
getBalanceHidingSettingsUseCase = getBalanceHidingSettingsUseCase,
messageSender = messageSender,
getCardImageUseCase = getCardImageUseCase,
onWalletSelected = ::onWalletSelected,
)
private val selectedUserWalletFlow =
MutableStateFlow(getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId })
// TODO(wc) Doston: Temp solution, will be fixed in next PR`s
private var proposalNetwork by Delegates.notNull<WcSessionProposal.ProposalNetwork>()
internal var sessionProposal by Delegates.notNull<WcSessionProposal>()
// UI states
internal val contentNavigation = StackNavigation<WcAppInfoRoutes>()
val appInfoUiState: StateFlow<WcAppInfoUM>
field = MutableStateFlow<WcAppInfoUM>(createLoadingState())
// TODO(wc) Doston: Temp solution, will be fixed in next PR`s
private var userWallet by Delegates.notNull<UserWallet>()
private var proposalNetwork by Delegates.notNull<WcSessionProposal.ProposalNetwork>()
val walletsUiState: StateFlow<WcAppInfoWalletUM>
field = MutableStateFlow(WcAppInfoWalletUM(persistentListOf(), selectedUserWalletFlow.value))
init {
loadDAppInfo()
combine(
flow = userWalletsFetcher.userWallets,
flow2 = selectedUserWalletFlow,
transform = ::updateWalletsState,
).launchIn(modelScope)
}
private fun loadDAppInfo() {
@ -62,14 +113,15 @@ internal class WcAppInfoModel @Inject constructor(
}
is WcPairState.Loading -> appInfoUiState.update { createLoadingState() }
is WcPairState.Proposal -> {
userWallet = state.dAppSession.proposalNetwork.keys.first { !it.isLocked }
proposalNetwork = state.dAppSession.proposalNetwork.getValue(userWallet)
sessionProposal = state.dAppSession
proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWalletFlow.value)
appInfoUiState.transformerUpdate(
WcAppInfoTransformer(
dAppSession = state.dAppSession,
onDismiss = ::dismiss,
onConnect = ::onConnect,
userWallet = userWallet,
onWalletClick = { contentNavigation.pushNew(WcAppInfoRoutes.SelectWallet) },
userWallet = selectedUserWalletFlow.value,
proposalNetwork = proposalNetwork,
),
)
@ -87,13 +139,24 @@ internal class WcAppInfoModel @Inject constructor(
private fun onConnect() {
wcPairUseCase.approve(
WcSessionApprove(
wallet = userWallet,
network =
proposalNetwork.required.plus(proposalNetwork.available).toList(),
wallet = selectedUserWalletFlow.value,
network = proposalNetwork.required.plus(proposalNetwork.available).toList(),
),
)
}
private fun onWalletSelected(userWalletId: UserWalletId) {
val selectedUserWallet = sessionProposal.proposalNetwork.keys.first { it.walletId == userWalletId }
selectedUserWalletFlow.update { selectedUserWallet }
proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWallet)
appInfoUiState.transformerUpdate(WcAppInfoWalletChangedTransformer(selectedUserWallet, proposalNetwork))
contentNavigation.pop()
}
private fun updateWalletsState(items: ImmutableList<UserWalletItemUM>, userWallet: UserWallet) {
walletsUiState.update { state -> state.copy(wallets = items, selectedUserWallet = userWallet) }
}
private fun createLoadingState(): WcAppInfoUM.Loading {
return WcAppInfoUM.Loading(
onDismiss = ::dismiss,

View file

@ -6,6 +6,7 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.iconResId
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.walletconnect.model.WcSession
@ -13,9 +14,10 @@ import com.tangem.domain.walletconnect.usecase.WcSessionsUseCase
import com.tangem.domain.walletconnect.usecase.disconnect.WcDisconnectUseCase
import com.tangem.features.walletconnect.connections.components.WcConnectedAppInfoComponent
import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfoUM
import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem
import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
@ -56,7 +58,15 @@ internal class WcConnectedAppInfoModel @Inject constructor(
isVerified = session.securityStatus == CheckDAppResult.SAFE,
appSubtitle = session.sdkModel.appMetaData.description,
walletName = session.wallet.name,
networks = persistentListOf(), // TODO(wc): Nikolai & Doston: Where to find networks???
networks = session.networks
.map {
WcNetworkInfoItem(
id = it.id.value,
icon = it.iconResId,
name = it.name,
symbol = it.currencySymbol,
)
}.toImmutableList(),
disconnectButtonConfig = WcPrimaryButtonConfig(
showProgress = false,
enabled = true,

View file

@ -6,6 +6,7 @@ import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
@ -16,11 +17,12 @@ import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.usecase.WcSessionsUseCase
import com.tangem.domain.walletconnect.usecase.disconnect.WcDisconnectUseCase
import com.tangem.features.walletconnect.connections.components.ConnectionsComponent
import com.tangem.features.walletconnect.connections.entity.WcConnectionsState
import com.tangem.features.walletconnect.connections.entity.WcConnectionsTopAppBarConfig
import com.tangem.features.walletconnect.connections.model.transformers.WcSessionsTransformer
@ -44,8 +46,10 @@ internal class WcConnectionsModel @Inject constructor(
private val wcDisconnectUseCase: WcDisconnectUseCase,
private val wcPairService: WcPairService,
override val dispatchers: CoroutineDispatcherProvider,
paramsContainer: ParamsContainer,
) : Model() {
private val params = paramsContainer.require<ConnectionsComponent.Params>()
val uiState: StateFlow<WcConnectionsState>
field = MutableStateFlow<WcConnectionsState>(getInitialState())
val bottomSheetNavigation: SlotNavigation<WcConnectionsBottomSheetConfig> = SlotNavigation()
@ -58,7 +62,15 @@ internal class WcConnectionsModel @Inject constructor(
private fun listenQrUpdates() {
listenToQrScanningUseCase(SourceType.WALLET_CONNECT)
.getOrElse { emptyFlow() }
.onEach { wcUrl -> wcPairService.pair(WcPairRequest(wcUrl, WcPairRequest.Source.QR)) }
.onEach { wcUrl ->
wcPairService.pair(
WcPairRequest(
userWalletId = params.userWalletId,
uri = wcUrl,
source = WcPairRequest.Source.QR,
),
)
}
.launchIn(modelScope)
}

View file

@ -1,17 +1,16 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.domain.blockaid.models.dapp.CheckDAppResult
import com.tangem.core.ui.extensions.iconResId
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.walletconnect.connections.entity.*
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toImmutableList
internal class WcAppInfoTransformer(
private val dAppSession: WcSessionProposal,
private val onDismiss: () -> Unit,
private val onConnect: () -> Unit,
private val onWalletClick: () -> Unit,
private val userWallet: UserWallet,
private val proposalNetwork: WcSessionProposal.ProposalNetwork,
) : Transformer<WcAppInfoUM> {
@ -24,7 +23,8 @@ internal class WcAppInfoTransformer(
appSubtitle = dAppSession.dAppMetaData.description,
notification = createNotification(dAppSession.securityStatus),
walletName = userWallet.name,
networksInfo = convertNetworksInfo(proposalNetwork),
onWalletClick = onWalletClick,
networksInfo = WcNetworksInfoConverter.convert(proposalNetwork),
connectButtonConfig = WcPrimaryButtonConfig(
showProgress = false,
enabled = proposalNetwork.missingRequired.isEmpty(),
@ -41,26 +41,4 @@ internal class WcAppInfoTransformer(
CheckDAppResult.FAILED_TO_VERIFY -> WcAppInfoSecurityNotification.UnknownDomain
}
}
private fun convertNetworksInfo(proposalNetwork: WcSessionProposal.ProposalNetwork): WcNetworksInfo {
return if (proposalNetwork.missingRequired.isNotEmpty()) {
WcNetworksInfo.MissingRequiredNetworkInfo(
networks = proposalNetwork.missingRequired
.joinToString { it.name },
)
} else {
WcNetworksInfo.ContainsAllRequiredNetworks(
items = (proposalNetwork.required + proposalNetwork.available)
.map {
WcNetworkInfoItem(
id = it.id.value,
icon = it.iconResId,
name = it.name,
symbol = it.currencySymbol,
)
}
.toImmutableList(),
)
}
}
}

View file

@ -0,0 +1,22 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
import com.tangem.utils.transformer.Transformer
internal class WcAppInfoWalletChangedTransformer(
private val selectedUserWallet: UserWallet,
private val proposalNetwork: WcSessionProposal.ProposalNetwork,
) : Transformer<WcAppInfoUM> {
override fun transform(prevState: WcAppInfoUM): WcAppInfoUM {
val contentState = prevState as? WcAppInfoUM.Content ?: return prevState
return contentState.copy(
walletName = selectedUserWallet.name,
networksInfo = WcNetworksInfoConverter.convert(proposalNetwork),
connectButtonConfig = prevState.connectButtonConfig.copy(
enabled = proposalNetwork.missingRequired.isEmpty(),
),
)
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.tangem.core.ui.extensions.iconResId
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem
import com.tangem.features.walletconnect.connections.entity.WcNetworksInfo
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
internal object WcNetworksInfoConverter : Converter<WcSessionProposal.ProposalNetwork, WcNetworksInfo> {
override fun convert(value: WcSessionProposal.ProposalNetwork): WcNetworksInfo {
return if (value.missingRequired.isNotEmpty()) {
WcNetworksInfo.MissingRequiredNetworkInfo(
networks = value.missingRequired
.joinToString { it.name },
)
} else {
WcNetworksInfo.ContainsAllRequiredNetworks(
items = (value.required + value.available)
.map {
WcNetworkInfoItem(
id = it.id.value,
icon = it.iconResId,
name = it.name,
symbol = it.currencySymbol,
)
}
.toImmutableList(),
)
}
}
}

View file

@ -16,7 +16,7 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.walletconnect.components.WcRoutingComponent
import com.tangem.features.walletconnect.connections.components.WcAppInfoContainerComponent
import com.tangem.features.walletconnect.transaction.components.WcSignTransactionComponent
import com.tangem.features.walletconnect.transaction.components.WcSignTransactionContainerComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -55,13 +55,17 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
router = model.innerRouter,
)
return when (config) {
is WcInnerRoute.SignMessage -> WcSignTransactionComponent(
is WcInnerRoute.SignMessage -> WcSignTransactionContainerComponent(
childContext,
params = WcSignTransactionComponent.Params(config.rawRequest),
params = WcSignTransactionContainerComponent.Params(config.rawRequest),
)
is WcInnerRoute.Pair -> WcAppInfoContainerComponent(
childContext,
WcAppInfoContainerComponent.Params(config.request.uri, config.request.source),
WcAppInfoContainerComponent.Params(
userWalletId = config.request.userWalletId,
wcUrl = config.request.uri,
source = config.request.source,
),
)
}
}

View file

@ -220,7 +220,12 @@ private fun WcAppInfoSecondBlock(state: WcAppInfoUM.Content, modifier: Modifier
val itemsModifier = Modifier
.fillMaxWidth()
.padding(TangemTheme.dimens.spacing12)
WalletRowItem(modifier = itemsModifier, walletName = state.walletName)
WalletRowItem(
modifier = Modifier
.clickable(onClick = state.onWalletClick)
.then(itemsModifier),
walletName = state.walletName,
)
HorizontalDivider(thickness = 1.dp, color = TangemTheme.colors.stroke.primary)
SelectNetworksBlock(
networksInfo = state.networksInfo,
@ -565,6 +570,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
appSubtitle = "react-app.walletconnect.com",
notification = WcAppInfoSecurityNotification.SecurityRisk,
walletName = "Tangem 2.0",
onWalletClick = {},
networksInfo = WcNetworksInfo.ContainsAllRequiredNetworks(
items = persistentListOf(
WcNetworkInfoItem(
@ -604,6 +610,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
appSubtitle = "react-app.walletconnect.com",
notification = WcAppInfoSecurityNotification.UnknownDomain,
walletName = "Tangem 2.0",
onWalletClick = {},
networksInfo = WcNetworksInfo.MissingRequiredNetworkInfo(networks = "Solana"),
onDismiss = {},
connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = true, onClick = {}),

Some files were not shown because too many files have changed in this diff Show more