diff --git a/app/build.gradle b/app/build.gradle index 76faa09bc5..44a13aa801 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,10 +75,13 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - - implementation 'com.tangem:blockchain:1.166.0' - implementation 'com.tangem:core:1.109.0' - implementation 'com.tangem:sdk:1.109.0' +// TODO: change to prod dependencies when they are available +// implementation 'com.tangem:blockchain:1.164.0' +// implementation 'com.tangem:core:1.109.0' +// implementation 'com.tangem:sdk:1.109.0' + implementation 'blockchain-sdk-kotlin:blockchain:tangem-20210416.151414-8' + implementation 'com.tangem:tangem-core:tangem-20210414.185224-2' + implementation 'com.tangem:tangem-sdk:tangem-20210414.185231-2' // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index bfc866a06c..7b7e74d399 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -25,16 +25,6 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.RestoreAppCurrency.Success -> { globalState.copy(appCurrency = action.appCurrency) } - is GlobalAction.UpdateWalletSignedHashes -> { - val card = globalState.scanNoteResponse?.card?.copy( - walletSignedHashes = action.walletSignedHashes - ) - if (card != null) { - globalState.copy(scanNoteResponse = globalState.scanNoteResponse.copy(card = card)) - } else { - globalState - } - } is GlobalAction.SetConfigManager -> { globalState.copy(configManager = action.configManager) } diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 77d26702ab..e6524859c6 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -2,10 +2,16 @@ package com.tangem.tap.domain import androidx.activity.ComponentActivity import com.tangem.* -import com.tangem.commands.* +import com.tangem.commands.CommandResponse +import com.tangem.commands.PinType +import com.tangem.commands.SetPinCommand +import com.tangem.commands.SetPinResponse import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.CardType +import com.tangem.commands.wallet.PurgeWalletCommand +import com.tangem.commands.wallet.PurgeWalletResponse import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.calculateSha256 import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tap.common.analytics.AnalyticsEvent @@ -32,13 +38,15 @@ class TangemSdkManager(val activity: ComponentActivity) { initialMessage = Message(activity.getString(R.string.initial_message_scan_header))) } - suspend fun createWallet(cardId: String?): CompletionResult { + suspend fun createWallet(cardId: String?): CompletionResult { return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask(), cardId, initialMessage = Message(activity.getString(R.string.initial_message_create_wallet_body))) } suspend fun eraseWallet(cardId: String?): CompletionResult { - return runTaskAsyncReturnOnMain(PurgeWalletCommand(), cardId, + return runTaskAsyncReturnOnMain(PurgeWalletCommand( + TangemSdkConstants.getDefaultWalletIndex()), + cardId, initialMessage = Message(activity.getString(R.string.initial_message_purge_wallet_body))) } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 497c7dde68..e2beba423d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -11,8 +11,7 @@ import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.configurable.config.ConfigManager -import com.tangem.tap.domain.extensions.amountToCreateAccount -import com.tangem.tap.domain.extensions.isNoAccountError +import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.TwinsHelper @@ -102,8 +101,10 @@ class TapWalletManager { )) } } - if (data.walletManager?.wallet?.blockchain == Blockchain.Ethereum || - data.walletManager?.wallet?.blockchain == Blockchain.EthereumTestnet) { + + val blockchain = data.card.getBlockchain() + if (blockchain == Blockchain.Ethereum || + blockchain == Blockchain.EthereumTestnet) { store.dispatch(TokensAction.LoadCardTokens) } loadData(data) @@ -112,10 +113,11 @@ class TapWalletManager { private fun updateConfigManager(data: ScanNoteResponse) { val configManager = store.state.globalState.configManager + val blockchain = data.card.getBlockchain() if (TapWorkarounds.isStart2Coin) { configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) configManager?.turnOff(ConfigManager.isTopUpEnabled) - } else if (data.walletManager?.wallet?.blockchain == Blockchain.Bitcoin + } else if (blockchain == Blockchain.Bitcoin || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) { configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled) configManager?.resetToDefault(ConfigManager.isTopUpEnabled) @@ -128,59 +130,77 @@ class TapWalletManager { suspend fun loadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { val artworkId = data.verifyResponse?.artworkInfo?.id - if (data.walletManager != null) { - val config = store.state.globalState.configManager?.config ?: return@withContext - - val primaryWalletManager = data.walletManager - val primaryBlockchain = data.walletManager.wallet.blockchain - val primaryTokenSymbol = data.card.cardData?.tokenSymbol - val primaryToken = primaryWalletManager.presetTokens.toList() - .firstOrNull { it.symbol == primaryTokenSymbol } - - store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(primaryBlockchain)) - if (primaryToken != null) { - store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) + when { + data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) + store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) } - if (data.card.isMultiwalletAllowed) { - loadMultiWalletData(data.card, primaryBlockchain, primaryWalletManager) - } else { - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) + data.card.getStatus() == CardStatus.Empty || + (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + store.dispatch(WalletAction.EmptyWallet) + store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) + } + else -> { + val config = store.state.globalState.configManager?.config ?: return@withContext + + val blockchain = data.card.getBlockchain() + val primaryWalletManager = if (blockchain != null) { + walletManagerFactory.makeWalletManagerForApp(data.card, blockchain) + } else { + null + } + + if (blockchain != null && primaryWalletManager != null) { + val primaryToken = data.card.getToken() + + store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) + if (primaryToken != null) { + primaryWalletManager.addToken(primaryToken) + store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) + } + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, primaryWalletManager) + } else { + store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) + store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))) + } + + } else { + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, null) + } + } + store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id)) + store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) + store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) + store.dispatch(WalletAction.LoadFiatRate()) } - store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id)) - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) - store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) - store.dispatch(WalletAction.LoadFiatRate()) - } else if (data.card.status == CardStatus.Empty || data.card.isTwinCard()) { - 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)) } store.dispatch(WalletAction.Warnings.CheckIfNeeded) } } private fun loadMultiWalletData( - card: Card, primaryBlockchain: Blockchain, primaryWalletManager: WalletManager + card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager? ) { - val presetTokens = primaryWalletManager.presetTokens.toList() + val presetTokens = primaryWalletManager?.presetTokens?.toList() ?: emptyList() val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId) if (savedCurrencies == null) { - store.dispatch(WalletAction.MultiWallet.SaveCurrencies( - CardCurrencies( - blockchains = listOf(primaryBlockchain), tokens = presetTokens - ))) - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) - store.dispatch(WalletAction.MultiWallet.AddTokens(presetTokens)) + if (primaryBlockchain != null && primaryWalletManager != null) { + store.dispatch(WalletAction.MultiWallet.SaveCurrencies( + CardCurrencies( + blockchains = listOf(primaryBlockchain), tokens = presetTokens + ))) + store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) + store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) + store.dispatch(WalletAction.MultiWallet.AddTokens(presetTokens)) + } store.dispatch(WalletAction.MultiWallet.FindBlockchainsInUse(card, walletManagerFactory)) store.dispatch(WalletAction.MultiWallet.FindTokensInUse) } else { - val blockchains = listOf(primaryBlockchain) + savedCurrencies.blockchains - val walletManagers = walletManagerFactory.makeWalletManagers(card, blockchains) + val blockchains = listOfNotNull(primaryBlockchain) + savedCurrencies.blockchains + val walletManagers = walletManagerFactory.makeWalletManagersForApp(card, blockchains) val tokens = presetTokens + savedCurrencies.tokens store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers)) @@ -192,18 +212,23 @@ class TapWalletManager { suspend fun reloadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { - if (data.walletManager != null) { - if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) - return@withContext + when { + data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) + } + data.card.getStatus() == CardStatus.Empty || + (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + store.dispatch(WalletAction.EmptyWallet) + } + else -> { + 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(config.isTopUpEnabled)) + store.dispatch(WalletAction.LoadFiatRate()) } - val config = store.state.globalState.configManager?.config ?: return@withContext - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) - store.dispatch(WalletAction.LoadFiatRate()) - } else if (data.card.status == CardStatus.Empty || data.card.isTwinCard()) { - store.dispatch(WalletAction.EmptyWallet) - } else { - store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index eb1e716da7..afcac855d5 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -32,7 +32,7 @@ object TapWorkarounds { "0030", "0031", "0035" - ) // Tangem tags + ) private val excludedIssuers = listOf( "TTM BANK" @@ -43,5 +43,6 @@ val Card.isMultiwalletAllowed: Boolean get() { return cardData?.productMask?.contains(Product.TwinCard) != true && !TapWorkarounds.isStart2Coin - && this.curve == EllipticCurve.Secp256k1 + && (this.firmwareVersion.major >= 4 || + this.getWallets().getOrNull(0)?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/BlockchainExtensions.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt similarity index 60% rename from app/src/main/java/com/tangem/tap/domain/extensions/BlockchainExtensions.kt rename to app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt index 00ad0c17ea..4f55b5b86e 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/BlockchainExtensions.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt @@ -2,6 +2,7 @@ package com.tangem.tap.domain.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.commands.common.card.EllipticCurve import org.stellar.sdk.requests.ErrorResponse import java.math.BigDecimal @@ -28,4 +29,15 @@ fun Blockchain.minimalAmount(): BigDecimal { return 1.toBigDecimal().movePointLeft(decimals()) } +fun Blockchain.getCurve(): EllipticCurve? { + return when (this) { + Blockchain.Unknown -> null + Blockchain.Bitcoin, Blockchain.BitcoinTestnet, Blockchain.BitcoinCash, Blockchain.Litecoin, + Blockchain.Ducatus, Blockchain.Ethereum, Blockchain.EthereumTestnet, Blockchain.RSK, + Blockchain.Tezos, Blockchain.XRP, Blockchain.Binance, Blockchain.BinanceTestnet -> + EllipticCurve.Secp256k1 + Blockchain.Cardano, Blockchain.CardanoShelley, Blockchain.Stellar -> EllipticCurve.Ed25519 + } +} + private const val NODL = "NODL" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt new file mode 100644 index 0000000000..4dd07f00f5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -0,0 +1,44 @@ +package com.tangem.tap.domain.extensions + +import com.tangem.FirmwareConstraints +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token +import com.tangem.commands.common.card.Card +import com.tangem.commands.common.card.CardStatus +import com.tangem.commands.wallet.CardWallet +import com.tangem.commands.wallet.WalletStatus +import com.tangem.common.TangemSdkConstants + +fun Card.getToken(): Token? { + val symbol = cardData?.tokenSymbol ?: return null + val contractAddress = cardData?.tokenContractAddress ?: return null + val decimals = cardData?.tokenDecimal ?: return null + return Token(symbol, contractAddress, decimals) +} + +fun Card.getBlockchain(): Blockchain? { + val blockchainName: String = cardData?.blockchainName ?: return null + return Blockchain.fromId(blockchainName) +} + +fun Card.getSingleWallet(): CardWallet? { + return wallet(TangemSdkConstants.getDefaultWalletIndex()) +} + +fun Card.getStatus(): CardStatus { + if (firmwareVersion < FirmwareConstraints.AvailabilityVersions.walletData) return status!! + + return if (getWallets().any { it.status == WalletStatus.Loaded }) { + CardStatus.Loaded + } else { + CardStatus.Empty + } +} + +fun Card.hasSignedHashes(): Boolean { + return getWallets().any { it.status == WalletStatus.Loaded && it.signedHashes ?: 0 > 0 } +} + +fun Card.signedHashesCount(): Int { + return getWallets().map { it.signedHashes ?: 0 }.sum() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt b/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt new file mode 100644 index 0000000000..c8710e3aeb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt @@ -0,0 +1,8 @@ +package com.tangem.tap.domain.extensions + +import com.tangem.commands.wallet.WalletIndex +import com.tangem.common.TangemSdkConstants + +fun TangemSdkConstants.Companion.getDefaultWalletIndex(): WalletIndex { + return WalletIndex.Index(oldCardDefaultWalletIndex) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt new file mode 100644 index 0000000000..fc847ae987 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -0,0 +1,33 @@ +package com.tangem.tap.domain.extensions + +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.commands.common.card.Card +import com.tangem.commands.common.card.EllipticCurve + +fun WalletManagerFactory.makeWalletManagerForApp(card: Card, blockchain: Blockchain): WalletManager? { + val curve = blockchain.getCurve() ?: return null + val publicKey = card.getWallets().firstOrNull { it.curve == curve }?.publicKey ?: return null + return makeWalletManager(card.cardId, publicKey, blockchain, curve) +} + +fun WalletManagerFactory.makeWalletManagersForApp( + card: Card, blockchains: List +): List { + return makeWalletManagersForCurve(card, blockchains, EllipticCurve.Secp256k1) + + makeWalletManagersForCurve(card, blockchains, EllipticCurve.Ed25519) +} + +fun WalletManagerFactory.makeWalletManagersForCurve( + card: Card, blockchains: List, curve: EllipticCurve +): List { + val blockchainsForCurve = blockchains.filter { it.getCurve() == curve } + val walletPublicKey = card.getWallets().firstOrNull { it.curve == curve }?.publicKey + + return if (walletPublicKey != null) { + makeWalletManagers(card.cardId, walletPublicKey, blockchainsForCurve, curve) + } else { + emptyList() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt index 0af8e1f29b..735cc1de64 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt @@ -2,26 +2,39 @@ package com.tangem.tap.domain.tasks import com.tangem.CardSession import com.tangem.CardSessionRunnable -import com.tangem.commands.ReadCommand +import com.tangem.FirmwareConstraints +import com.tangem.TangemSdkError +import com.tangem.commands.common.card.Card import com.tangem.common.CompletionResult import com.tangem.tasks.CreateWalletTask +import com.tangem.tasks.PreflightReadCapable +import com.tangem.tasks.PreflightReadSettings +import com.tangem.tasks.PreflightReadTask -class CreateWalletAndRescanTask : CardSessionRunnable { +class CreateWalletAndRescanTask : CardSessionRunnable, PreflightReadCapable { override val requiresPin2 = false + override fun preflightReadSettings() = PreflightReadSettings.FullCardRead - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - CreateWalletTask().run(session) { result -> + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val firmwareVerion = session.environment.card?.firmwareVersion + if (firmwareVerion == null) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + val task = if (firmwareVerion < FirmwareConstraints.AvailabilityVersions.walletData) { + CreateWalletTask() + } else { + CreateWalletsTask() + } + + task.run(session) { result -> when (result) { - is CompletionResult.Success -> { - ReadCommand().run(session) { readResult -> - when (readResult) { - is CompletionResult.Success -> ScanNoteTask(readResult.data).run(session, callback) - is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) - } - } - } + is CompletionResult.Success -> + PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } + + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt new file mode 100644 index 0000000000..b72cf26ae3 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -0,0 +1,64 @@ +package com.tangem.tap.domain.tasks + +import com.tangem.CardSession +import com.tangem.CardSessionRunnable +import com.tangem.commands.common.card.Card +import com.tangem.commands.common.card.EllipticCurve +import com.tangem.commands.wallet.CreateWalletCommand +import com.tangem.commands.wallet.WalletConfig +import com.tangem.common.CompletionResult +import com.tangem.tasks.PreflightReadSettings +import com.tangem.tasks.PreflightReadTask + +class CreateWalletsTask(wallets: List = emptyList()) : CardSessionRunnable { + override val requiresPin2 = false + + val wallets = if (wallets.isEmpty()) { + listOf( + WalletConfig(null, null, EllipticCurve.Secp256k1, null), + WalletConfig(null, null, EllipticCurve.Ed25519, null), + WalletConfig(null, null, EllipticCurve.Secp256r1, null), + ) + } else { + wallets + } + + var index = 0 + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val walletConfig = wallets[index] + createWallet(walletConfig, session, callback) + } + + private fun createWallet( + walletConfig: WalletConfig, session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + + CreateWalletCommand( + walletConfig, walletIndexValue = walletConfig.curveId!!.toWalletIndex() + ).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + if (index == wallets.lastIndex) { + PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback) + return@run + } + index += 1 + createWallet(wallets[index], session, callback) + } + is CompletionResult.Failure -> { + callback(CompletionResult.Failure(result.error)) + } + } + } + } +} + +private fun EllipticCurve.toWalletIndex(): Int { + return when (this) { + EllipticCurve.Secp256k1 -> 0 + EllipticCurve.Ed25519 -> 1 + EllipticCurve.Secp256r1 -> 2 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 0c6e990dc3..f252714774 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -1,36 +1,40 @@ package com.tangem.tap.domain.tasks import com.tangem.* -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.commands.CommandResponse import com.tangem.commands.ReadIssuerDataCommand import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.CardStatus -import com.tangem.commands.verifycard.VerifyCardCommand -import com.tangem.commands.verifycard.VerifyCardResponse +import com.tangem.commands.common.card.EllipticCurve +import com.tangem.commands.verification.VerifyCardCommand +import com.tangem.commands.verification.VerifyCardResponse +import com.tangem.commands.wallet.WalletConfig import com.tangem.common.CompletionResult import com.tangem.common.extensions.toHexString import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.TapWorkarounds.isExcluded -import com.tangem.tap.domain.isMultiwalletAllowed +import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.extensions.getStatus import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTwinCard import com.tangem.tap.store +import com.tangem.tasks.PreflightReadCapable +import com.tangem.tasks.PreflightReadSettings import com.tangem.tasks.ScanTask data class ScanNoteResponse( - val walletManager: WalletManager?, val card: Card, val verifyResponse: VerifyCardResponse? = null, val secondTwinPublicKey: String? = null, ) : CommandResponse -class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { +class ScanNoteTask(val card: Card? = null) : CardSessionRunnable, PreflightReadCapable { override val requiresPin2 = false + override fun preflightReadSettings() = PreflightReadSettings.FullCardRead + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { ScanTask().run(session) { result -> when (result) { @@ -48,39 +52,25 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit + card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit ) { - VerifyCardCommand(true).run(session) { verifyResult -> when (verifyResult) { is CompletionResult.Success -> { - callback(CompletionResult.Success(ScanNoteResponse( - walletManager, card, verifyResult.data, publicKey))) + if (card.isTwinCard()) { + dealWithTwinCard(card, session, verifyResult.data, callback) + } else if (card.firmwareVersion.major >= 4) { + createMissingWalletsIfNeeded(card, session, verifyResult.data, callback) + } else { + callback(CompletionResult.Success(ScanNoteResponse(card, verifyResult.data))) + } } is CompletionResult.Failure -> { callback(CompletionResult.Failure(TangemSdkError.CardVerificationFailed())) @@ -89,39 +79,73 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit + ) { + if (card.getStatus() == CardStatus.Empty) { + callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse))) + return + } + + val curvesPresent = card.getWallets().map { it.curve } + val curvesToCreate = EllipticCurve.values().subtract(curvesPresent) + + if (curvesToCreate.isEmpty()) { + callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse))) + return + } + + val configs = curvesToCreate.map { curve -> + WalletConfig( + isReusable = null, prohibitPurgeWallet = null, curveId = curve, + signingMethods = null + ) + } + CreateWalletsTask(configs).run(session) { result -> + when (result) { + is CompletionResult.Success -> + callback(CompletionResult.Success(ScanNoteResponse(result.data, verifyResponse))) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + + } + private fun dealWithTwinCard( - card: Card, session: CardSession, + card: Card, session: CardSession, verifyResponse: VerifyCardResponse, callback: (result: CompletionResult) -> Unit ) { ReadIssuerDataCommand().run(session) { readDataResult -> when (readDataResult) { is CompletionResult.Success -> { + val publicKey = card.getSingleWallet()?.publicKey + if (publicKey == null) { + callback(CompletionResult.Success(ScanNoteResponse(card, null))) + return@run + } val verified = TwinCardsManager.verifyTwinPublicKey( - readDataResult.data.issuerData, card.walletPublicKey + readDataResult.data.issuerData, publicKey ) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) - val walletManager = try { - getWalletManagerFactory().makeMultisigWalletManager(card, twinPublicKey) - } catch (exception: Exception) { - callback(CompletionResult.Success(ScanNoteResponse(null, card))) - return@run - } - verifyCard(walletManager, card, twinPublicKey.toHexString(), session, callback) + callback(CompletionResult.Success( + ScanNoteResponse(card, verifyResponse, twinPublicKey.toHexString()) + )) return@run } else { - callback(CompletionResult.Success(ScanNoteResponse(null, card))) + callback(CompletionResult.Success(ScanNoteResponse(card, null))) } } is CompletionResult.Failure -> - callback(CompletionResult.Success(ScanNoteResponse(null, card))) + callback(CompletionResult.Success(ScanNoteResponse(card, null))) } } } private fun getErrorIfExcludedCard(card: Card): TangemError? { if (card.isExcluded()) return TapSdkError.CardForDifferentApp - if (card.status == CardStatus.Purged) return TangemSdkError.CardIsPurged() + if (card.status == CardStatus.Purged) return TangemSdkError.WalletIsPurged() if (card.status == CardStatus.NotPersonalized) return TangemSdkError.NotPersonalized() return null } diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt index 42c7aeefe4..78afe93215 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt @@ -7,6 +7,7 @@ import com.squareup.moshi.JsonClass import com.squareup.moshi.Types import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.commands.common.card.FirmwareVersion import com.tangem.tap.common.extensions.readJsonFileToString import com.tangem.tap.network.createMoshi @@ -93,19 +94,27 @@ class CurrenciesRepository(val context: Application) { return tokensAdapter.fromJson(json)!!.map { it.toToken() } } - fun getBlockchains(): List { - return listOf( - Blockchain.Bitcoin, Blockchain.BitcoinCash, Blockchain.Binance, Blockchain.Litecoin, - Blockchain.XRP, Blockchain.Tezos, - Blockchain.Ethereum, Blockchain.RSK) + fun getBlockchains(cardFirmware: FirmwareVersion?): List { + return if (cardFirmware == null || cardFirmware.major < 4) { + secp256k1Blochcains + } else { + secp256k1Blochcains + ed25519Blockchains + } } companion object { private const val POPULAR_TOKENS_FILE_NAME = "erc20_tokens" private const val FILE_NAME_PREFIX_TOKENS = "tokens" private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" + fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId" fun getFileNameForBlockchains(cardId: String): String = "${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId" + + private val secp256k1Blochcains = listOf( + Blockchain.Bitcoin, Blockchain.BitcoinCash, Blockchain.Binance, Blockchain.Litecoin, + Blockchain.XRP, Blockchain.Tezos, + Blockchain.Ethereum, Blockchain.RSK) + private val ed25519Blockchains = listOf(Blockchain.CardanoShelley, Blockchain.Stellar) } } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt index 7296c72a11..0b424bb64d 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt @@ -2,18 +2,20 @@ package com.tangem.tap.domain.twins import com.tangem.CardSession import com.tangem.CardSessionRunnable -import com.tangem.commands.CreateWalletResponse -import com.tangem.commands.PurgeWalletCommand import com.tangem.commands.common.card.CardStatus +import com.tangem.commands.wallet.CreateWalletResponse +import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants +import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tasks.CreateWalletTask class CreateFirstTwinWalletTask : CardSessionRunnable { override val requiresPin2 = false override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - if (session.environment.card?.walletPublicKey != null) { - PurgeWalletCommand().run(session) { response -> + if (session.environment.card?.getSingleWallet()?.publicKey != null) { + PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response -> when (response) { is CompletionResult.Success -> { session.environment.card = session.environment.card?.copy(status = CardStatus.Empty) diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index 5587faa1ab..e02a2511c2 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -3,11 +3,13 @@ package com.tangem.tap.domain.twins import com.tangem.CardSession import com.tangem.CardSessionRunnable import com.tangem.Message -import com.tangem.commands.CreateWalletResponse -import com.tangem.commands.PurgeWalletCommand import com.tangem.commands.common.card.CardStatus +import com.tangem.commands.wallet.CreateWalletResponse +import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tasks.CreateWalletTask class CreateSecondTwinWalletTask( @@ -18,9 +20,9 @@ class CreateSecondTwinWalletTask( override val requiresPin2 = true override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - if (session.environment.card?.walletPublicKey != null) { + if (session.environment.card?.getSingleWallet()?.publicKey != null) { session.setInitialMessage(preparingMessage) - PurgeWalletCommand().run(session) { response -> + PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response -> when (response) { is CompletionResult.Success -> { session.environment.card = diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt index 073c47b344..e8454b6756 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt @@ -3,7 +3,7 @@ package com.tangem.tap.domain.twins import com.tangem.CardSession import com.tangem.CardSessionRunnable import com.tangem.KeyPair -import com.tangem.commands.ReadCommand +import com.tangem.commands.read.ReadCommand import com.tangem.common.CompletionResult import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt index 30d3b688dd..5ab77f557e 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt @@ -6,6 +6,7 @@ import com.tangem.KeyPair import com.tangem.TangemSdkError import com.tangem.commands.* import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.guard import com.tangem.common.files.FileHashHelper @@ -17,14 +18,14 @@ class WriteProtectedIssuerDataTask( override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val hashes = arrayOf(twinPublicKey.calculateSha256()) - SignCommand(hashes).run(session) { signResult -> + SignCommand(hashes, TangemSdkConstants.getDefaultWalletIndex()).run(session) { signResult -> when (signResult) { is CompletionResult.Success -> { ReadIssuerDataCommand().run(session) { readResult -> when (readResult) { is CompletionResult.Success -> { writeIssuerData( - twinPublicKey, issuerKeys, signResult.data.signature, + twinPublicKey, issuerKeys, signResult.data.signatures[0], readResult.data, session, callback ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index ee44e36c9d..4ec1681b66 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -203,6 +203,5 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet { private fun Card.toCardInfo(): CardInfo? { val cardId = this.cardId.chunked(4).joinToString(separator = " ") val issuer = this.cardData?.issuerName ?: return null - val signedHashes = this.walletSignedHashes ?: return null - return CardInfo(cardId, issuer, signedHashes) + return CardInfo(cardId, issuer) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 5ef1033e2f..22fca3db45 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -26,7 +26,6 @@ data class DetailsState( data class CardInfo( val cardId: String, val issuer: String, - val signedHashes: Int, ) enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt index 669ab9be91..e99f7c7ad6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt @@ -18,7 +18,7 @@ class CreateTwinWalletMiddleware { fun handle(action: DetailsAction.CreateTwinWalletAction) { when (action) { is DetailsAction.CreateTwinWalletAction.ShowWarning -> { - val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet + val wallet = store.state.detailsState.wallets.firstOrNull() if (wallet == null) { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) return diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 895f379b09..fc793be4cc 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -74,12 +74,8 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber) { @@ -274,7 +275,6 @@ class FeedbackEmail : EmailData { val builder = StringBuilder() builder.appendKeyValue("Card ID", infoHolder.cardId) builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion) - builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount) infoHolder.walletsInfo.forEach { builder.appendKeyValue("Blockchain", it.blockchain.fullName) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 5a5f927b30..a28d92df4b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -4,14 +4,13 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder import com.tangem.blockchain.common.* -import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Signer +import com.tangem.blockchain.extensions.SimpleResult import com.tangem.commands.common.card.Card import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapWorkarounds @@ -115,11 +114,10 @@ private fun sendTransaction( val result = (walletManager as TransactionSender).send(txData, signer) withContext(Dispatchers.Main) { when (result) { - is Result.Success -> { + is SimpleResult.Success -> { tangemSdk.config.linkedTerminal = isLinkedTerminal FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.TRANSACTION_IS_SENT, card) dispatch(SendAction.SendSuccess) - dispatch(GlobalAction.UpdateWalletSignedHashes(result.data.walletSignedHashes)) dispatch(NavigationAction.PopBackTo()) scope.launch(Dispatchers.IO) { withContext(Dispatchers.Main) { @@ -131,7 +129,7 @@ private fun sendTransaction( } } } - is Result.Failure -> { + is SimpleResult.Failure -> { when (result.error) { is CreateAccountUnderfunded -> { val error = result.error as CreateAccountUnderfunded diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 9db3d6e7ca..8cad24d39c 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -5,7 +5,6 @@ import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.states.IdStateHolder import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.store import org.rekotlin.Action import java.math.BigDecimal @@ -60,8 +59,7 @@ private class EmptyReducer : SendInternalReducer { private class PrepareSendScreenStatesReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { val prepareAction = action as PrepareSendScreen - val walletManager = action.walletManager - ?: store.state.globalState.scanNoteResponse!!.walletManager!! + val walletManager = action.walletManager!! val amountToExtract = prepareAction.tokenAmount ?: prepareAction.coinAmount!! val decimals = amountToExtract.decimals diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 7923d17bd4..cb13d64dd0 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -13,8 +13,9 @@ class TokensMiddleware { { action -> when (action) { is TokensAction.LoadCurrencies -> { + val cardFirmware = state()?.globalState?.scanNoteResponse?.card?.firmwareVersion val tokens = currenciesRepository.getPopularTokens() - val blockchains = currenciesRepository.getBlockchains() + val blockchains = currenciesRepository.getBlockchains(cardFirmware) val currencies = CurrencyListItem.createListOfCurrencies( blockchains, tokens ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 6daa182e35..d68d7863f2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -10,6 +10,8 @@ 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.extensions.makeWalletManagerForApp +import com.tangem.tap.domain.extensions.makeWalletManagersForApp import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.scope @@ -46,7 +48,7 @@ class MultiWalletMiddleware { globalState?.scanNoteResponse?.card?.let { card -> currenciesRepository.saveAddedBlockchain(card.cardId, action.blockchain) globalState.tapWalletManager.walletManagerFactory - .makeWalletManager(card, action.blockchain)?.let { + .makeWalletManagerForApp(card, action.blockchain)?.let { store.dispatch(WalletAction.MultiWallet.AddWalletManagers(it)) } } @@ -66,9 +68,10 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.FindBlockchainsInUse -> { - val blockchains = currenciesRepository.getBlockchains() + val cardFirmware = globalState?.scanNoteResponse?.card?.firmwareVersion + val blockchains = currenciesRepository.getBlockchains(cardFirmware) .filterNot { walletState?.blockchains?.contains(it) == true } - val walletManagers = action.factory.makeWalletManagers(action.card, blockchains) + val walletManagers = action.factory.makeWalletManagersForApp(action.card, blockchains) scope.launch { walletManagers.map { walletManager -> diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 05993a08ef..e11a577a51 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -108,7 +108,11 @@ class WalletMiddleware { ) when (result) { is CompletionResult.Success -> { - globalState?.tapWalletManager?.onCardScanned(result.data) + val scanNoteResponse = + globalState?.scanNoteResponse?.copy(card = result.data) + scanNoteResponse?.let { + globalState.tapWalletManager.onCardScanned(scanNoteResponse) + } } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index d158d3d323..79ab8e3feb 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -6,7 +6,7 @@ import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.extensions.SimpleResult import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.CardType -import com.tangem.commands.verifycard.VerifyCardState +import com.tangem.commands.verification.VerifyCardState import com.tangem.common.extensions.getType import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler @@ -14,6 +14,8 @@ import com.tangem.tap.common.extensions.isGreaterThan import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager +import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.extensions.hasSignedHashes import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.twins.isTwinCard import com.tangem.tap.features.wallet.redux.WalletAction @@ -63,13 +65,12 @@ class WarningsMiddleware { } private fun showCardWarningsIfNeeded(globalState: GlobalState?) { - val validator = globalState?.scanNoteResponse?.walletManager as? SignatureCountValidator globalState?.scanNoteResponse?.card?.let { card -> globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) if (card.getType() != CardType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) } else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) { - checkIfWarningNeeded(card, validator)?.let { addWarningMessage(it) } + checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) } } if (card.getType() == CardType.Release) { if (globalState.scanNoteResponse.verifyResponse?.verificationState == @@ -82,12 +83,14 @@ class WarningsMiddleware { } private fun checkIfWarningNeeded( - card: Card, signatureCountValidator: SignatureCountValidator? = null, + card: Card ): WarningMessage? { - if (card.isTwinCard() || card.isMultiwalletAllowed) return null + if (card.isTwinCard()) return null - return if (signatureCountValidator == null) { - if (card.walletSignedHashes ?: 0 > 0) { + val validator = store.state.walletState.walletManagers.firstOrNull() + as? SignatureCountValidator + return if (validator == null || card.isMultiwalletAllowed) { + if (card.hasSignedHashes()) { WarningMessagesManager.alreadySignedHashesWarning() } else { store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) @@ -108,10 +111,11 @@ class WarningsMiddleware { if (card.isTwinCard() || card.isMultiwalletAllowed) return - val validator = store.state.globalState.scanNoteResponse?.walletManager + val validator = store.state.walletState.walletManagers.firstOrNull() as? SignatureCountValidator scope.launch { - val result = validator?.validateSignatureCount(card.walletSignedHashes ?: 0) + val signedHashes = card.getSingleWallet()?.signedHashes ?: 0 + val result = validator?.validateSignatureCount(signedHashes) withContext(Dispatchers.Main) { when (result) { SimpleResult.Success -> { @@ -121,7 +125,7 @@ class WarningsMiddleware { is SimpleResult.Failure -> if (result.error is BlockchainSdkError.SignatureCountNotMatched) { addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true) - } else if (card.walletSignedHashes ?: 0 > 0) { + } else if (signedHashes > 0) { addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index 9e2320d30c..6e69a80b7d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -34,28 +34,26 @@ class MultiWalletView : WalletView { override fun changeWalletView(fragment: WalletFragment) { setFragment(fragment) onViewCreated() - showMultiWalletView() - setupButtons() + showMultiWalletView(fragment) + setupButtons(fragment) } - private fun showMultiWalletView() { - val fragment = fragment ?: return - fragment.tv_twin_card_number.hide() - fragment.iv_twin_card.hide() - fragment.rv_pending_transaction.hide() - fragment.l_card_balance.hide() - fragment.l_address.hide() - fragment.l_buttons_short.hide() - fragment.l_buttons_long.hide() - fragment.btn_scan_multiwallet?.show() - fragment.rv_multiwallet.show() - fragment.btn_add_token.show() + private fun showMultiWalletView(fragment: WalletFragment) = with(fragment) { + tv_twin_card_number.hide() + iv_twin_card.hide() + rv_pending_transaction.hide() + l_card_balance.hide() + l_address.hide() + l_buttons_short.hide() + l_buttons_long.hide() + btn_scan_multiwallet?.show() + rv_multiwallet.show() + btn_add_token.show() } - private fun setupButtons() { - val fragment = fragment ?: return - fragment.btn_scan_multiwallet?.setOnClickListener { store.dispatch(WalletAction.Scan) } + private fun setupButtons(fragment: WalletFragment) = with(fragment) { + btn_scan_multiwallet?.setOnClickListener { store.dispatch(WalletAction.Scan) } } override fun setFragment(fragment: WalletFragment) { @@ -114,22 +112,22 @@ class MultiWalletView : WalletView { private fun showErrorState( fragment: WalletFragment, errorTitle: CharSequence, errorDescription: CharSequence, - ) { - fragment.l_card_balance.show() - fragment.l_balance.hide() - fragment.l_balance_error.show() - fragment.rv_multiwallet.hide() - fragment.btn_add_token.hide() - fragment.tv_error_title.text = errorTitle - fragment.tv_error_descriptions.text = errorDescription + ) = with(fragment) { + l_card_balance.show() + l_balance.hide() + l_balance_error.show() + rv_multiwallet.hide() + btn_add_token.hide() + tv_error_title.text = errorTitle + tv_error_descriptions.text = errorDescription } - private fun configureButtonsForEmptyWalletState(fragment: WalletFragment) { - fragment.btn_scan_multiwallet.hide() - fragment.l_buttons_long.show() - fragment.btn_scan_long.setOnClickListener { store.dispatch(WalletAction.Scan) } - fragment.btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) } - fragment.btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet) + private fun configureButtonsForEmptyWalletState(fragment: WalletFragment) = with(fragment) { + btn_scan_multiwallet.hide() + l_buttons_long.show() + btn_scan_long.setOnClickListener { store.dispatch(WalletAction.Scan) } + btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) } + btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet) } private fun handleDialogs(walletDialog: StateDialog?) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index d6762f815b..97f2618542 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -44,17 +44,16 @@ class SingleWalletView : WalletView { override fun changeWalletView(fragment: WalletFragment) { setFragment(fragment) onViewCreated() - showSingleWalletView() + showSingleWalletView(fragment) } - private fun showSingleWalletView() { - val fragment = fragment ?: return - fragment.rv_multiwallet.hide() - fragment.btn_add_token.hide() - fragment.btn_scan_multiwallet?.hide() - fragment.rv_pending_transaction.hide() - fragment.l_card_balance.show() - fragment.l_address.show() + private fun showSingleWalletView(fragment: WalletFragment) = with(fragment) { + rv_multiwallet.hide() + btn_add_token.hide() + btn_scan_multiwallet?.hide() + rv_pending_transaction.hide() + l_card_balance.show() + l_address.show() } override fun onViewCreated() { @@ -70,13 +69,15 @@ class SingleWalletView : WalletView { } override fun onNewState(state: WalletState) { - setupTwinCards(state.twinCardsState) + val fragment = fragment ?: return state.primaryWallet ?: return - setupButtons(state.primaryWallet, state.twinCardsState != null) - setupAddressCard(state.primaryWallet) + + setupTwinCards(state.twinCardsState, fragment) + setupButtons(state.primaryWallet, state.twinCardsState != null, fragment) + setupAddressCard(state.primaryWallet, fragment) showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions) setupBalance(state, state.primaryWallet) - handleDialogs(state.walletDialog) + handleDialogs(state.walletDialog, fragment) } private fun showPendingTransactionsIfPresent(pendingTransactions: List) { @@ -92,43 +93,45 @@ class SingleWalletView : WalletView { } } - private fun setupTwinCards(twinCardsState: TwinCardsState?) { - fragment?.apply { - twinCardsState?.cardNumber?.let { cardNumber -> - this.tv_twin_card_number.show() - this.iv_twin_card.show() - val number = when (cardNumber) { - TwinCardNumber.First -> "1" - TwinCardNumber.Second -> "2" - } - this.tv_twin_card_number.text = - this.getString(R.string.wallet_twins_chip_format, number) - } - if (twinCardsState?.cardNumber == null) { - this.tv_twin_card_number.hide() - this.iv_twin_card.hide() - } - if (twinCardsState?.showTwinOnboarding == true) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) + private fun setupTwinCards( + twinCardsState: TwinCardsState?, fragment: WalletFragment + ) = with(fragment) { + twinCardsState?.cardNumber?.let { cardNumber -> + tv_twin_card_number.show() + iv_twin_card.show() + val number = when (cardNumber) { + TwinCardNumber.First -> "1" + TwinCardNumber.Second -> "2" } + tv_twin_card_number.text = + this.getString(R.string.wallet_twins_chip_format, number) } + if (twinCardsState?.cardNumber == null) { + tv_twin_card_number.hide() + iv_twin_card.hide() + } + if (twinCardsState?.showTwinOnboarding == true) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) + } + } - private fun setupButtons(state: WalletData, isTwinsWallet: Boolean) { - val fragment = fragment ?: return + private fun setupButtons( + state: WalletData, isTwinsWallet: Boolean, fragment: WalletFragment + ) = with(fragment){ setupButtonsType(state, fragment) val btnConfirm = if (state.topUpState.allowed) { - fragment.btn_confirm_short + btn_confirm_short } else { - fragment.btn_confirm_long + btn_confirm_long } val btnScan = if (state.topUpState.allowed) { - fragment.btn_scan_short + btn_scan_short } else { - fragment.btn_scan_long + btn_scan_long } setupConfirmButton(state, btnConfirm, fragment, isTwinsWallet) @@ -137,27 +140,27 @@ class SingleWalletView : WalletView { store.dispatch(WalletAction.Scan) } - fragment.btn_copy.setOnClickListener { + btn_copy.setOnClickListener { state.walletAddresses?.selectedAddress?.address?.let { addressString -> store.dispatch(WalletAction.CopyAddress(addressString, fragment.requireContext())) } } - fragment.btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } + btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } - fragment.btn_top_up.setOnClickListener { + btn_top_up.setOnClickListener { store.dispatch( WalletAction.TopUpAction.TopUp(fragment.requireContext(), R.color.backgroundLightGray) ) } } - private fun setupButtonsType(state: WalletData, fragment: WalletFragment) { + private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) { if (state.topUpState.allowed) { - fragment.l_buttons_long.hide() - fragment.l_buttons_short.show() + l_buttons_long.hide() + l_buttons_short.show() } else { - fragment.l_buttons_long.show() - fragment.l_buttons_short.hide() + l_buttons_long.show() + l_buttons_short.hide() } } @@ -185,39 +188,37 @@ class SingleWalletView : WalletView { } - private fun setupAddressCard(state: WalletData) { - val fragment = fragment ?: return + private fun setupAddressCard(state: WalletData, fragment: WalletFragment) = with(fragment) { if (state.walletAddresses != null && state.blockchain != null) { - fragment.l_address?.show() + l_address?.show() if (state.shouldShowMultipleAddress()) { - (fragment.l_address as? ViewGroup)?.beginDelayedTransition() - fragment.chip_group_address_type.show() - fragment.chip_group_address_type.fitChipsByGroupWidth() + (l_address as? ViewGroup)?.beginDelayedTransition() + chip_group_address_type.show() + chip_group_address_type.fitChipsByGroupWidth() val checkedId = MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type) - if (checkedId != View.NO_ID) fragment.chip_group_address_type.check(checkedId) + if (checkedId != View.NO_ID) chip_group_address_type.check(checkedId) - fragment.chip_group_address_type.setOnCheckedChangeListener { group, checkedId -> + chip_group_address_type.setOnCheckedChangeListener { group, checkedId -> if (checkedId == -1) return@setOnCheckedChangeListener val type = MultipleAddressUiHelper.idToType(checkedId, state.blockchain) type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) } } } else { - fragment.chip_group_address_type.hide() + chip_group_address_type.hide() } - fragment.tv_address.text = state.walletAddresses.selectedAddress.address - fragment.tv_explore?.setOnClickListener { + tv_address.text = state.walletAddresses.selectedAddress.address + tv_explore?.setOnClickListener { store.dispatch(WalletAction.ExploreAddress( state.walletAddresses.selectedAddress.exploreUrl, fragment.requireContext())) } } else { - fragment.l_address?.hide() + l_address?.hide() } } - private fun handleDialogs(walletDialog: StateDialog?) { - val fragment = fragment ?: return + private fun handleDialogs(walletDialog: StateDialog?, fragment: WalletFragment) { val context = fragment.context ?: return when (walletDialog) { is WalletDialog.QrDialog -> { diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 00c0626cba..69b8b5cf55 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -90,28 +90,6 @@ app:layout_constraintTop_toBottomOf="@id/tv_card_id" tools:text="Tangem" /> - - - - + app:layout_constraintTop_toBottomOf="@id/tv_issuer_title" />