Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-24 10:04:41 +07:00
parent 8af3e349a7
commit 3cdfcb6ebc
13 changed files with 149 additions and 37 deletions

View file

@ -12,6 +12,8 @@ android {
dependencies { dependencies {
/* Project - Domain */ /* Project - Domain */
implementation(projects.domain.account)
implementation(projects.domain.account.status)
implementation(projects.domain.walletConnect) implementation(projects.domain.walletConnect)
implementation(projects.domain.walletConnect.models) implementation(projects.domain.walletConnect.models)
implementation(projects.domain.transaction) implementation(projects.domain.transaction)

View file

@ -21,6 +21,9 @@ import com.tangem.data.walletconnect.utils.WcScope
import com.tangem.datasource.di.SdkMoshi import com.tangem.datasource.di.SdkMoshi
import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.datasource.local.walletconnect.WalletConnectStore import com.tangem.datasource.local.walletconnect.WalletConnectStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.supplier.MultiAccountListSupplier
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.walletconnect.WcPairService import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.WcRequestService import com.tangem.domain.walletconnect.WcRequestService
@ -92,6 +95,7 @@ internal object WalletConnectDataModule {
getWallets: GetWalletsUseCase, getWallets: GetWalletsUseCase,
wcNetworksConverter: WcNetworksConverter, wcNetworksConverter: WcNetworksConverter,
analytics: AnalyticsEventHandler, analytics: AnalyticsEventHandler,
accountsFeatureToggles: AccountsFeatureToggles,
wcScope: WcScope, wcScope: WcScope,
): DefaultWcSessionsManager { ): DefaultWcSessionsManager {
return DefaultWcSessionsManager( return DefaultWcSessionsManager(
@ -100,6 +104,7 @@ internal object WalletConnectDataModule {
getWallets = getWallets, getWallets = getWallets,
wcNetworksConverter = wcNetworksConverter, wcNetworksConverter = wcNetworksConverter,
analytics = analytics, analytics = analytics,
accountsFeatureToggles = accountsFeatureToggles,
scope = wcScope, scope = wcScope,
) )
} }
@ -179,22 +184,24 @@ internal object WalletConnectDataModule {
namespaceConverters: Set<@JvmSuppressWildcards WcNamespaceConverter>, namespaceConverters: Set<@JvmSuppressWildcards WcNamespaceConverter>,
walletManagersFacade: WalletManagersFacade, walletManagersFacade: WalletManagersFacade,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
): WcNetworksConverter = WcNetworksConverter( ): WcNetworksConverter = WcNetworksConverter(
namespaceConverters = namespaceConverters, namespaceConverters = namespaceConverters,
walletManagersFacade = walletManagersFacade, walletManagersFacade = walletManagersFacade,
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
) )
@Provides @Provides
@Singleton @Singleton
fun associateNetworksDelegate( fun associateNetworksDelegate(
namespaceConverters: Set<@JvmSuppressWildcards WcNamespaceConverter>, networksConverter: WcNetworksConverter,
multiAccountListSupplier: MultiAccountListSupplier,
getWallets: GetWalletsUseCase, getWallets: GetWalletsUseCase,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
): AssociateNetworksDelegate = AssociateNetworksDelegate( ): AssociateNetworksDelegate = AssociateNetworksDelegate(
namespaceConverters = namespaceConverters,
getWallets = getWallets, getWallets = getWallets,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, networksConverter = networksConverter,
multiAccountListSupplier = multiAccountListSupplier,
) )
@Provides @Provides

View file

@ -2,22 +2,25 @@ package com.tangem.data.walletconnect.pair
import com.reown.walletkit.client.Wallet import com.reown.walletkit.client.Wallet
import com.reown.walletkit.client.Wallet.Model.Namespace import com.reown.walletkit.client.Wallet.Model.Namespace
import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.data.walletconnect.utils.WcNetworksConverter
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.account.supplier.MultiAccountListSupplier
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.walletconnect.model.WcPairError import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcSessionProposal.ProposalNetwork import com.tangem.domain.walletconnect.model.WcSessionProposal.ProposalNetwork
import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
internal class AssociateNetworksDelegate( internal class AssociateNetworksDelegate(
private val namespaceConverters: Set<WcNamespaceConverter>, private val networksConverter: WcNetworksConverter,
private val multiAccountListSupplier: MultiAccountListSupplier,
private val getWallets: GetWalletsUseCase, private val getWallets: GetWalletsUseCase,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
) { ) {
@Throws(WcPairError.UnsupportedBlockchains::class) @Throws(WcPairError.UnsupportedBlockchains::class)
@ -29,18 +32,48 @@ internal class AssociateNetworksDelegate(
.subtract(requiredNamespaces) .subtract(requiredNamespaces)
return userWallets.associateWith { wallet -> return userWallets.associateWith { wallet ->
mapNetworksForWallet(wallet, requiredNamespaces, optionalNamespaces, sessionProposal) mapNetworksForPortfolio(wallet, null, requiredNamespaces, optionalNamespaces, sessionProposal)
} }
} }
@Throws(WcPairError.UnsupportedBlockchains::class)
suspend fun associateAccounts(sessionProposal: Wallet.Model.SessionProposal): Map<AccountId, ProposalNetwork> {
val userWallets = getWallets.invokeSync()
.filter { it.isMultiCurrency && !it.isLocked }
val requiredNamespaces: Set<String> = sessionProposal.requiredNamespaces.setOfChainId()
val optionalNamespaces: Set<String> = sessionProposal.optionalNamespaces.setOfChainId()
// remove duplicates
.subtract(requiredNamespaces)
val allAccounts = multiAccountListSupplier.invoke()
.filter { it.isNotEmpty() }
.first()
.filter { accountList -> userWallets.any { accountList.userWalletId == it.walletId } }
.map { accountList -> accountList to userWallets.first { it.walletId == accountList.userWalletId } }
return allAccounts
.map { (accountList, wallet) -> accountList.accounts.map { account -> account to wallet } }
.flatten()
.associate { (account, wallet) ->
account.accountId to mapNetworksForPortfolio(
wallet = wallet,
account = account,
requiredNamespaces = requiredNamespaces,
optionalNamespaces = optionalNamespaces,
sessionProposal = sessionProposal,
)
}
}
@Suppress("ComplexCondition") @Suppress("ComplexCondition")
private suspend fun mapNetworksForWallet( private suspend fun mapNetworksForPortfolio(
wallet: UserWallet, wallet: UserWallet,
account: Account?,
requiredNamespaces: Set<String>, requiredNamespaces: Set<String>,
optionalNamespaces: Set<String>, optionalNamespaces: Set<String>,
sessionProposal: Wallet.Model.SessionProposal, sessionProposal: Wallet.Model.SessionProposal,
): ProposalNetwork { ): ProposalNetwork {
val walletNetworks = getWalletNetworks(userWalletId = wallet.walletId) val portfolioNetworks = account?.let { getAccountNetworks(it.accountId) }
?: getWalletNetworks(userWalletId = wallet.walletId)
val unknownRequired = mutableSetOf<String>() val unknownRequired = mutableSetOf<String>()
val unknownOptional = mutableSetOf<String>() val unknownOptional = mutableSetOf<String>()
@ -50,28 +83,28 @@ internal class AssociateNetworksDelegate(
val notAdded = mutableSetOf<Network>() val notAdded = mutableSetOf<Network>()
requiredNamespaces.forEach { chainId -> requiredNamespaces.forEach { chainId ->
val wcNetwork = namespaceConverters.firstNotNullOfOrNull { it.toNetwork(chainId, wallet) } val wcNetwork = networksConverter.createNetwork(chainId, wallet)
if (wcNetwork == null) { if (wcNetwork == null) {
unknownRequired.add(missingNetworkName(chainId)) unknownRequired.add(missingNetworkName(chainId))
return@forEach return@forEach
} }
val walletNetwork = walletNetworks.find { network -> wcNetwork.rawId == network.rawId } val portfolioNetwork = portfolioNetworks.find { network -> wcNetwork.rawId == network.rawId }
if (walletNetwork == null) { if (portfolioNetwork == null) {
missingRequired.add(wcNetwork) missingRequired.add(wcNetwork)
} else { } else {
required.add(walletNetwork) required.add(portfolioNetwork)
} }
} }
optionalNamespaces.forEach { chainId -> optionalNamespaces.forEach { chainId ->
val wcNetwork = namespaceConverters.firstNotNullOfOrNull { it.toNetwork(chainId, wallet) } val wcNetwork = networksConverter.createNetwork(chainId, wallet)
if (wcNetwork == null) { if (wcNetwork == null) {
unknownOptional.add(missingNetworkName(chainId)) unknownOptional.add(missingNetworkName(chainId))
return@forEach return@forEach
} }
val walletNetwork = walletNetworks.find { network -> wcNetwork.rawId == network.rawId } val portfolioNetwork = portfolioNetworks.find { network -> wcNetwork.rawId == network.rawId }
if (walletNetwork != null) { if (portfolioNetwork != null) {
available.add(walletNetwork) available.add(portfolioNetwork)
} else { } else {
notAdded.add(wcNetwork) notAdded.add(wcNetwork)
} }
@ -88,16 +121,18 @@ internal class AssociateNetworksDelegate(
required = required, required = required,
available = available, available = available,
notAdded = notAdded, notAdded = notAdded,
account = account,
) )
} }
private suspend fun getWalletNetworks(userWalletId: UserWalletId): List<Network> { private suspend fun getWalletNetworks(userWalletId: UserWalletId): List<Network> {
return multiWalletCryptoCurrenciesSupplier.getSyncOrNull( return networksConverter.getWalletNetworks(userWalletId)
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId = userWalletId), // flatten all derivation
) .distinctBy { it.rawId }
.orEmpty() }
.filterIsInstance<CryptoCurrency.Coin>()
.map(CryptoCurrency.Coin::network) private suspend fun getAccountNetworks(accountId: AccountId): List<Network> {
return networksConverter.getAccountNetworks(accountId)
// flatten all derivation // flatten all derivation
.distinctBy { it.rawId } .distinctBy { it.rawId }
} }

View file

@ -10,6 +10,7 @@ import com.reown.walletkit.client.Wallet
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.walletconnect.utils.WC_TAG import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.data.walletconnect.utils.getDappOriginUrl import com.tangem.data.walletconnect.utils.getDappOriginUrl
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.blockaid.BlockAidVerifier import com.tangem.domain.blockaid.BlockAidVerifier
import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.model.* import com.tangem.domain.walletconnect.model.*
@ -34,6 +35,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
private val sdkDelegate: WcPairSdkDelegate, private val sdkDelegate: WcPairSdkDelegate,
private val blockAidVerifier: BlockAidVerifier, private val blockAidVerifier: BlockAidVerifier,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val accountsFeatureToggles: AccountsFeatureToggles,
@Assisted private val pairRequest: WcPairRequest, @Assisted private val pairRequest: WcPairRequest,
) : WcPairUseCase { ) : WcPairUseCase {
@ -111,6 +113,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
val sessionDTO = WcSessionDTO( val sessionDTO = WcSessionDTO(
topic = "", topic = "",
walletId = sessionForApprove.wallet.walletId, walletId = sessionForApprove.wallet.walletId,
accountId = sessionForApprove.account?.accountId,
url = sdkVerifyContext.getDappOriginUrl(), url = sdkVerifyContext.getDappOriginUrl(),
securityStatus = proposalState.dAppSession.securityStatus, securityStatus = proposalState.dAppSession.securityStatus,
connectingTime = connectingTime, connectingTime = connectingTime,
@ -196,6 +199,11 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
verifyContext: Wallet.Model.VerifyContext, verifyContext: Wallet.Model.VerifyContext,
): Either<WcPairError, WcPairState.Proposal> = runCatching { ): Either<WcPairError, WcPairState.Proposal> = runCatching {
val proposalNetwork = associateNetworksDelegate.associate(sessionProposal) val proposalNetwork = associateNetworksDelegate.associate(sessionProposal)
val proposalAccountNetwork = if (accountsFeatureToggles.isFeatureEnabled) {
associateNetworksDelegate.associateAccounts(sessionProposal)
} else {
null
}
val verificationInfo = when { val verificationInfo = when {
verifyContext.validation == Wallet.Model.Validation.INVALID -> CheckDAppResult.UNSAFE verifyContext.validation == Wallet.Model.Validation.INVALID -> CheckDAppResult.UNSAFE
verifyContext.isScam == true -> CheckDAppResult.UNSAFE verifyContext.isScam == true -> CheckDAppResult.UNSAFE
@ -204,7 +212,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
CheckDAppResult.FAILED_TO_VERIFY CheckDAppResult.FAILED_TO_VERIFY
} }
} }
val requestedNetworks = proposalNetwork val requestedNetworks = (proposalAccountNetwork ?: proposalNetwork)
.values.map { it.available.plus(it.required) }.flatten().toSet() .values.map { it.available.plus(it.required) }.flatten().toSet()
analytics.send( analytics.send(
WcAnalyticEvents.PairRequested( WcAnalyticEvents.PairRequested(
@ -225,6 +233,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
dAppMetaData = appMetaData, dAppMetaData = appMetaData,
proposalNetwork = proposalNetwork, proposalNetwork = proposalNetwork,
securityStatus = verificationInfo, securityStatus = verificationInfo,
proposalAccountNetwork = proposalAccountNetwork,
) )
WcPairState.Proposal(dAppSession) WcPairState.Proposal(dAppSession)
}.fold( }.fold(

View file

@ -8,6 +8,7 @@ import com.reown.walletkit.client.WalletKit
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.walletconnect.utils.* import com.tangem.data.walletconnect.utils.*
import com.tangem.datasource.local.walletconnect.WalletConnectStore import com.tangem.datasource.local.walletconnect.WalletConnectStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.WcSession
@ -31,6 +32,7 @@ internal class DefaultWcSessionsManager(
private val dispatchers: CoroutineDispatcherProvider, private val dispatchers: CoroutineDispatcherProvider,
private val wcNetworksConverter: WcNetworksConverter, private val wcNetworksConverter: WcNetworksConverter,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val scope: WcScope, private val scope: WcScope,
) : WcSessionsManager, WcSdkObserver { ) : WcSessionsManager, WcSdkObserver {
@ -90,7 +92,11 @@ internal class DefaultWcSessionsManager(
val wcSessions = savedPending.plus(inStore).mapNotNull { storeSession -> val wcSessions = savedPending.plus(inStore).mapNotNull { storeSession ->
val wallet = wallets.find { it.walletId == storeSession.walletId } ?: return@mapNotNull null val wallet = wallets.find { it.walletId == storeSession.walletId } ?: return@mapNotNull null
val sdkSession = inSdk.find { it.topic == storeSession.topic } ?: return@mapNotNull null val sdkSession = inSdk.find { it.topic == storeSession.topic } ?: return@mapNotNull null
val networks = wcNetworksConverter.findWalletNetworks(wallet, sdkSession) val account = storeSession.accountId?.let { wcNetworksConverter.getAccount(it) }
if (accountsFeatureToggles.isFeatureEnabled && account == null) {
return@mapNotNull null
}
val networks = wcNetworksConverter.findWalletNetworks(wallet, account, sdkSession)
val originUrl = storeSession.url ?: sdkSession.metaData?.url ?: "" val originUrl = storeSession.url ?: sdkSession.metaData?.url ?: ""
WcSession( WcSession(
wallet = wallet, wallet = wallet,
@ -102,6 +108,7 @@ internal class DefaultWcSessionsManager(
), ),
securityStatus = storeSession.securityStatus, securityStatus = storeSession.securityStatus,
networks = networks, networks = networks,
account = account,
connectingTime = storeSession.connectingTime, connectingTime = storeSession.connectingTime,
showWalletInfo = wallets.size > 1, showWalletInfo = wallets.size > 1,
) )

View file

@ -3,6 +3,11 @@ package com.tangem.data.walletconnect.utils
import com.reown.walletkit.client.Wallet import com.reown.walletkit.client.Wallet
import com.tangem.data.common.currency.isCustomCoin import com.tangem.data.common.currency.isCustomCoin
import com.tangem.data.walletconnect.model.CAIP10 import com.tangem.data.walletconnect.model.CAIP10
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
@ -18,6 +23,7 @@ import javax.inject.Inject
internal class WcNetworksConverter @Inject constructor( internal class WcNetworksConverter @Inject constructor(
private val namespaceConverters: Set<WcNamespaceConverter>, private val namespaceConverters: Set<WcNamespaceConverter>,
private val walletManagersFacade: WalletManagersFacade, private val walletManagersFacade: WalletManagersFacade,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
) { ) {
@ -67,8 +73,13 @@ internal class WcNetworksConverter @Inject constructor(
return allCoinNetwork return allCoinNetwork
} }
suspend fun findWalletNetworks(wallet: UserWallet, sdkSession: Wallet.Model.Session): Set<Network> { suspend fun findWalletNetworks(
val walletNetworks = getWalletNetworks(wallet.walletId) wallet: UserWallet,
account: Account?,
sdkSession: Wallet.Model.Session,
): Set<Network> {
val portfolioNetworks = account?.let { getAccountNetworks(it.accountId) }
?: getWalletNetworks(wallet.walletId)
val existNetworks = sdkSession.namespaces.values val existNetworks = sdkSession.namespaces.values
.map { it.accounts }.flatten().toSet() .map { it.accounts }.flatten().toSet()
.mapNotNull { CAIP10.fromRaw(it) } .mapNotNull { CAIP10.fromRaw(it) }
@ -76,7 +87,7 @@ internal class WcNetworksConverter @Inject constructor(
val blockchain = namespaceConverters val blockchain = namespaceConverters
.firstNotNullOfOrNull { it.toBlockchain(caip10.chainId) } .firstNotNullOfOrNull { it.toBlockchain(caip10.chainId) }
?: return@mapNotNullTo null ?: return@mapNotNullTo null
walletNetworks portfolioNetworks
// find all derivation // find all derivation
.filter { it.rawId == blockchain.id } .filter { it.rawId == blockchain.id }
// find equal address // find equal address
@ -89,18 +100,38 @@ internal class WcNetworksConverter @Inject constructor(
return existNetworks return existNetworks
} }
suspend fun getAccount(accountId: AccountId): Account? {
return getAccountStatus(accountId)?.account
}
suspend fun convertNetworksForApprove(sessionForApprove: WcSessionApprove): List<Network> { suspend fun convertNetworksForApprove(sessionForApprove: WcSessionApprove): List<Network> {
val walletNetworks = getWalletNetworks(sessionForApprove.wallet.walletId) val portfolioNetworks = sessionForApprove.account?.let { getAccountNetworks(it.accountId) }
?: getWalletNetworks(sessionForApprove.wallet.walletId)
return sessionForApprove.network return sessionForApprove.network
.map { network -> walletNetworks.filter { walletNetwork -> walletNetwork.rawId == network.rawId } } .map { network -> portfolioNetworks.filter { walletNetwork -> walletNetwork.rawId == network.rawId } }
.flatten() .flatten()
} }
private suspend fun getWalletNetworks(userWalletId: UserWalletId): List<Network> { suspend fun getWalletNetworks(userWalletId: UserWalletId): List<Network> {
return multiWalletCryptoCurrenciesSupplier.getSyncOrNull( return multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId = userWalletId), params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId = userWalletId),
) )
.orEmpty() .orEmpty()
.filterIsInstance<CryptoCurrency.Coin>().map(CryptoCurrency.Coin::network) .filterIsInstance<CryptoCurrency.Coin>().map(CryptoCurrency.Coin::network)
} }
private suspend fun getAccountStatus(accountId: AccountId): AccountStatus? {
return singleAccountStatusListSupplier.getSyncOrNull(
SingleAccountStatusListProducer.Params(accountId.userWalletId),
)?.accountStatuses?.find { it.account.accountId == accountId }
}
suspend fun getAccountNetworks(accountId: AccountId): List<Network> {
return getAccountStatus(accountId)
?.flattenCurrencies()
?.map { it.currency }
?.filterIsInstance<CryptoCurrency.Coin>()
?.map(CryptoCurrency.Coin::network)
?: emptyList()
}
} }

View file

@ -14,9 +14,13 @@ import com.tangem.data.walletconnect.pair.CaipNamespaceDelegate
import com.tangem.data.walletconnect.pair.DefaultWcPairUseCase import com.tangem.data.walletconnect.pair.DefaultWcPairUseCase
import com.tangem.data.walletconnect.pair.WcPairSdkDelegate import com.tangem.data.walletconnect.pair.WcPairSdkDelegate
import com.tangem.data.walletconnect.utils.WcSdkSessionConverter import com.tangem.data.walletconnect.utils.WcSdkSessionConverter
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.blockaid.BlockAidVerifier import com.tangem.domain.blockaid.BlockAidVerifier
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.walletconnect.model.* import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.walletconnect.usecase.pair.WcPairState import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.coVerifyOrder import io.mockk.coVerifyOrder
@ -33,6 +37,7 @@ internal class DefaultWcPairUseCaseTest {
private val analytics: AnalyticsEventHandler = mockk<AnalyticsEventHandler>(relaxed = true) private val analytics: AnalyticsEventHandler = mockk<AnalyticsEventHandler>(relaxed = true)
private val sdkDelegate: WcPairSdkDelegate = mockk<WcPairSdkDelegate>() private val sdkDelegate: WcPairSdkDelegate = mockk<WcPairSdkDelegate>()
private val blockAidVerifier: BlockAidVerifier = mockk<BlockAidVerifier>() private val blockAidVerifier: BlockAidVerifier = mockk<BlockAidVerifier>()
private val accountsFeatureToggles = mockk<AccountsFeatureToggles>()
private val url = "testUrl" private val url = "testUrl"
private val source = WcPairRequest.Source.QR private val source = WcPairRequest.Source.QR
@ -70,6 +75,7 @@ internal class DefaultWcPairUseCaseTest {
get() = WcSessionApprove( get() = WcSessionApprove(
wallet = MockUserWalletFactory.create(), wallet = MockUserWalletFactory.create(),
network = listOf(), network = listOf(),
account = null,
) )
private val sdkApprove: Wallet.Params.SessionApprove private val sdkApprove: Wallet.Params.SessionApprove
@ -102,6 +108,7 @@ internal class DefaultWcPairUseCaseTest {
networks = setOf(), networks = setOf(),
connectingTime = null, connectingTime = null,
showWalletInfo = false, showWalletInfo = false,
account = null,
) )
private fun useCaseFactory() = DefaultWcPairUseCase( private fun useCaseFactory() = DefaultWcPairUseCase(
@ -110,12 +117,14 @@ internal class DefaultWcPairUseCaseTest {
sdkDelegate = sdkDelegate, sdkDelegate = sdkDelegate,
blockAidVerifier = blockAidVerifier, blockAidVerifier = blockAidVerifier,
analytics = analytics, analytics = analytics,
accountsFeatureToggles = accountsFeatureToggles,
pairRequest = WcPairRequest(userWalletId = UserWalletId(""), uri = url, source = source), pairRequest = WcPairRequest(userWalletId = UserWalletId(""), uri = url, source = source),
) )
@Before @Before
fun setup() { fun setup() {
coEvery { associateNetworksDelegate.associate(sdkProposal) } returns mapOf() coEvery { associateNetworksDelegate.associate(sdkProposal) } returns mapOf()
coEvery { accountsFeatureToggles.isFeatureEnabled } returns false
coEvery { coEvery {
caipNamespaceDelegate.associate( caipNamespaceDelegate.associate(
sessionProposal = sdkProposal, sessionProposal = sdkProposal,

View file

@ -58,6 +58,7 @@ internal class WcSignUseCaseDelegateTest {
session = WcSession( session = WcSession(
wallet = MockUserWalletFactory.create(), wallet = MockUserWalletFactory.create(),
networks = setOf(), networks = setOf(),
account = null,
securityStatus = CheckDAppResult.FAILED_TO_VERIFY, securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
connectingTime = 0L, connectingTime = 0L,
sdkModel = WcSdkSession( sdkModel = WcSdkSession(

View file

@ -1,12 +1,14 @@
package com.tangem.domain.walletconnect.model package com.tangem.domain.walletconnect.model
import com.domain.blockaid.models.dapp.CheckDAppResult import com.domain.blockaid.models.dapp.CheckDAppResult
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSession import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSession
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
data class WcSession( data class WcSession(
val wallet: UserWallet, val wallet: UserWallet,
val account: Account?,
val networks: Set<Network>, val networks: Set<Network>,
val sdkModel: WcSdkSession, val sdkModel: WcSdkSession,
val securityStatus: CheckDAppResult, val securityStatus: CheckDAppResult,

View file

@ -1,9 +1,11 @@
package com.tangem.domain.walletconnect.model package com.tangem.domain.walletconnect.model
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
data class WcSessionApprove( data class WcSessionApprove(
val wallet: UserWallet, val wallet: UserWallet,
val account: Account?,
val network: List<Network>, val network: List<Network>,
) )

View file

@ -2,12 +2,14 @@ package com.tangem.domain.walletconnect.model
import com.domain.blockaid.models.dapp.CheckDAppResult import com.domain.blockaid.models.dapp.CheckDAppResult
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class WcSessionDTO( data class WcSessionDTO(
val topic: String, val topic: String,
val walletId: UserWalletId, val walletId: UserWalletId,
val accountId: AccountId? = null,
val url: String?, val url: String?,
val securityStatus: CheckDAppResult = CheckDAppResult.FAILED_TO_VERIFY, val securityStatus: CheckDAppResult = CheckDAppResult.FAILED_TO_VERIFY,
val connectingTime: Long? = null, val connectingTime: Long? = null,

View file

@ -1,18 +1,22 @@
package com.tangem.domain.walletconnect.model package com.tangem.domain.walletconnect.model
import com.domain.blockaid.models.dapp.CheckDAppResult import com.domain.blockaid.models.dapp.CheckDAppResult
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData
data class WcSessionProposal( data class WcSessionProposal(
val dAppMetaData: WcAppMetaData, val dAppMetaData: WcAppMetaData,
val proposalNetwork: Map<UserWallet, ProposalNetwork>, val proposalNetwork: Map<UserWallet, ProposalNetwork>,
val proposalAccountNetwork: Map<AccountId, ProposalNetwork>?,
val securityStatus: CheckDAppResult, val securityStatus: CheckDAppResult,
) { ) {
data class ProposalNetwork( data class ProposalNetwork(
val wallet: UserWallet, val wallet: UserWallet,
val account: Account?,
val missingRequired: Set<Network>, val missingRequired: Set<Network>,
val required: Set<Network>, val required: Set<Network>,
val available: Set<Network>, val available: Set<Network>,

View file

@ -186,6 +186,7 @@ internal class WcPairModel @Inject constructor(
WcSessionApprove( WcSessionApprove(
wallet = selectedUserWalletFlow.value, wallet = selectedUserWalletFlow.value,
network = enabledAvailableNetworks + proposalNetwork.required, network = enabledAvailableNetworks + proposalNetwork.required,
account = null, // todo account
), ),
) )
} }