Updated on 2026-08-14
This commit is contained in:
parent
8af3e349a7
commit
3cdfcb6ebc
13 changed files with 149 additions and 37 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
)
|
)
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue