diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt index f8361ffae1..f316b2e433 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt @@ -5,6 +5,7 @@ import com.tangem.TangemSdk import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.Wallet import com.tangem.common.CompletionResult +import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.models.scan.CardDTO import com.tangem.tap.domain.tasks.SignHashesTask import kotlinx.coroutines.suspendCancellableCoroutine @@ -23,12 +24,12 @@ class TangemSigner( publicKey: Wallet.PublicKey, ): CompletionResult> { return suspendCancellableCoroutine { continuation -> - val cardId = if (card.backupStatus?.isActive == true) null else card.cardId - + val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins val task = SignHashesTask(hashes, publicKey) + tangemSdk.startSessionWithRunnable( runnable = task, - cardId = cardId, + cardId = card.cardId.takeIf { isCardNotBackedUp }, initialMessage = initialMessage, accessCode = accessCode, ) { result -> 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 fc1d4e3958..7d5a21594a 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -14,6 +14,7 @@ import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.setContext import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.model.UserWallet +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.tap.domain.walletStores.WalletStoresError import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.disclaimer.createDisclaimer @@ -36,7 +37,10 @@ class TapWalletManager { suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean, sendAnalyticsEvent: Boolean) { // do nothing if its the same wallet - if (store.state.globalState.scanResponse == userWallet.scanResponse) return + store.state.globalState.scanResponse?.let { + val userWalletId = UserWalletIdBuilder.scanResponse(it).build() + if (userWalletId == userWallet.walletId) return + } Analytics.setContext(userWallet.scanResponse) if (sendAnalyticsEvent) { diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt index ec4c83c460..71255de6a0 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt @@ -17,6 +17,7 @@ import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUse import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsSensitiveInformationRepository import com.tangem.tap.domain.userWalletList.utils.json.ByteArrayKeyAdapter import com.tangem.tap.domain.userWalletList.utils.json.CardBackupStatusAdapter +import com.tangem.tap.domain.userWalletList.utils.json.DerivationPathAdapterWithMigration import com.tangem.tap.domain.userWalletList.utils.json.ExtendedPublicKeysMapAdapter import com.tangem.tap.domain.userWalletList.utils.json.ScanResponseDerivedKeysMapAdapter import com.tangem.tap.domain.userWalletList.utils.json.WalletDerivedKeysMapAdapter @@ -33,8 +34,8 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation( .add(ByteArrayKeyAdapter()) .add(ExtendedPublicKeysMapAdapter()) .add(CardBackupStatusAdapter()) + .add(DerivationPathAdapterWithMigration()) .add(TangemSdkAdapter.DateAdapter()) - .add(TangemSdkAdapter.DerivationPathAdapter()) .add(TangemSdkAdapter.DerivationNodeAdapter()) .add(TangemSdkAdapter.FirmwareVersionAdapter()) // For PrimaryCard model .add(KotlinJsonAdapterFactory()) diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index ad29020162..78f74e0c3d 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -54,16 +54,6 @@ internal class BiometricUserWalletsListManager( get() = keysRepository.hasSavedEncryptionKeys() override suspend fun unlock(): CompletionResult { - // Occurs when user has locked user wallets after unlocking with biometry - // e.g. after biometric storage master key invalidation - if (state.value.hasLockedUserWalletsAfterUnlock) { - return CompletionResult.Failure( - error = UserWalletsListError.UnableToUnlockUserWallets( - cause = IllegalStateException("Permanently locked"), - ), - ) - } - return unlockWithBiometryInternal() .mapFailure { error -> if (error is UserWalletsListError) { @@ -216,10 +206,7 @@ internal class BiometricUserWalletsListManager( .map { state.update { prevState -> val hasLockedUserWallets = prevState.userWallets.any { it.isLocked } - prevState.copy( - isLocked = hasLockedUserWallets, - hasLockedUserWalletsAfterUnlock = hasLockedUserWallets, - ) + prevState.copy(isLocked = hasLockedUserWallets) } } } @@ -262,15 +249,6 @@ internal class BiometricUserWalletsListManager( } } } - .doOnFailure { error -> - if (error is UserWalletsListError.EncryptionKeyInvalidated) { - state.update { prevState -> - prevState.copy( - hasLockedUserWalletsAfterUnlock = true, - ) - } - } - } } private suspend fun getSavedUserWallets(): CompletionResult> { @@ -334,6 +312,5 @@ internal class BiometricUserWalletsListManager( val userWallets: List = emptyList(), val selectedUserWalletId: UserWalletId? = null, val isLocked: Boolean = true, - val hasLockedUserWalletsAfterUnlock: Boolean = false, ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/json/DerivationPathAdapterWithMigration.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/json/DerivationPathAdapterWithMigration.kt new file mode 100644 index 0000000000..4719b96b49 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/json/DerivationPathAdapterWithMigration.kt @@ -0,0 +1,35 @@ +package com.tangem.tap.domain.userWalletList.utils.json + +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +import com.tangem.common.extensions.guard +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.crypto.hdWallet.DerivationNode +import com.tangem.crypto.hdWallet.DerivationPath +import timber.log.Timber + +class DerivationPathAdapterWithMigration { + @ToJson + fun toJson(src: DerivationPath): String = src.rawPath + + @FromJson + fun fromJson(json: String): DerivationPath { + val jsonMap = MoshiJsonConverter.default().toMap(json) + return if (jsonMap.isEmpty()) { + DerivationPath(json) + } else { + fromLegacyScheme(jsonMap) + } + } + + @Suppress("UNCHECKED_CAST") + private fun fromLegacyScheme(jsonMap: Map): DerivationPath { + val rawPath = jsonMap["rawPath"] as String + val nodeIndexes = (jsonMap["nodes"] as? List).guard { + Timber.e("Unable to convert derivation path nodes from JSON") + return DerivationPath(rawPath) + } + val nodes = nodeIndexes.map { DerivationNode.fromIndex(it.toLong()) } + return DerivationPath(rawPath, nodes) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index cfcc202a2b..b10ad9de2a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -10,6 +10,7 @@ import com.tangem.core.analytics.Analytics import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.extensions.dispatchDialogShow @@ -31,14 +32,6 @@ 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 import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.foregroundActivityObserver -import com.tangem.tap.preferencesStorage -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userTokensRepository -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletStoresManager import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -84,6 +77,8 @@ class DetailsMiddleware { allowsRequestAccessCodeFromRepository = true, ) .doOnSuccess { scanResponse -> + // if we use biometric, scanResponse in GlobalState is null, and crashes NPE on twin cards + store.dispatch(GlobalAction.SaveScanResponse(scanResponse)) val currentUserWalletId = state.scanResponse ?.let { UserWalletIdBuilder.scanResponse(it).build() } val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt index d4526edf76..f2fd8a3b07 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt @@ -160,7 +160,10 @@ fun DetailsItem(item: SettingsElement, appCurrency: String, onItemsClick: () -> @Composable fun TangemSocialAccounts(links: List, onSocialNetworkClick: (SocialNetworkLink) -> Unit) { - LazyRow(modifier = Modifier.padding(start = 8.dp, end = 8.dp)) { + LazyRow( + modifier = Modifier.padding(start = 8.dp, end = 8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { items(links) { Icon( painter = painterResource(id = it.network.iconRes), diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt index ce8e1fd1ef..c0dd629819 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt @@ -55,25 +55,28 @@ sealed class SocialNetwork(val id: String, val iconRes: Int) { object GitHub : SocialNetwork("GitHub", R.drawable.ic_github) object YouTube : SocialNetwork("YouTube", R.drawable.ic_youtube) object LinkedIn : SocialNetwork("LinkedIn", R.drawable.ic_linkedin) + object Discord : SocialNetwork("Discord", R.drawable.ic_discord) } object TangemSocialAccounts { val accountsEn: List = listOf( - SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/TangemCards"), + SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_chat"), SocialNetworkLink(SocialNetwork.Twitter, "https://twitter.com/tangem"), SocialNetworkLink(SocialNetwork.Facebook, "https://m.facebook.com/TangemCards/"), SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"), SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"), SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"), SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"), + SocialNetworkLink(SocialNetwork.Discord, "https://discord.gg/7AqTVyqdGS"), ) val accountsRu: List = listOf( - SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_ru"), + SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_chat_ru"), SocialNetworkLink(SocialNetwork.Twitter, "https://twitter.com/tangem"), SocialNetworkLink(SocialNetwork.Facebook, "https://m.facebook.com/TangemCards/"), SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"), SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"), SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"), SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"), + SocialNetworkLink(SocialNetwork.Discord, "https://discord.gg/7AqTVyqdGS"), ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index a7e540bf43..772bd7a3e7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.wallet.redux import android.graphics.Bitmap +import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.domain.common.util.cardTypesResolver @@ -16,6 +17,7 @@ import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.store import org.rekotlin.StateType +import java.math.BigDecimal import kotlin.properties.ReadOnlyProperty data class WalletState( @@ -103,6 +105,9 @@ data class WalletState( it.derivationPath?.rawPath == currency.derivationPath } } + + fun getBlockchainAmount(currency: Currency): BigDecimal = + getWalletManager(currency)?.wallet?.amounts?.get(AmountType.Coin)?.value ?: BigDecimal.ZERO } enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error } @@ -114,7 +119,6 @@ enum class ErrorType { sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { class SendButton(enabled: Boolean) : WalletMainButton(enabled) - class CreateWalletButton(enabled: Boolean) : WalletMainButton(enabled) } data class Artwork( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index d4556d790b..4ea69fa30f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -55,6 +55,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import timber.log.Timber +import java.math.BigDecimal import javax.inject.Inject /** @@ -128,7 +129,8 @@ class WalletDetailsFragment : (WalletState::selectedWalletData or WalletState::isExchangeServiceFeatureOn) { state -> val selectedWallet = state.selectedWalletData if (selectedWallet != null) { - setupButtonsRow(selectedWallet, state.isExchangeServiceFeatureOn) + val blockchainAmount: BigDecimal = state.getBlockchainAmount(selectedWallet.currency) + setupButtonsRow(selectedWallet, state.isExchangeServiceFeatureOn, blockchainAmount) } } (WalletState::state or WalletState::error) { state -> @@ -281,7 +283,11 @@ class WalletDetailsFragment : } } - private fun setupButtonsRow(selectedWallet: WalletDataModel, isExchangeServiceFeatureOn: Boolean) { + private fun setupButtonsRow( + selectedWallet: WalletDataModel, + isExchangeServiceFeatureOn: Boolean, + blockchainAmount: BigDecimal, + ) { val exchangeManager = store.state.globalState.exchangeManager binding.rowButtons.apply { onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) } @@ -310,7 +316,7 @@ class WalletDetailsFragment : binding.rowButtons.updateButtonsVisibility( actions = actions, exchangeServiceFeatureOn = isExchangeServiceFeatureOn, - sendAllowed = selectedWallet.mainButton.enabled, + sendAllowed = selectedWallet.mainButton(blockchainAmount).enabled, ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 1d0cc810ed..4522299310 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -239,7 +239,10 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber store.dispatch(WalletAction.Send()) - is WalletMainButton.CreateWalletButton -> store.dispatch(WalletAction.CreateWallet) - } - } + rowButtons.onSendClick = { store.dispatch(WalletAction.Send()) } } private fun setupAddressCard(state: WalletState, binding: FragmentWalletBinding) = with(binding.lAddress) { diff --git a/app/src/main/res/drawable/ic_discord.xml b/app/src/main/res/drawable/ic_discord.xml new file mode 100644 index 0000000000..88444ddf14 --- /dev/null +++ b/app/src/main/res/drawable/ic_discord.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index 738e44b33c..96fb733d71 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -9,6 +9,6 @@ }, { "name": "REDESIGNED_CUSTOM_TOKEN_SCREEN_ENABLED", - "version": "4.5.0" + "version": "undefined" } ] \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt b/domain/legacy/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt index b18597f282..39df7c6984 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt @@ -45,7 +45,7 @@ object SaltPayWorkaround { ) + attachTestWalletCardIds() val walletCardIdRanges = listOf( - CardIdRange("AC05000000000003", "AC05000000023997")!!, + CardIdRange("AC05000000000003", "AC05000000015993")!!, ) + attachTestWalletCardIdRanges() fun tokenFrom(blockchain: Blockchain): Token { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 8181f50b3c..f6cde60962 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -74,7 +74,7 @@ arrow = "1.2.0-RC" # region Tangem tangemBlockchainSdk = "develop-210" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-229" +tangemCardSdk = "develop-235" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem