Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-05 16:35:37 +03:00
parent a38bc0322d
commit bf31324d3e
5 changed files with 125 additions and 5 deletions

View file

@ -91,8 +91,12 @@ class TapWalletManager(
store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
store.dispatchWalletAction(action = WalletAction.Warnings.CheckIfNeeded)
}
setupWalletConnectV2(userWallet)
loadData(userWallet = userWallet, refresh = refresh)
val walletFeatureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles)
if (!walletFeatureToggles.isRedesignedScreenEnabled) {
setupWalletConnectV2(userWallet)
loadData(userWallet = userWallet, refresh = refresh)
}
}
private fun Store<AppState>.dispatchWalletAction(action: WalletAction) {

View file

@ -14,6 +14,8 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.walletconnect.WalletConnectActions
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
@ -60,6 +62,28 @@ class WalletConnectMiddleware {
if (DemoHelper.tryHandle(state, action)) return
when (action) {
is WalletConnectActions.New.Initialize -> {
val userWallet = action.userWallet
val cardId = if (userWallet.scanResponse.card.backupStatus?.isActive != true) {
userWallet.cardId
} else { // if wallet has backup, any card from wallet can be used to sign
null
}
scope.launch {
val wcInteractor = store.state.daggerGraphState.walletConnectInteractor ?: return@launch
wcInteractor.startListening(
userWalletId = userWallet.walletId.stringValue,
cardId = cardId,
)
}
}
is WalletConnectActions.New.SetupUserChains -> {
scope.launch {
val userWallet = action.userWallet
val wcInteractor = store.state.daggerGraphState.walletConnectInteractor ?: return@launch
wcInteractor.setUserChains(getAccountsForWc(wcInteractor, userWallet))
}
}
is WalletConnectAction.ResetState -> walletConnectManager = WalletConnectManager()
is WalletConnectAction.RestoreSessions -> {
walletConnectManager.restoreSessions(action.scanResponse)
@ -485,4 +509,22 @@ class WalletConnectMiddleware {
private fun isWalletConnectUri(uri: String): Boolean {
return WalletConnectManager.isCorrectWcUri(uri) || walletConnectInteractor.isWalletConnectUri(uri)
}
private suspend fun getAccountsForWc(wcInteractor: WalletConnectInteractor, userWallet: UserWallet): List<Account> {
val walletManagerFacade = store.state.daggerGraphState
.get(DaggerGraphState::walletManagersFacade)
return walletManagerFacade.getStoredWalletManagers(userWallet.walletId).mapNotNull {
val wallet = it.wallet
val chainId = wcInteractor.blockchainHelper.networkIdToChainIdOrNull(
wallet.blockchain.toNetworkId(),
)
chainId?.let {
Account(
chainId,
wallet.address,
wallet.publicKey.derivationPath?.rawPath,
)
}
}
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.domain.walletconnect
import com.tangem.domain.wallets.models.UserWallet
import org.rekotlin.Action
sealed class WalletConnectActions : Action {
sealed class New {
data class Initialize(val userWallet: UserWallet) : WalletConnectActions()
data class SetupUserChains(val userWallet: UserWallet) : WalletConnectActions()
}
}

View file

@ -77,6 +77,7 @@ interface WalletManagersFacade {
derivationPath: String?,
): WalletManager?
@Deprecated("Will be removed in future")
suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List<WalletManager>
/**

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels
import androidx.lifecycle.*
import androidx.paging.cachedIn
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
import com.tangem.blockchain.common.Blockchain
@ -39,6 +40,7 @@ import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.settings.*
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -48,6 +50,7 @@ import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.walletconnect.WalletConnectActions
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
@ -158,6 +161,7 @@ internal class WalletViewModel @Inject constructor(
private var singleWalletCryptoCurrencyStatus: CryptoCurrencyStatus? = null
private val tokensJobHolder = JobHolder()
private val updateWcJobHolder = JobHolder()
private val marketPriceJobHolder = JobHolder()
private val buttonsJobHolder = JobHolder()
private val notificationsJobHolder = JobHolder()
@ -455,6 +459,7 @@ internal class WalletViewModel @Inject constructor(
* If jobs aren't stopped and wallet is changed then it will update state for the prev wallet.
*/
tokensJobHolder.update(job = null)
updateWcJobHolder.update(job = null)
marketPriceJobHolder.update(job = null)
buttonsJobHolder.update(job = null)
notificationsJobHolder.update(job = null)
@ -851,6 +856,7 @@ internal class WalletViewModel @Inject constructor(
* If jobs aren't stopped and wallet is changed then it will update state for the prev wallet.
*/
tokensJobHolder.update(job = null)
updateWcJobHolder.update(job = null)
marketPriceJobHolder.update(job = null)
buttonsJobHolder.update(job = null)
notificationsJobHolder.update(job = null)
@ -862,17 +868,22 @@ internal class WalletViewModel @Inject constructor(
wallet.isLocked -> {
uiState = stateFactory.getLockedState()
}
wallet.isMultiCurrency -> getMultiCurrencyContent(index)
wallet.isMultiCurrency -> getMultiCurrencyContent(wallet, index)
!wallet.isMultiCurrency -> getSingleCurrencyContent(index)
}
}
private fun getMultiCurrencyContent(walletIndex: Int) {
private fun getMultiCurrencyContent(wallet: UserWallet, walletIndex: Int) {
val state = requireNotNull(uiState as? WalletMultiCurrencyState) {
"Impossible to get a token list updates if state isn't WalletMultiCurrencyState"
}
getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id)
val tokenListFlow = getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id)
.shareIn(viewModelScope, SharingStarted.WhileSubscribed())
initAndSetupWc(tokenListFlow, wallet)
tokenListFlow
.distinctUntilChanged()
.onEach { maybeTokenList ->
uiState = stateFactory.getStateByTokensList(maybeTokenList)
@ -905,6 +916,44 @@ internal class WalletViewModel @Inject constructor(
}
}
private fun initAndSetupWc(tokenListFlow: SharedFlow<Either<TokenListError, TokenList>>, wallet: UserWallet) {
initWalletConnectForWallet(wallet)
tokenListFlow
.filter(::filterLoadedTokenList)
.take(1)
.onEach {
it.onRight {
setupWalletConnectOnWallet(wallet)
}
}
.flowOn(dispatchers.io)
.launchIn(viewModelScope)
.saveIn(updateWcJobHolder)
}
private fun List<CryptoCurrencyStatus>.isAllCurrenciesLoaded(): Boolean {
return !this.any { it.value is CryptoCurrencyStatus.Loading }
}
private fun filterLoadedTokenList(either: Either<TokenListError, TokenList>): Boolean {
return either.fold(
ifRight = { list ->
when (list) {
is TokenList.Ungrouped -> {
list.currencies.isAllCurrenciesLoaded()
}
is TokenList.GroupedByNetwork -> {
list.groups.flatMap { group -> group.currencies }.isAllCurrenciesLoaded()
}
else -> {
false
}
}
},
ifLeft = { false },
)
}
private fun List<CryptoCurrencyStatus>.hasNonZeroWallets(): Boolean {
return any {
val amount = it.value.amount ?: return@any false
@ -1023,6 +1072,18 @@ internal class WalletViewModel @Inject constructor(
)
}
private fun initWalletConnectForWallet(userWallet: UserWallet) {
reduxStateHolder.dispatch(
WalletConnectActions.New.Initialize(userWallet = userWallet),
)
}
private fun setupWalletConnectOnWallet(userWallet: UserWallet) {
reduxStateHolder.dispatch(
WalletConnectActions.New.SetupUserChains(userWallet = userWallet),
)
}
private fun getWallet(index: Int): UserWallet {
return requireNotNull(
value = wallets.getOrNull(index),