Updated on 2026-08-14

This commit is contained in:
Tangem 2022-09-17 15:04:50 +04:00
parent 4d73b7725e
commit d8be65f5f7
41 changed files with 983 additions and 767 deletions

View file

@ -11,7 +11,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
@ -108,11 +107,9 @@ class DetailsMiddleware {
}
scope.launch {
val result = tangemSdkManager.resetToFactorySettings(card)
withContext(Dispatchers.Main) {
when (result) {
is CompletionResult.Success -> {
currenciesRepository.removeCurrencies(card.cardId)
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home))
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
@ -123,7 +120,6 @@ class DetailsMiddleware {
)
}
}
}
}
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.card.Card
import com.tangem.common.extensions.guard
import com.tangem.domain.common.ScanResponse
@ -12,12 +13,11 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.domain.walletconnect.BnbHelper
import com.tangem.tap.domain.walletconnect.WalletConnectManager
import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils
import com.tangem.tap.domain.walletconnect.WcWalletManagerFactory
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.scope
@ -189,15 +189,10 @@ class WalletConnectMiddleware {
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork))
return
}
val factory = WcWalletManagerFactory(
factory = store.state.globalState.tapWalletManager.walletManagerFactory,
currenciesRepository = currenciesRepository,
)
val walletState = store.state.walletState
val walletManager = factory.getWalletManager(
val walletManager = getWalletManager(
wallet = action.session.wallet,
blockchain = blockchain,
walletState = walletState,
walletState = store.state.walletState,
).guard {
store.dispatchOnMain(
GlobalAction.ShowDialog(
@ -230,20 +225,10 @@ class WalletConnectMiddleware {
handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain)
}
private suspend fun getAvailableBlockchains(card: Card, walletState: WalletState): List<Blockchain> {
return if (walletState.cardId == card.cardId) {
walletState.currencies.filter {
it.isBlockchain() && !it.isCustomCurrency(card.derivationStyle) && it.blockchain.isEvm()
}.map { it.blockchain }
} else {
currenciesRepository
.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed)
.filter {
it.blockchain.isEvm() &&
it.derivationPath == it.blockchain.derivationPath(card.derivationStyle)?.rawPath
}
.map { it.blockchain }
}
private fun getAvailableBlockchains(card: Card, walletState: WalletState): List<Blockchain> {
return walletState.currencies.filter {
it.isBlockchain() && !it.isCustomCurrency(card.derivationStyle) && it.blockchain.isEvm()
}.map { it.blockchain }
}
private suspend fun prepareWalletManager(
@ -253,11 +238,7 @@ class WalletConnectMiddleware {
session: WalletConnectSession,
walletConnectManager: WalletConnectManager,
) {
val factory = WcWalletManagerFactory(
factory = store.state.globalState.tapWalletManager.walletManagerFactory,
currenciesRepository = currenciesRepository,
)
val walletManager = factory.getWalletManager(scanResponse, blockchain, walletState).guard {
val walletManager = getWalletManager(session.wallet, blockchain, walletState).guard {
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(session.session))
store.dispatchOnMain(
GlobalAction.ShowDialog(
@ -306,17 +287,27 @@ class WalletConnectMiddleware {
NewWcSessionData(session = updatedSession, scanResponse = scanResponse, blockchain = blockchain),
),
)
val blockchains = if (blockchain.isEvm()) getAvailableBlockchains(card, walletState) else emptyList()
store.dispatch(
GlobalAction.ShowDialog(
WalletConnectDialog.ApproveWcSession(session = updatedSession, networks = blockchains),
),
)
}
scope.launch {
val blockchains = if (blockchain.isEvm()) getAvailableBlockchains(card, walletState) else emptyList()
withMainContext {
store.dispatch(
GlobalAction.ShowDialog(
WalletConnectDialog.ApproveWcSession(session = updatedSession, networks = blockchains),
),
)
}
private fun getWalletManager(
wallet: WalletForSession, blockchain: Blockchain, walletState: WalletState,
): WalletManager? {
val blockchainToMake = if (blockchain == Blockchain.Ethereum && wallet.isTestNet) {
Blockchain.EthereumTestnet
} else {
blockchain
}
val blockchainNetwork = BlockchainNetwork(
blockchain = blockchainToMake,
derivationPath = wallet.derivationPath?.rawPath,
tokens = emptyList(),
)
return walletState.getWalletManager(blockchainNetwork)
}
}

View file

@ -78,30 +78,35 @@ private fun handleReadCard() {
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
} else {
changeButtonState(ButtonState.PROGRESS)
store.dispatch(GlobalAction.ScanCard(onSuccess = { scanResponse ->
store.state.globalState.tapWalletManager.updateConfigManager(scanResponse)
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
store.dispatch(
GlobalAction.ScanCard(
onSuccess = { scanResponse ->
store.state.globalState.tapWalletManager.updateConfigManager(scanResponse)
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
store.dispatch(GlobalAction.Onboarding.Start(scanResponse))
navigateTo(navigateTo)
} else {
scope.launch {
store.onCardScanned(scanResponse)
withMainContext {
if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly))
navigateTo(AppScreen.OnboardingTwins)
} else {
navigateTo(AppScreen.Wallet, null)
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
store.dispatch(GlobalAction.Onboarding.Start(scanResponse))
navigateTo(navigateTo)
} else {
scope.launch {
withMainContext {
if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly))
navigateTo(AppScreen.OnboardingTwins)
} else {
navigateTo(AppScreen.Wallet, null)
}
}
store.onCardScanned(scanResponse)
}
}
}
}
}, onFailure = {
changeButtonState(ButtonState.ENABLED)
}))
},
onFailure = {
changeButtonState(ButtonState.ENABLED)
},
),
)
}
}

View file

@ -18,7 +18,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.twins.TwinCardsManager
@ -271,7 +270,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
CreateTwinWalletMode.RecreateWallet -> {
currenciesRepository.removeCurrencies(scanResponse.card.cardId)
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
}

View file

@ -94,8 +94,8 @@ private fun handleWalletAction(action: Action) {
store.dispatchOnMain(
WalletAction.MultiWallet.SaveCurrencies(
blockchainNetworks = blockchainNetworks,
cardId = result.data.card.cardId
)
card = result.data.card,
),
)
onboardingManager.activationStarted(updatedResponse.card.cardId)
store.dispatch(OnboardingWalletAction.ProceedBackup)
@ -273,15 +273,6 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
backupService.proceedBackup { result ->
when (result) {
is CompletionResult.Success -> {
val blockchainNetworks = listOf(
BlockchainNetwork(Blockchain.Bitcoin, result.data),
BlockchainNetwork(Blockchain.Ethereum, result.data)
)
store.dispatchOnMain(
WalletAction.MultiWallet.SaveCurrencies(
blockchainNetworks = blockchainNetworks, cardId = result.data.cardId
)
)
if (backupService.currentState == BackupService.State.Finished) {
store.dispatchOnMain(BackupAction.FinishBackup)
} else {

View file

@ -19,7 +19,8 @@ import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction
import com.tangem.domain.redux.domainStore
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.tap.*
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.assetReader
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
@ -32,6 +33,9 @@ import com.tangem.tap.domain.tokens.LoadAvailableCoinsService
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Middleware
@ -77,7 +81,7 @@ class TokensMiddleware {
val loadCoinsService = LoadAvailableCoinsService(
store.state.domainNetworks.tangemTechService,
currenciesRepository
assetReader,
)
scope.launch {
@ -285,22 +289,28 @@ class TokensMiddleware {
else -> DerivationParams.Custom(derivationPath)
}
}
val walletManager = factory.makeWalletManagerForApp(
scanResponse = scanResponse,
blockchain = currency.blockchain,
derivationParams = derivationParams
derivationParams = derivationParams,
) ?: return@mapNotNull null
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
WalletAction.MultiWallet.AddBlockchain(blockchainNetwork, walletManager)
WalletAction.MultiWallet.AddBlockchain(
blockchain = blockchainNetwork,
walletManager = walletManager,
save = true,
)
}
is Currency.Token -> {
val rawDerivationPath = currency.derivationPath
?: currency.blockchain.derivationPath(derivationStyle)?.rawPath
val blockchainNetwork =
BlockchainNetwork(currency.blockchain, rawDerivationPath, emptyList())
WalletAction.MultiWallet.AddToken(currency.token, blockchainNetwork)
BlockchainNetwork(currency.blockchain, rawDerivationPath, listOf(currency.token))
WalletAction.MultiWallet.AddToken(
token = currency.token,
blockchain = blockchainNetwork,
save = true,
)
}
}
}

View file

@ -1,14 +1,18 @@
package com.tangem.tap.features.wallet.models
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.toCoinId
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.network.api.tangemTech.TokenResponse
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
sealed interface Currency {
val coinId: String?
get() = when (this) {
is Blockchain -> blockchain.toCoinId()
@ -17,24 +21,28 @@ sealed interface Currency {
val blockchain: com.tangem.blockchain.common.Blockchain
val currencySymbol: CryptoCurrencyName
val derivationPath: String?
val currencyName: String
get() = when (this) {
is Blockchain -> blockchain.fullName
is Token -> token.name
}
val decimals
get() = when (this) {
is Blockchain -> blockchain.decimals()
is Token -> token.decimals
}
data class Token(
val token: com.tangem.blockchain.common.Token,
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
override val derivationPath: String?,
) : Currency {
override val currencySymbol = token.symbol
}
data class Blockchain(
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
override val derivationPath: String?,
) : Currency {
override val currencySymbol: CryptoCurrencyName = blockchain.currency
}
@ -48,24 +56,35 @@ sealed interface Currency {
}
fun isBlockchain(): Boolean = this is Blockchain
fun isToken(): Boolean = this is Token
fun toTokenResponse(): TokenResponse {
return TokenResponse(
id = coinId ?: "",
networkId = blockchain.toNetworkId(),
derivationPath = derivationPath ?: DERIVATION_PATH_RAW_VALUE,
name = currencyName,
symbol = currencySymbol,
decimals = decimals,
contractAddress = if (this is Token) token.contractAddress else null,
)
}
companion object {
private const val DERIVATION_PATH_RAW_VALUE = "m/44/0'/0/0"
fun fromBlockchainNetwork(
blockchainNetwork: BlockchainNetwork,
token: com.tangem.blockchain.common.Token? = null
token: com.tangem.blockchain.common.Token? = null,
): Currency {
return if (token != null) {
Token(
token = token,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
derivationPath = blockchainNetwork.derivationPath,
)
} else {
Blockchain(
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
derivationPath = blockchainNetwork.derivationPath,
)
}
}
@ -74,7 +93,7 @@ sealed interface Currency {
return when (customCurrency) {
is CustomCurrency.CustomBlockchain -> Blockchain(
blockchain = customCurrency.network,
derivationPath = customCurrency.derivationPath?.rawPath
derivationPath = customCurrency.derivationPath?.rawPath,
)
is CustomCurrency.CustomToken -> Token(
token = customCurrency.token,
@ -88,8 +107,53 @@ sealed interface Currency {
return Token(
token = tokenWithBlockchain.token,
blockchain = tokenWithBlockchain.blockchain,
derivationPath = null
derivationPath = null,
)
}
fun fromTokenResponse(tokenResponse: TokenResponse): Currency {
val derivationPath = if (tokenResponse.derivationPath == DERIVATION_PATH_RAW_VALUE) {
null
} else {
tokenResponse.derivationPath
}
return when {
tokenResponse.contractAddress != null -> Token(
com.tangem.blockchain.common.Token(
name = tokenResponse.name,
symbol = tokenResponse.symbol,
contractAddress = tokenResponse.contractAddress!!,
decimals = tokenResponse.decimals,
id = tokenResponse.id,
),
blockchain = com.tangem.blockchain.common.Blockchain.fromNetworkId(tokenResponse.networkId)!!,
derivationPath = derivationPath,
)
else -> Blockchain(
blockchain = com.tangem.blockchain.common.Blockchain.fromNetworkId(tokenResponse.networkId)!!,
derivationPath = derivationPath,
)
}
}
}
}
}
fun BlockchainNetwork.toCurrencies(): List<Currency> {
val blockchain = Currency.fromBlockchainNetwork(this)
val tokens = this.tokens.map { Currency.fromBlockchainNetwork(this, it) }
return listOf(blockchain) + tokens
}
fun List<BlockchainNetwork>.toCurrencies(): List<Currency> {
return flatMap { it.toCurrencies() }
}
fun List<Currency>.toBlockchainNetworks(): List<BlockchainNetwork> {
return this.filter { it.isBlockchain() }.map { BlockchainNetwork(it.blockchain, it.derivationPath, getTokens(it)) }
}
private fun List<Currency>.getTokens(currency: Currency): List<Token> {
return this
.filter { it.isToken() && it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath }
.mapNotNull { if (it is Currency.Token) it.token else null }
}

View file

@ -31,7 +31,6 @@ sealed class WalletAction : Action {
data class Failure(val error: TapError) : WalletAction()
}
data class LoadWallet(
val blockchain: BlockchainNetwork? = null,
val walletManager: WalletManager? = null
@ -54,22 +53,21 @@ sealed class WalletAction : Action {
data class AddBlockchain(
val blockchain: BlockchainNetwork,
val walletManager: WalletManager?
val walletManager: WalletManager?,
val save: Boolean,
) : MultiWallet()
data class AddBlockchains(
val blockchains: List<BlockchainNetwork>, val walletManagers: List<WalletManager>
val blockchains: List<BlockchainNetwork>, val walletManagers: List<WalletManager>, val save: Boolean,
) : MultiWallet()
data class AddTokens(val tokens: List<Token>, val blockchain: BlockchainNetwork) :
data class AddTokens(val tokens: List<Token>, val blockchain: BlockchainNetwork, val save: Boolean) :
MultiWallet()
data class AddToken(val token: Token, val blockchain: BlockchainNetwork) : MultiWallet()
data class AddToken(val token: Token, val blockchain: BlockchainNetwork, val save: Boolean) : MultiWallet()
data class SaveCurrencies(
val blockchainNetworks: List<BlockchainNetwork>, val cardId: String? = null
val blockchainNetworks: List<BlockchainNetwork>, val card: Card? = null,
) : MultiWallet()
// object FindTokensInUse : MultiWallet()
// object FindBlockchainsInUse : MultiWallet()
data class TokenLoaded(
val amount: Amount,
@ -82,14 +80,15 @@ sealed class WalletAction : Action {
data class TryToRemoveWallet(val currency: Currency) : MultiWallet()
data class RemoveWallet(
val currency: Currency,
val fromScreen: AppScreen
val fromScreen: AppScreen,
) : MultiWallet()
data class SetPrimaryBlockchain(val blockchain: Blockchain) : MultiWallet()
data class SetPrimaryToken(val token: Token) : MultiWallet()
data class ShowWalletBackupWarning(val show: Boolean) : MultiWallet()
object BackupWallet : MultiWallet()
data class AddMissingDerivations(val blockchains: List<BlockchainNetwork>) : MultiWallet()
object ScanToGetDerivations : MultiWallet()
}
sealed class Warnings : WalletAction() {

View file

@ -19,9 +19,6 @@ import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.models.WalletRent
import com.tangem.tap.features.wallet.models.WalletWarning
import com.tangem.tap.features.wallet.models.hasPendingTransactions
import com.tangem.tap.features.wallet.models.hasSendableAmounts
import com.tangem.tap.features.wallet.models.isSendableAmount
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
import com.tangem.tap.features.wallet.ui.BalanceStatus
@ -47,6 +44,7 @@ data class WalletState(
val isTestnet: Boolean = false,
val totalBalance: TotalBalance? = null,
val showBackupWarning: Boolean = false,
val missingDerivations: List<BlockchainNetwork> = emptyList(),
) : StateType {
// if you do not delegate - the application crashes on startup,
@ -131,32 +129,6 @@ data class WalletState(
return walletsData.find { it.currency == selectedCurrency }
}
fun canBeRemoved(walletData: WalletData?): Boolean {
if (walletData == null) return false
if (!isPrimaryCurrency(walletData)) {
val walletManager = getWalletManager(walletData.currency)
?: return true
if (walletData.currency is Currency.Blockchain &&
walletManager.cardTokens.isNotEmpty()
) {
return false
}
val wallet = walletManager.wallet
return when (walletData.currency) {
is Currency.Blockchain -> !wallet.hasPendingTransactions() && !wallet.hasSendableAmounts()
is Currency.Token -> {
val token = walletData.currency.token
!wallet.hasPendingTransactions(token) && !wallet.isSendableAmount(token)
}
}
}
return false
}
private fun isPrimaryCurrency(walletData: WalletData): Boolean {
return (walletData.currency is Currency.Blockchain &&
walletData.currency.blockchain == store.state.walletState.primaryBlockchain)

View file

@ -4,6 +4,7 @@ import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.guard
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.safeUpdate
@ -11,18 +12,20 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.toCurrencies
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.features.wallet.redux.reducers.toWallet
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.userTokensRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -44,41 +47,47 @@ class MultiWalletMiddleware {
}
}
is WalletAction.MultiWallet.AddToken -> {
addTokens(listOf(action.token), action.blockchain, walletState, globalState)
addTokens(listOf(action.token), action.blockchain, walletState, globalState, action.save)
}
is WalletAction.MultiWallet.AddTokens -> {
addTokens(action.tokens, action.blockchain, walletState, globalState)
addTokens(action.tokens, action.blockchain, walletState, globalState, action.save)
}
is WalletAction.MultiWallet.AddBlockchain -> {
action.walletManager?.let {
handleAddingWalletManagers(globalState, listOf(action.walletManager))
}
val currencies: List<Currency> =
(walletState?.currencies ?: emptyList()) + action.blockchain.toCurrencies()
globalState.scanResponse?.let {
currenciesRepository.saveUpdatedCurrency(
cardId = it.card.cardId,
blockchainNetwork = action.blockchain
)
if (action.save && globalState.scanResponse != null) {
scope.launch {
userTokensRepository.saveUserTokens(
card = globalState.scanResponse.card,
tokens = currencies,
)
}
}
store.dispatch(
WalletAction.LoadFiatRate(
coinsList = listOf(
Currency.Blockchain(
action.blockchain.blockchain,
action.blockchain.derivationPath
)
)
)
action.blockchain.derivationPath,
),
),
),
)
store.dispatch(
WalletAction.LoadWallet(
action.blockchain, action.walletManager
)
action.blockchain, action.walletManager,
),
)
}
is WalletAction.MultiWallet.SaveCurrencies -> {
val cardId = action.cardId ?: globalState.scanResponse?.card?.cardId ?: return
currenciesRepository.saveCurrencies(cardId, action.blockchainNetworks)
val card = action.card ?: globalState.scanResponse?.card ?: return
scope.launch { userTokensRepository.saveUserTokens(card, action.blockchainNetworks.toCurrencies()) }
}
is WalletAction.MultiWallet.TryToRemoveWallet -> {
val currency = action.currency
@ -89,54 +98,44 @@ class MultiWalletMiddleware {
}
if (currency.isBlockchain() && walletManager.cardTokens.isNotEmpty()) {
store.dispatchDialogShow(WalletDialog.TokensAreLinkedDialog(
currencyTitle = currency.currencyName,
currencySymbol = currency.currencySymbol
))
store.dispatchDialogShow(
WalletDialog.TokensAreLinkedDialog(
currencyTitle = currency.currencyName,
currencySymbol = currency.currencySymbol,
),
)
} else {
store.dispatchDialogShow(WalletDialog.RemoveWalletDialog(
currencyTitle = currency.currencyName,
onOk = {
store.dispatch(WalletAction.MultiWallet.RemoveWallet(
currency = currency,
fromScreen = AppScreen.WalletDetails
))
store.dispatch(NavigationAction.PopBackTo())
}
))
store.dispatchDialogShow(
WalletDialog.RemoveWalletDialog(
currencyTitle = currency.currencyName,
onOk = {
store.dispatch(
WalletAction.MultiWallet.RemoveWallet(
currency = currency,
fromScreen = AppScreen.WalletDetails,
),
)
store.dispatch(NavigationAction.PopBackTo())
},
),
)
}
}
is WalletAction.MultiWallet.RemoveWallet -> {
val currency = action.currency
val cardId = globalState.scanResponse?.card?.cardId.guard {
store.dispatchErrorNotification(TapError.UnsupportedState("cardId is NULL"))
val card = globalState.scanResponse?.card.guard {
store.dispatchErrorNotification(TapError.UnsupportedState("card is NULL"))
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
return
}
when (currency) {
is Currency.Blockchain -> {
currenciesRepository.removeBlockchain(
cardId = cardId,
blockchainNetwork = BlockchainNetwork(
blockchain = currency.blockchain,
derivationPath = currency.derivationPath,
tokens = emptyList()
)
)
}
is Currency.Token -> {
val walletManager = walletState?.getWalletManager(currency)
if (walletManager != null) {
walletManager.removeToken(currency.token)
currenciesRepository.removeToken(
cardId = cardId,
token = currency.token,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
)
}
}
var currencies = walletState?.currencies ?: emptyList()
currencies = currencies.filterNot { it == currency }
if (currency.isBlockchain()) {
currencies
.filter { it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath }
}
scope.launch { userTokensRepository.saveUserTokens(card, currencies) }
if (action.fromScreen == AppScreen.AddTokens) {
store.dispatch(WalletAction.MultiWallet.SelectWallet(null))
}
@ -148,94 +147,9 @@ class MultiWalletMiddleware {
store.dispatch(GlobalAction.Onboarding.Start(it, fromHomeScreen = false))
}
}
// is WalletAction.MultiWallet.FindBlockchainsInUse -> {
// val scanResponse = globalState.scanResponse ?: return
// if (scanResponse.supportsHdWallet()) return
//
// val cardFirmware = scanResponse.card.firmwareVersion
// val blockchains = currenciesRepository.getBlockchains(cardFirmware)
// .filterNot { walletState?.blockchains?.contains(it) == true }
// .map { BlockchainNetwork(it, null, emptyList()) }
// val walletManagers =
// tapWalletManager.walletManagerFactory.makeWalletManagersForApp(
// scanResponse,
// blockchains
// )
//
// scope.launch {
// walletManagers.map { walletManager ->
// async(Dispatchers.IO) {
// walletManager.safeUpdate()
// val wallet = walletManager.wallet
// val coinAmount = wallet.amounts[AmountType.Coin]?.value
// if (coinAmount != null && !coinAmount.isZero()) {
// scope.launch(Dispatchers.Main) {
// val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
// if (walletState?.getWalletData(blockchainNetwork) == null) {
// store.dispatch(WalletAction.MultiWallet.AddBlockchain(
// blockchainNetwork, walletManager
// ))
// store.dispatch(WalletAction.LoadWallet.Success(
// wallet = wallet,
// blockchain = blockchainNetwork
// ))
// }
// }
// }
// }
// }
// }
// }
// is WalletAction.MultiWallet.FindTokensInUse -> {
// val scanResponse = globalState.scanResponse ?: return
// if (scanResponse.supportsHdWallet()) return
//
// val walletFactory = tapWalletManager.walletManagerFactory
// val card = scanResponse.card
//
// val walletManager = walletState?.getWalletManager(
// Currency.Blockchain(Blockchain.Ethereum, null)
// )
// ?: walletFactory.makeWalletManagerForApp(
// scanResponse,
// Currency.Blockchain(Blockchain.Ethereum, null)
// )
//
// val tokenFinder = walletManager as? TokenFinder ?: return
// scope.launch {
// val result = tokenFinder.findTokens()
//
// withContext(Dispatchers.Main) {
// when (result) {
// is Result.Success -> {
// if (result.data.isNotEmpty()) {
// val blockchainNetwork = BlockchainNetwork(
// walletManager.wallet.blockchain,
// walletManager.wallet.publicKey.derivationPath?.rawPath,
// walletManager.cardTokens.toList()
// )
// currenciesRepository.saveUpdatedCurrency(
// card.cardId,
// blockchainNetwork
// )
// store.dispatch(
// WalletAction.MultiWallet.AddBlockchain(
// blockchainNetwork,
// walletManager
// )
// )
// store.dispatch(
// WalletAction.MultiWallet.AddTokens(
// walletManager.cardTokens.toList(),
// blockchainNetwork
// )
// )
// }
// }
// }
// }
// }
// }
is WalletAction.MultiWallet.ScanToGetDerivations -> {
store.dispatch(WalletAction.Scan)
}
}
}
@ -249,7 +163,7 @@ class MultiWalletMiddleware {
private fun handleAddingWalletManagers(
globalState: GlobalState,
walletManagers: List<WalletManager>
walletManagers: List<WalletManager>,
) {
globalState.feedbackManager?.infoHolder?.setWalletsInfo(walletManagers)
if (globalState.scanResponse?.isDemoCard() == true) {
@ -259,50 +173,59 @@ class MultiWalletMiddleware {
private fun addTokens(
tokens: List<Token>, blockchainNetwork: BlockchainNetwork,
walletState: WalletState?, globalState: GlobalState?
walletState: WalletState?, globalState: GlobalState?,
save: Boolean,
) {
if (tokens.isEmpty()) return
val scanResponse = globalState?.scanResponse ?: return
val wmFactory = globalState.tapWalletManager.walletManagerFactory
val walletState = walletState ?: return
val walletManager = walletState.getWalletManager(blockchainNetwork)?.also {
if (save) {
val wallets = tokens.mapNotNull { token -> token.toWallet(walletState, blockchainNetwork) }
val currencies = walletState.updateWalletsData(wallets).currencies
scope.launch { userTokensRepository.saveUserTokens(scanResponse.card, currencies) }
}
} ?: wmFactory.makeWalletManagerForApp(scanResponse, blockchainNetwork)?.also {
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchainNetwork.updateTokens(tokens), it, save))
}
val walletManager = walletState?.getWalletManager(blockchainNetwork)
?: wmFactory.makeWalletManagerForApp(scanResponse, blockchainNetwork)?.also {
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchainNetwork, it))
} ?: return
store.dispatch(WalletAction.LoadFiatRate(coinsList = tokens.map { token ->
Currency.Token(
token, blockchainNetwork.blockchain, blockchainNetwork.derivationPath
)
}))
if (tokens.isNotEmpty()) walletManager.addTokens(tokens)
currenciesRepository.saveUpdatedCurrency(
cardId = scanResponse.card.cardId,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
store.dispatch(
WalletAction.LoadFiatRate(
coinsList = tokens.map { token ->
Currency.Token(
token, blockchainNetwork.blockchain, blockchainNetwork.derivationPath,
)
},
),
)
if (tokens.isNotEmpty()) walletManager?.addTokens(tokens)
scope.launch {
when (val result = walletManager.safeUpdate()) {
is com.tangem.common.services.Result.Success -> {
val wallet = result.data
wallet.getTokens()
.filter { tokens.contains(it) }
.mapNotNull { token ->
wallet.getTokenAmount(token)?.let { Pair(token, it) }
}
.forEach {
withContext(Dispatchers.Main) {
store.dispatch(
WalletAction.MultiWallet.TokenLoaded(
it.second,
it.first,
blockchainNetwork
)
)
val result = walletManager?.safeUpdate()
withMainContext {
when (result) {
is com.tangem.common.services.Result.Success -> {
val wallet = result.data
wallet.getTokens()
.filter { tokens.contains(it) }
.mapNotNull { token ->
wallet.getTokenAmount(token)?.let { Pair(token, it) }
}
}
.forEach {
withContext(Dispatchers.Main) {
store.dispatch(
WalletAction.MultiWallet.TokenLoaded(
it.second,
it.first,
blockchainNetwork,
),
)
}
}
}
else -> {}
}
is com.tangem.common.services.Result.Failure -> {}
}
}
}

View file

@ -108,10 +108,13 @@ class WalletMiddleware {
val coinAmount = action.wallet.amounts[AmountType.Coin]?.value
if (coinAmount != null && !coinAmount.isZero()) {
if (walletState.getWalletData(action.blockchain) == null) {
store.dispatch(WalletAction.MultiWallet.AddBlockchain(
action.blockchain,
null
))
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
action.blockchain,
null,
true,
),
)
store.dispatch(WalletAction.LoadWallet.Success(
action.wallet,
action.blockchain

View file

@ -34,11 +34,10 @@ class MultiWalletReducer {
val walletManager = action.walletManagers.firstOrNull {
it.wallet.blockchain == blockchain.blockchain &&
(it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath)
} ?: return@mapNotNull null
val wallet = walletManager.wallet
}
val wallet = walletManager?.wallet
val cardToken = if (!state.isMultiwalletAllowed) {
wallet.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
wallet?.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
} else {
null
}
@ -168,9 +167,11 @@ class MultiWalletReducer {
state.copy(primaryToken = action.token)
is WalletAction.MultiWallet.SaveCurrencies -> state
is WalletAction.MultiWallet.ShowWalletBackupWarning -> state.copy(
showBackupWarning = action.show
showBackupWarning = action.show,
)
is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(missingDerivations = action.blockchains)
is WalletAction.MultiWallet.BackupWallet -> state
is WalletAction.MultiWallet.ScanToGetDerivations -> state
}
}

View file

@ -145,18 +145,19 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
BalanceStatus.Loading
}
if (action.blockchain == null) {
val wallets = newState.wallets.map {
it.copy(
walletsData = it.walletsData.map { walletData ->
val wallets = newState.wallets.map { walletStore ->
walletStore.copy(
walletsData = walletStore.walletsData.map { walletData ->
walletData.copy(
currencyData = walletData.currencyData.copy(
status = balanceStatus,
status =
if (walletStore.walletManager != null) balanceStatus else BalanceStatus.Unreachable,
currency = walletData.currencyData.currency,
currencySymbol = walletData.currencyData.currencySymbol,
),
mainButton = WalletMainButton.SendButton(false),
)
}
},
)
}
newState = newState.copy(

View file

@ -12,7 +12,7 @@ import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.tokens.CurrenciesRepository
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.ProgressState
@ -76,13 +76,14 @@ class MultiWalletView : WalletView() {
handleTotalBalance(binding, state.totalBalance)
handleBackupWarning(binding, state.showBackupWarning)
handleRescanWarning(binding, state.missingDerivations.isNotEmpty())
walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken)
binding.btnAddToken.setOnClickListener {
val card = store.state.globalState.scanResponse!!.card
store.dispatch(
TokensAction.LoadCurrencies(
supportedBlockchains = currenciesRepository.getBlockchains(
supportedBlockchains = CurrenciesRepository.getBlockchains(
card.firmwareVersion,
card.isTestCard,
),
@ -111,6 +112,16 @@ class MultiWalletView : WalletView() {
}
}
private fun handleRescanWarning(
binding: FragmentWalletBinding,
showRescanWarning: Boolean,
) = with(binding.lWalletRescanWarning) {
root.isVisible = showRescanWarning
root.setOnClickListener {
store.dispatch(WalletAction.MultiWallet.ScanToGetDerivations)
}
}
private fun handleTotalBalance(
binding: FragmentWalletBinding,
totalBalance: TotalBalance?,