package com.tangem.tap.domain import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager import com.tangem.commands.common.card.CardStatus import com.tangem.commands.common.card.masks.Product import com.tangem.commands.common.network.Result import com.tangem.common.extensions.toHexString import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.config.ConfigManager import com.tangem.tap.domain.extensions.amountToCreateAccount import com.tangem.tap.domain.extensions.isNoAccountError import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.send.redux.AddressPayIdActionUi import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import com.tangem.tap.store import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.math.BigDecimal class TapWalletManager { private val payIdManager = PayIdManager() private val coinMarketCapService = CoinMarketCapService() suspend fun loadWalletData() { val walletManager = store.state.globalState.scanNoteResponse?.walletManager if (walletManager == null) { store.dispatch(WalletAction.LoadWallet.Failure()) return } loadWallet(walletManager) } suspend fun loadPayId() { val result = loadPayIdIfNeeded() result?.let { handlePayIdResult(it) } } suspend fun updateWallet() { val walletManager = store.state.globalState.scanNoteResponse?.walletManager if (walletManager == null) { withContext(Dispatchers.Main) { store.dispatch(WalletAction.UpdateWallet.Failure()) } return } val result = try { walletManager.update() Result.Success(walletManager.wallet) } catch (exception: Exception) { Result.Failure(exception) } withContext(Dispatchers.Main) { when (result) { is Result.Success -> store.dispatch(WalletAction.UpdateWallet.Success(result.data)) is Result.Failure -> store.dispatch(WalletAction.UpdateWallet.Failure(result.error?.localizedMessage)) } } } suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName) { val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet ?: return val currencyList = wallet.getTokens().map { it.symbol }.toMutableList() currencyList.add(wallet.blockchain.currency) val results = mutableListOf?>>() currencyList.forEach { results.add(it to coinMarketCapService.getRate(it, fiatCurrency)) } handleFiatRatesResult(results) } suspend fun onCardScanned(data: ScanNoteResponse, addAnalyticsEvent: Boolean = false) { if (addAnalyticsEvent) { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card) } TapWorkarounds.updateCard(data.card) val configManager = store.state.globalState.configManager if (TapWorkarounds.isStart2Coin) { configManager?.turnOff(ConfigManager.isWalletPayIdEnabled) configManager?.turnOff(ConfigManager.isTopUpEnabled) } else { configManager?.resetToDefault(ConfigManager.isWalletPayIdEnabled) configManager?.resetToDefault(ConfigManager.isTopUpEnabled) } withContext(Dispatchers.Main) { store.dispatch(WalletAction.ResetState) store.dispatch(GlobalAction.SaveScanNoteResponse(data)) store.dispatch(AddressPayIdActionUi.ChangePayIdState(configManager?.config?.isWalletPayIdEnabled ?: false)) if (data.card.cardData?.productMask?.contains(Product.TwinCard) == true) { val secondCardId = TwinsHelper.getTwinsCardId(data.card.cardId) val cardNumber = TwinsHelper.getTwinCardNumber(data.card.cardId) if (secondCardId != null && cardNumber != null) { store.dispatch(WalletAction.TwinsAction.SetTwinCard(secondCardId, cardNumber)) } } loadData(data) } } suspend fun loadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { store.dispatch(WalletAction.CheckIfWarningNeeded) val artworkId = data.verifyResponse?.artworkInfo?.id if (data.walletManager != null) { if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) return@withContext } val config = store.state.globalState.configManager?.config ?: return@withContext store.dispatch(WalletAction.LoadWallet( data.walletManager.wallet, data.verifyResponse?.artworkInfo?.id, config.isTopUpEnabled)) store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) store.dispatch(WalletAction.LoadFiatRate) store.dispatch(WalletAction.LoadPayId) } else if (data.card.status == CardStatus.Empty || data.card.cardData?.productMask?.contains(Product.TwinCard) == true) { store.dispatch(WalletAction.EmptyWallet) store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) } else { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) } } } private suspend fun loadWallet(walletManager: WalletManager) { val result = try { walletManager.update() Result.Success(walletManager.wallet) } catch (exception: Exception) { Result.Failure(exception) } handleUpdateWalletResult(result, walletManager) } private suspend fun handleUpdateWalletResult(result: Result, walletManager: WalletManager) { withContext(Dispatchers.Main) { when (result) { is Result.Success -> store.dispatch(WalletAction.LoadWallet.Success(result.data)) is Result.Failure -> { val error = result.error val blockchain = walletManager.wallet.blockchain if (error != null && blockchain.isNoAccountError(error)) { val token = walletManager.wallet.getFirstToken() val amountToCreateAccount = blockchain.amountToCreateAccount(token) if (amountToCreateAccount != null) { store.dispatch(WalletAction.LoadWallet.NoAccount(amountToCreateAccount.toString())) return@withContext } } store.dispatch(WalletAction.LoadWallet.Failure(result.error?.localizedMessage)) } } } } private suspend fun loadPayIdIfNeeded(): Result? { val scanNoteResponse = store.state.globalState.scanNoteResponse if (store.state.globalState.configManager?.config?.isWalletPayIdEnabled == false || scanNoteResponse?.walletManager?.wallet?.blockchain?.isPayIdSupported() == false) { return null } val cardId = scanNoteResponse?.card?.cardId val publicKey = scanNoteResponse?.card?.cardPublicKey if (cardId == null || publicKey == null) { return null } return payIdManager.getPayId(cardId, publicKey.toHexString()) } private suspend fun handlePayIdResult(result: Result) { withContext(Dispatchers.Main) { when (result) { is Result.Success -> { val config = store.state.globalState.configManager?.config if (config?.isWalletPayIdEnabled == false) { store.dispatch(WalletAction.DisablePayId) return@withContext } val payId = result.data if (payId == null) { store.dispatch(WalletAction.LoadPayId.NotCreated) } else { store.dispatch(WalletAction.LoadPayId.Success(payId)) } } is Result.Failure -> store.dispatch(WalletAction.LoadPayId.Failure) } } } private suspend fun handleFiatRatesResult(results: List?>>) { withContext(Dispatchers.Main) { results.map { when (it.second) { is Result.Success -> { val rate = it.first to (it.second as Result.Success).data store.dispatch(GlobalAction.SetFiatRate(rate)) store.dispatch(WalletAction.LoadFiatRate.Success(rate)) } is Result.Failure -> store.dispatch(WalletAction.LoadFiatRate.Failure) null -> { } } } } } } fun Wallet.getFirstToken(): Token? { return getTokens().toList().getOrNull(0) }