Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-21 00:32:09 +04:00
parent e561cac009
commit c28d80cb18
91 changed files with 178 additions and 5792 deletions

View file

@ -4,39 +4,27 @@ import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.core.analytics.Analytics
import com.tangem.datasource.config.ConfigManager
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.operations.attestation.Attestation
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.walletStores.WalletStoresError
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
import com.tangem.tap.domain.walletconnect2.domain.models.Account
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.disclaimer.createDisclaimer
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.rekotlin.Store
import timber.log.Timber
class TapWalletManager(
private val dispatchers: CoroutineDispatcherProvider = AppCoroutineDispatcherProvider(),
@ -55,15 +43,15 @@ class TapWalletManager(
val walletManagerFactory: WalletManagerFactory
by lazy { WalletManagerFactory(blockchainSdkConfig) }
suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean, sendAnalyticsEvent: Boolean) {
suspend fun onWalletSelected(userWallet: UserWallet, sendAnalyticsEvent: Boolean) {
// If a previous job was running, it gets cancelled before the new one starts,
// ensuring that only one job is active at any given time.
loadUserWalletDataJob = CoroutineScope(dispatchers.io)
.launch { loadUserWalletData(userWallet, refresh, sendAnalyticsEvent) }
.launch { loadUserWalletData(userWallet, sendAnalyticsEvent) }
.also { it.join() }
}
private suspend fun loadUserWalletData(userWallet: UserWallet, refresh: Boolean, sendAnalyticsEvent: Boolean) {
private suspend fun loadUserWalletData(userWallet: UserWallet, sendAnalyticsEvent: Boolean) {
Analytics.setContext(userWallet.scanResponse)
if (sendAnalyticsEvent) {
Analytics.send(Basic.WalletOpened())
@ -78,114 +66,11 @@ class TapWalletManager(
withMainContext {
// Order is important
store.dispatch(DisclaimerAction.SetDisclaimer(card.createDisclaimer()))
store.dispatchWalletAction(action = WalletAction.UserWalletChanged(userWallet))
store.dispatchWalletAction(
action = WalletAction.UpdateCanSaveUserWallets(
canSaveUserWallets = store.state.daggerGraphState.get(DaggerGraphState::walletsRepository)
.shouldSaveUserWalletsSync(),
),
)
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
store.dispatch(WalletConnectAction.ResetState)
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
store.dispatch(WalletConnectAction.RestoreSessions(scanResponse))
store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
store.dispatchWalletAction(action = WalletAction.Warnings.CheckIfNeeded)
}
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) {
val walletFeatureToggles = state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles)
if (!walletFeatureToggles.isRedesignedScreenEnabled) {
dispatch(action = action)
}
}
private fun setupWalletConnectV2(userWallet: 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,
)
}
}
suspend fun loadData(userWallet: UserWallet, refresh: Boolean = false) {
walletStoresManager.fetch(userWallet, refresh)
.doOnSuccess {
Timber.d("Wallet stores fetched for ${userWallet.walletId}")
store.dispatchOnMain(WalletAction.LoadData.Success)
store.state.globalState.topUpController?.loadDataSuccess()
store.dispatchWithMain(WalletAction.Warnings.CheckHashesCount.VerifyOnlineIfNeeded)
val wcInteractor = store.state.daggerGraphState.walletConnectInteractor
wcInteractor?.setUserChains(getAccountsForWc(wcInteractor))
}
.doOnFailure { error ->
val errorAction = when (error) {
is WalletStoresError -> when (error) {
is WalletStoresError.FetchFiatRatesError -> WalletAction.LoadData.Failure(error = null)
is WalletStoresError.UpdateWalletManagerTokensError -> WalletAction.LoadData.Failure(
error = TapError.WalletManager.InternalError(
message = error.cause.localizedMessage ?: error.customMessage,
),
)
is WalletStoresError.WalletManagerNotCreated -> WalletAction.LoadData.Failure(
error = TapError.WalletManager.CreationError,
)
is WalletStoresError.UnknownBlockchain -> WalletAction.LoadData.Failure(
error = TapError.UnknownBlockchain,
)
is WalletStoresError.NoInternetConnection -> WalletAction.LoadData.Failure(
error = TapError.NoInternetConnection,
)
}
else -> WalletAction.LoadData.Failure(error = null)
}
Timber.e(error, "Wallet stores fetching failed for ${userWallet.walletId}")
store.dispatchOnMain(errorAction)
}
}
private suspend fun getAccountsForWc(wcInteractor: WalletConnectInteractor): List<Account> {
val walletManagerToggles = store.state.daggerGraphState
.get(DaggerGraphState::walletFeatureToggles)
val walletManagers = if (walletManagerToggles.isRedesignedScreenEnabled) {
val walletManagerFacade = store.state.daggerGraphState
.get(DaggerGraphState::walletManagersFacade)
val userWallet = userWalletsListManager.selectedUserWalletSync ?: return emptyList()
walletManagerFacade.getStoredWalletManagers(userWallet.walletId)
} else {
store.state.walletState.walletManagers
}
return walletManagers.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

@ -11,10 +11,10 @@ import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.extensions.createAddressesData
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.reducers.createAddressesData
import java.math.BigDecimal
interface WalletStoreBuilder {

View file

@ -1,68 +0,0 @@
package com.tangem.tap.domain.statePrinter
import com.tangem.blockchain.common.Wallet
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.domain.redux.state.StringStateConverter
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.store
import timber.log.Timber
/**
[REDACTED_AUTHOR]
*/
class WalletStateConverter : StringStateConverter<AppState> {
private val converter = MoshiJsonConverter.INSTANCE
override fun convert(stateHolder: AppState): String {
val walletState = stateHolder.walletState
val walletManagers = mutableListOf<Map<String, Any?>>()
walletState.walletManagers.forEach {
val wallet = it.wallet
val walletManagerMap = mutableMapOf<String, Any?>()
walletManagerMap["walletManager"] = mapOf<String, Any>(
"wallet" to convertWallet(it.wallet),
)
walletManagers.add(walletManagerMap)
}
val json = converter.prettyPrint(walletManagers)
return json
}
private fun convertWallet(wallet: Wallet): MutableMap<String, Any> {
val walletMap = mutableMapOf<String, Any>()
val amounts = mutableMapOf<String, Any>()
wallet.amounts.forEach { (type, amount) ->
val amountMap = mapOf<String, Any?>(
"value" to amount.value?.toPlainString(),
"currencySymbol" to amount.currencySymbol,
"decimals" to amount.decimals,
"type" to amount.type::class.java.simpleName,
)
amounts[type::class.java.simpleName] = amountMap
}
val publicKeyMap = mapOf(
"seedKey" to wallet.publicKey.seedKey,
"derivedKey" to wallet.publicKey.derivedKey,
"derivationPath" to wallet.publicKey.derivationPath?.rawPath,
"blockchainKey" to wallet.publicKey.blockchainKey,
)
walletMap["address"] = wallet.address
walletMap["blockchain"] = wallet.blockchain.name
walletMap["curve"] = wallet.blockchain.getSupportedCurves()[0].curve
walletMap["publicKey"] = publicKeyMap
walletMap["amounts"] = amounts
walletMap["addresses"] = wallet.addresses.toString()
return walletMap
}
}
fun printWalletState() {
val stringState = WalletStateConverter().convert(store.state)
Timber.d(stringState)
}