package com.tangem.tap.domain import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager import com.tangem.commands.CardStatus import com.tangem.commands.common.network.Result import com.tangem.common.extensions.toHexString import com.tangem.tap.TapConfig 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.tasks.ScanNoteResponse import com.tangem.tap.features.wallet.redux.PayIdState 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 } updateWallet(walletManager) } suspend fun loadPayId() { val result = loadPayIdIfNeeded() result?.let { handlePayIdResult(it) } } suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName) { val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet val blockchainCurrency = wallet?.blockchain?.currency val tokenCurrency = wallet?.token?.symbol val blockchainRate = blockchainCurrency?.let { coinMarketCapService.getRate(it, fiatCurrency) } val tokenRate = tokenCurrency?.let { coinMarketCapService.getRate(it, fiatCurrency) } val results = mutableListOf?>>() if (blockchainCurrency != null) results.add(blockchainCurrency to blockchainRate) if (tokenCurrency != null) results.add(tokenCurrency to tokenRate) handleFiatRatesResult(results) } suspend fun onCardScanned(data: ScanNoteResponse) { withContext(Dispatchers.Main) { store.dispatch(GlobalAction.SaveScanNoteResponse(data)) val artworkId = data.verifyResponse?.artworkInfo?.id if (data.walletManager != null) { if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) return@withContext } store.dispatch(WalletAction.LoadWallet) store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) store.dispatch(WalletAction.LoadFiatRate) store.dispatch(WalletAction.LoadPayId) } else if (data.card.status == CardStatus.Empty) { 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 updateWallet(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 amountToCreateAccount = blockchain.amountToCreateAccount() if (amountToCreateAccount != null) { store.dispatch(WalletAction.LoadWallet.NoAccount(amountToCreateAccount)) return@withContext } } store.dispatch(WalletAction.LoadWallet.Failure(result.error?.localizedMessage)) } } } } private suspend fun loadPayIdIfNeeded(): Result? { val scanNoteResponse = store.state.globalState.scanNoteResponse if (!TapConfig.usePayId || store.state.walletState.payIdData.payIdState == PayIdState.Disabled || 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 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 -> { } } } } } }