Updated on 2026-08-14

This commit is contained in:
Tangem 2021-10-24 17:44:31 +03:00
commit 7b456a1d08
13 changed files with 78 additions and 32 deletions

View file

@ -91,7 +91,7 @@ class TapWalletManager {
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
if (data.card.isTangemTwin()) {
data.card.getTwinCardNumber()?.let {
store.dispatch(WalletAction.TwinsAction.SetTwinCard(null, it, true))
store.dispatch(WalletAction.TwinsAction.SetTwinCard(it, true))
}
}

View file

@ -117,8 +117,17 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanResponse> {
private fun getErrorIfExcludedCard(card: Card): TangemError? {
if (card.isExcluded()) return TapSdkError.CardForDifferentApp
// Disable new cards on the old version of the app // TODO: remove when cards are supported
if (card.isMultiCurrencyWallet() || card.isNote()) return UpdateAppToUseThisCard()
// Disable new multi-currency HD wallet cards on the old version of the app
// if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard()
return null
}
private fun getWalletManagerFactory(): WalletManagerFactory {
val blockchainSdkConfig = store.state.globalState.configManager?.config
?.blockchainSdkConfig ?: BlockchainSdkConfig()
return WalletManagerFactory(blockchainSdkConfig)
}
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.KeyPair
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.operations.wallet.CreateWalletCommand
import com.tangem.operations.wallet.CreateWalletResponse
@ -13,14 +14,21 @@ import com.tangem.operations.wallet.PurgeWalletCommand
import com.tangem.tap.domain.extensions.getSingleWallet
class CreateSecondTwinWalletTask(
private val firstPublicKey: String,
private val issuerKeys: KeyPair,
private val preparingMessage: Message,
private val creatingWalletMessage: Message
private val firstPublicKey: String,
private val firstCardId: String,
private val issuerKeys: KeyPair,
private val preparingMessage: Message,
private val creatingWalletMessage: Message
) : CardSessionRunnable<CreateWalletResponse> {
override fun run(session: CardSession, callback: (result: CompletionResult<CreateWalletResponse>) -> Unit) {
val publicKey = session.environment.card?.getSingleWallet()?.publicKey
val card = session.environment.card
val publicKey = card?.getSingleWallet()?.publicKey
if (publicKey != null) {
if (!card.cardId.startsWith(TwinsHelper.getPairCardSeries(firstCardId) ?: "")) {
callback(CompletionResult.Failure(TangemSdkError.WrongCardType()))
return
}
session.setInitialMessage(preparingMessage)
PurgeWalletCommand(publicKey).run(session) { response ->
when (response) {

View file

@ -21,8 +21,6 @@ import com.tangem.tap.tangemSdkManager
class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) {
private val currentCardId: String = scanResponse.card.cardId
// private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId)
private val secondCardId: String? = null
private var currentCardPublicKey: String? = null
private var secondCardPublicKey: String? = null
@ -60,13 +58,15 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
preparingMessage: Message,
creatingWalletMessage: Message,
): SimpleResult {
val task = CreateSecondTwinWalletTask(
firstPublicKey = currentCardPublicKey!!,
firstCardId = currentCardId,
issuerKeys = issuerKeyPair,
preparingMessage = preparingMessage,
creatingWalletMessage = creatingWalletMessage
)
val response = tangemSdkManager.runTaskAsync(
CreateSecondTwinWalletTask(
firstPublicKey = currentCardPublicKey!!,
issuerKeys = issuerKeyPair,
preparingMessage = preparingMessage,
creatingWalletMessage = creatingWalletMessage),
secondCardId, initialMessage
task, null, initialMessage
)
when (response) {
is CompletionResult.Success -> {
@ -116,6 +116,14 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)
}
fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean {
if (issuerData.size < 65) return false
val publicKey = issuerData.sliceArray(0 until 65)
val signedKey = issuerData.sliceArray(65 until issuerData.size)
return (cardWalletPublicKey != null &&
CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey))
}
private fun getIssuerKeys(context: Context, publicKey: String): KeyPair {
val issuer = getIssuers(context).first { it.publicKey == publicKey }
return KeyPair(

View file

@ -20,6 +20,19 @@ class TwinsHelper {
}
}
fun getPairCardSeries(cardId: String): String? {
return when (getTwinCardNumber(cardId) ?: return null) {
TwinCardNumber.First -> {
val index = firstCardSeries.indexOf(cardId.take(4))
secondCardSeries[index]
}
TwinCardNumber.Second -> {
val index = secondCardSeries.indexOf(cardId.take(4))
firstCardSeries[index]
}
}
}
fun getTwinCardIdForUser(cardId: String): String {
if (cardId.length < 16) return cardId

View file

@ -61,7 +61,7 @@ class WalletConnectSdkHelper {
val decimals = blockchain.decimals()
val value = transaction.value?.hexToBigDecimal()
val value = (transaction.value ?: "0").hexToBigDecimal()
?.movePointLeft(decimals) ?: return null
val gasPrice = transaction.gasPrice?.hexToBigDecimal()

View file

@ -170,12 +170,16 @@ private fun handleSecurityAction(
state.copy(confirmScreenState = confirmScreenState)
}
is DetailsAction.ManageSecurity.SaveChanges.Success -> {
// Setting options to show only LongTap from now on
// Setting options to show only LongTap from now on for non-twins
state.copy(
securityScreenState = state.securityScreenState?.copy(
currentOption = state.securityScreenState.selectedOption,
allowedOptions = EnumSet.of(SecurityOption.LongTap)
))
securityScreenState = state.securityScreenState?.copy(
currentOption = state.securityScreenState.selectedOption,
allowedOptions = state.card?.let {
prepareAllowedSecurityOptions(
it, state.securityScreenState.selectedOption
)
} ?: EnumSet.of(SecurityOption.LongTap)
))
}
else -> state
@ -187,7 +191,12 @@ private fun prepareAllowedSecurityOptions(
): EnumSet<SecurityOption> {
val prohibitDefaultPin = card?.settings?.isResettingUserCodesAllowed != true
val prohibitDefaultPin = card?.settings?.isRemovingAccessCodeAllowed != true
val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java)
if (card?.isTwinCard() == true) {
allowedSecurityOptions.add(SecurityOption.PassCode)
}
if ((currentSecurityOption == SecurityOption.LongTap) || !prohibitDefaultPin) {
allowedSecurityOptions.add(SecurityOption.LongTap)
}

View file

@ -151,8 +151,8 @@ sealed class WalletAction : Action {
object ShowOnboarding : TwinsAction()
object SetOnboardingShown : TwinsAction()
data class SetTwinCard(
val secondCardId: String?, val number: TwinCardNumber,
val isCreatingTwinCardsAllowed: Boolean,
val number: TwinCardNumber,
val isCreatingTwinCardsAllowed: Boolean,
) : TwinsAction()
}
}

View file

@ -199,7 +199,6 @@ data class TradeCryptoAvailability(
)
data class TwinCardsState(
val secondCardId: String?,
val cardNumber: TwinCardNumber?,
val showTwinOnboarding: Boolean,
val isCreatingTwinCardsAllowed: Boolean

View file

@ -10,7 +10,6 @@ class TwinsReducer {
is WalletAction.TwinsAction.SetTwinCard -> {
state.copy(
twinCardsState = TwinCardsState(
secondCardId = action.secondCardId,
cardNumber = action.number,
showTwinOnboarding = state.twinCardsState?.showTwinOnboarding
?: false,
@ -21,7 +20,7 @@ class TwinsReducer {
is WalletAction.TwinsAction.ShowOnboarding -> {
state.copy(
twinCardsState = state.twinCardsState?.copy(showTwinOnboarding = true)
?: TwinCardsState(null, null,
?: TwinCardsState(cardNumber = null,
showTwinOnboarding = true,
isCreatingTwinCardsAllowed = false)
)

View file

@ -8,7 +8,6 @@ import androidx.transition.TransitionInflater
import com.squareup.picasso.Picasso
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.twins.TwinsHelper
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
@ -34,10 +33,9 @@ class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards) {
super.onViewCreated(view, savedInstanceState)
store.dispatch(WalletAction.TwinsAction.SetOnboardingShown)
val secondCardId = store.state.walletState.twinCardsState?.secondCardId ?: ""
val secondTwinCardId = TwinsHelper.getTwinCardIdForUser(secondCardId)
val text = getString(R.string.twins_onboarding_description_format, secondTwinCardId)
val secondTwinNumber =
store.state.walletState.twinCardsState?.cardNumber?.pairNumber()?.number ?: ""
val text = getString(R.string.twins_onboarding_description_format, secondTwinNumber)
tv_twin_cards_description_1.text = text
setOnClickListeners()

View file

@ -94,7 +94,10 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
override fun newState(state: WalletState) {
if (activity == null) return
if (state.isMultiwalletAllowed && walletView is SingleWalletView) {
if (state.isMultiwalletAllowed &&
state.primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
walletView is SingleWalletView
) {
walletView = MultiWalletView()
walletView.changeWalletView(this)
} else if (!state.isMultiwalletAllowed && walletView is MultiWalletView) {