Updated on 2026-08-14
This commit is contained in:
parent
e922364e1a
commit
1f0173686f
8 changed files with 167 additions and 61 deletions
|
|
@ -15,15 +15,16 @@ import com.tangem.common.core.Config
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeyList
|
||||
import com.tangem.operations.pins.CheckUserCodesCommand
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.AnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
|
||||
import com.tangem.tap.domain.tasks.DerivationTask
|
||||
import com.tangem.tap.domain.tasks.DerivationTaskResponse
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
|
|
@ -77,10 +78,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
|
||||
suspend fun derivePublicKeys(
|
||||
cardId: String,
|
||||
walletPublicKey: ByteArray,
|
||||
derivationPaths: List<DerivationPath>
|
||||
): CompletionResult<ExtendedPublicKeyList> {
|
||||
return runTaskAsyncReturnOnMain(DeriveWalletPublicKeysTask(walletPublicKey, derivationPaths), cardId)
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
return runTaskAsyncReturnOnMain(DerivationTask(derivations), cardId)
|
||||
}
|
||||
|
||||
suspend fun resetToFactorySettings(card: Card): CompletionResult<Card> {
|
||||
|
|
|
|||
|
|
@ -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.common.card.EllipticCurve
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
||||
|
|
@ -25,4 +26,18 @@ fun Blockchain.minimalAmount(): BigDecimal {
|
|||
return 1.toBigDecimal().movePointLeft(decimals())
|
||||
}
|
||||
|
||||
fun Blockchain.getPrimaryCurve(): EllipticCurve? {
|
||||
return when {
|
||||
getSupportedCurves().contains(EllipticCurve.Secp256k1) -> {
|
||||
EllipticCurve.Secp256k1
|
||||
}
|
||||
getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
|
||||
EllipticCurve.Ed25519
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val NODL = "NODL"
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeyList
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
|
||||
class DerivationTaskResponse(
|
||||
val entries: Map<ByteArrayKey, ExtendedPublicKeyList>
|
||||
): CommandResponse
|
||||
|
||||
class DerivationTask(
|
||||
private val derivations: Map<ByteArrayKey, List<DerivationPath>>
|
||||
) : CardSessionRunnable<DerivationTaskResponse> {
|
||||
|
||||
val response: MutableMap<ByteArrayKey, ExtendedPublicKeyList> = mutableMapOf()
|
||||
|
||||
override fun run(session: CardSession, callback: CompletionCallback<DerivationTaskResponse>) {
|
||||
derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback)
|
||||
}
|
||||
|
||||
private fun derive(
|
||||
keys: List<ByteArrayKey>,
|
||||
index: Int,
|
||||
session: CardSession,
|
||||
callback: CompletionCallback<DerivationTaskResponse>
|
||||
) {
|
||||
if (index == keys.count()) {
|
||||
callback(CompletionResult.Success(DerivationTaskResponse(response.toMap())))
|
||||
return
|
||||
}
|
||||
|
||||
val key = keys[index]
|
||||
val paths = derivations[key]!!
|
||||
DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
response[key] = result.data
|
||||
derive(keys = keys, index = index + 1, session = session, callback = callback)
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,12 @@ import com.tangem.common.hdWallet.ExtendedPublicKey
|
|||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.backup.StartPrimaryCardLinkingTask
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.operations.wallet.CreateWalletTask
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.tap.domain.tasks.DerivationTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateWalletsTask
|
||||
import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey
|
||||
import com.tangem.tap.domain.tasks.product.ProductCommandProcessor
|
||||
|
|
@ -170,16 +170,15 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
|
|||
return
|
||||
}
|
||||
|
||||
DeriveWalletPublicKeysTask(response.wallet.publicKey, derivationPaths)
|
||||
DerivationTask(mapOf(response.wallet.publicKey.toMapKey() to derivationPaths))
|
||||
.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val derivedKeys = mapOf(response.wallet.publicKey.toMapKey() to result.data)
|
||||
callback(
|
||||
CompletionResult.Success(
|
||||
CreateProductWalletTaskResponse(
|
||||
card = session.environment.card!!,
|
||||
derivedKeys = derivedKeys,
|
||||
derivedKeys = result.data.entries,
|
||||
primaryCard = primaryCard
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import com.tangem.operations.PreflightReadTask
|
|||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.backup.StartPrimaryCardLinkingTask
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
|
|
@ -32,7 +31,9 @@ import com.tangem.tap.domain.TapWorkarounds
|
|||
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.tap.domain.TapWorkarounds.isExcluded
|
||||
import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease
|
||||
import com.tangem.tap.domain.extensions.getPrimaryCurve
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.tasks.DerivationTask
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.tap.domain.twins.TwinsHelper
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
|
@ -237,10 +238,8 @@ private class ScanWalletProcessor(
|
|||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit
|
||||
) {
|
||||
val derivationPaths = collectDerivationPaths(card)?.distinct()
|
||||
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
|
||||
|
||||
if (derivationPaths.isNullOrEmpty() || wallet == null || wallet.chainCode == null) {
|
||||
val derivations = collectDerivations(card)
|
||||
if (derivations.isEmpty()) {
|
||||
callback(
|
||||
CompletionResult.Success(
|
||||
ScanResponse(
|
||||
|
|
@ -254,15 +253,14 @@ private class ScanWalletProcessor(
|
|||
return
|
||||
}
|
||||
|
||||
DeriveWalletPublicKeysTask(wallet.publicKey, derivationPaths).run(session) { result ->
|
||||
DerivationTask(derivations).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val derivedKeys = mapOf(wallet.publicKey.toMapKey() to result.data)
|
||||
val response = ScanResponse(
|
||||
card = card,
|
||||
productType = ProductType.Wallet,
|
||||
walletData = session.environment.walletData,
|
||||
derivedKeys = derivedKeys,
|
||||
derivedKeys = result.data.entries,
|
||||
primaryCard = primaryCard
|
||||
)
|
||||
callback(CompletionResult.Success(response))
|
||||
|
|
@ -273,8 +271,8 @@ private class ScanWalletProcessor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun collectDerivationPaths(card: Card): List<DerivationPath>? {
|
||||
val currenciesRepository = currenciesRepository ?: return null
|
||||
private fun getBlockchainsToDerive(card: Card): List<Blockchain> {
|
||||
val currenciesRepository = currenciesRepository ?: return emptyList()
|
||||
val cardCurrencies = currenciesRepository.loadCardCurrencies(card.cardId)
|
||||
|
||||
val blockchainsToDerive = if (cardCurrencies == null) {
|
||||
|
|
@ -293,10 +291,31 @@ private class ScanWalletProcessor(
|
|||
)
|
||||
)
|
||||
}
|
||||
return blockchainsToDerive.distinct()
|
||||
}
|
||||
|
||||
return blockchainsToDerive.toSet()
|
||||
.filter { it.getSupportedCurves()?.contains(EllipticCurve.Secp256k1) == true }
|
||||
.mapNotNull { it.derivationPath() }
|
||||
private fun collectDerivations(card: Card): Map<ByteArrayKey, List<DerivationPath>> {
|
||||
val blockchains = getBlockchainsToDerive(card)
|
||||
val derivations = mutableMapOf<ByteArrayKey, List<DerivationPath>>()
|
||||
|
||||
blockchains.forEach { blockchain ->
|
||||
val curve = blockchain.getPrimaryCurve()
|
||||
|
||||
val wallet = card.wallets.firstOrNull { it.curve == curve } ?: return@forEach
|
||||
if (wallet.chainCode == null) return@forEach
|
||||
|
||||
val key = wallet.publicKey.toMapKey()
|
||||
val path = blockchain.derivationPath()
|
||||
if (path != null) {
|
||||
val addedDerivations = derivations[key]
|
||||
if (addedDerivations != null) {
|
||||
derivations[key] = addedDerivations + path
|
||||
} else {
|
||||
derivations[key] = listOf(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return derivations
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.wallet.R
|
|||
|
||||
sealed class CurrencyListItem {
|
||||
var isAdded: Boolean = false
|
||||
var isLock: Boolean = false
|
||||
var isLocked: Boolean = false
|
||||
|
||||
data class TokenListItem(val token: Token) : CurrencyListItem()
|
||||
data class BlockchainListItem(val blockchain: Blockchain) : CurrencyListItem()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.common.hdWallet.ExtendedPublicKey
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -13,6 +16,7 @@ import com.tangem.tap.currenciesRepository
|
|||
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
|
|
@ -45,27 +49,8 @@ class TokensMiddleware {
|
|||
cardFirmware = scanResponse.card.firmwareVersion,
|
||||
isTestNet = isTestcard
|
||||
)
|
||||
val currencies = CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList()
|
||||
if (scanResponse.supportsHdWallet()) {
|
||||
currencies.forEach {
|
||||
when (it) {
|
||||
is CurrencyListItem.TitleListItem -> {
|
||||
}
|
||||
is CurrencyListItem.BlockchainListItem -> {
|
||||
val firstCurve = it.blockchain.getSupportedCurves()?.firstOrNull()
|
||||
if (firstCurve == EllipticCurve.Ed25519) {
|
||||
it.isLock = true
|
||||
}
|
||||
}
|
||||
is CurrencyListItem.TokenListItem -> {
|
||||
val firstCurve = it.token.blockchain.getSupportedCurves()?.firstOrNull()
|
||||
if (firstCurve == EllipticCurve.Ed25519) {
|
||||
it.isLock = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val currencies =
|
||||
CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList()
|
||||
store.dispatch(TokensAction.LoadCurrencies.Success(currencies))
|
||||
}
|
||||
|
||||
|
|
@ -94,26 +79,33 @@ class TokensMiddleware {
|
|||
blockchains: List<Blockchain>,
|
||||
tokens: List<Token>
|
||||
) {
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: return
|
||||
|
||||
val tokenBlockchains = tokens.map { it.blockchain }
|
||||
val derivationPathsCandidates = (blockchains + tokenBlockchains).distinct()
|
||||
.mapNotNull { it.derivationPath() }
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys = scanResponse.derivedKeys[mapKeyOfWalletPublicKey]?.toMutableList() ?: mutableListOf()
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.map { it.derivationPath }
|
||||
|
||||
val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
val derivationDataList = listOfNotNull(
|
||||
getDerivations(EllipticCurve.Secp256k1, scanResponse, blockchains, tokens),
|
||||
getDerivations(EllipticCurve.Ed25519, scanResponse, blockchains, tokens)
|
||||
)
|
||||
val derivations = derivationDataList.map { it.derivations }.toMap()
|
||||
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.derivePublicKeys(scanResponse.card.cardId, wallet.publicKey, toDerive)
|
||||
val result = tangemSdkManager.derivePublicKeys(
|
||||
scanResponse.card.cardId,
|
||||
derivations
|
||||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val newDerivedKeys = result.data
|
||||
alreadyDerivedKeys.addAll(newDerivedKeys)
|
||||
val newDerivedKeys = result.data.entries
|
||||
val updatedDerivedKeys =
|
||||
mutableMapOf<KeyWalletPublicKey, List<ExtendedPublicKey>>()
|
||||
|
||||
newDerivedKeys.forEach { entry ->
|
||||
val derivationData = derivationDataList.find {
|
||||
it.mapKeyOfWalletPublicKey == entry.key
|
||||
} ?: return@forEach
|
||||
updatedDerivedKeys[entry.key] =
|
||||
derivationData.alreadyDerivedKeys + entry.value.toList()
|
||||
}
|
||||
|
||||
val updatedScanResponse = scanResponse.copy(
|
||||
derivedKeys = mapOf(mapKeyOfWalletPublicKey to alreadyDerivedKeys.toList())
|
||||
derivedKeys = updatedDerivedKeys
|
||||
)
|
||||
store.dispatch(GlobalAction.SaveScanNoteResponse(updatedScanResponse))
|
||||
submitAdd(blockchains, tokens)
|
||||
|
|
@ -128,6 +120,37 @@ class TokensMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getDerivations(
|
||||
curve: EllipticCurve,
|
||||
scanResponse: ScanResponse,
|
||||
blockchains: List<Blockchain>,
|
||||
tokens: List<Token>
|
||||
): DerivationData? {
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
|
||||
|
||||
val tokenBlockchains = tokens.map { it.blockchain }
|
||||
val derivationPathsCandidates = (blockchains + tokenBlockchains).distinct()
|
||||
.mapNotNull { it.derivationPath() }
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys =
|
||||
scanResponse.derivedKeys[mapKeyOfWalletPublicKey]?.toMutableList() ?: mutableListOf()
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.map { it.derivationPath }
|
||||
|
||||
val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
return DerivationData(
|
||||
derivations = mapKeyOfWalletPublicKey to toDerive,
|
||||
alreadyDerivedKeys = alreadyDerivedKeys,
|
||||
mapKeyOfWalletPublicKey = mapKeyOfWalletPublicKey
|
||||
)
|
||||
}
|
||||
|
||||
private class DerivationData(
|
||||
val derivations: Pair<ByteArrayKey, List<DerivationPath>>,
|
||||
val alreadyDerivedKeys: List<ExtendedPublicKey>,
|
||||
val mapKeyOfWalletPublicKey: ByteArrayKey
|
||||
)
|
||||
|
||||
private fun submitAdd(blockchains: List<Blockchain>, tokens: List<Token>) {
|
||||
(blockchains.map {
|
||||
WalletAction.MultiWallet.AddBlockchain(it)
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
}
|
||||
|
||||
private fun modifyAddTokenButton(currency: CurrencyListItem) {
|
||||
if (currency.isLock) {
|
||||
if (currency.isLocked) {
|
||||
view.btn_add_token.setText(R.string.common_add)
|
||||
view.btn_add_token.isEnabled = false
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue