Updated on 2026-08-14

This commit is contained in:
Tangem 2026-01-22 17:22:06 +03:00
parent 8126bf44b8
commit 45e78f3985
4 changed files with 66 additions and 0 deletions

View file

@ -12,6 +12,9 @@ android {
}
dependencies {
/* Tangem libraries */
implementation(tangemDeps.card.core)
/* Project - API */
implementation(projects.features.manageTokens.api)
implementation(projects.features.swapV2.api)

View file

@ -8,6 +8,7 @@ import arrow.core.getOrElse
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.managetokens.ValidateDerivationPathUseCase
import com.tangem.features.managetokens.utils.CardanoDerivationPathValidator
import com.tangem.domain.managetokens.model.exceptoin.DerivationPathValidationException
import com.tangem.domain.models.network.Network
import com.tangem.features.managetokens.component.CustomTokenDerivationInputComponent
@ -27,6 +28,8 @@ internal class DefaultCustomTokenDerivationInputComponent @AssistedInject constr
private val validateDerivationPathUseCase: ValidateDerivationPathUseCase,
) : CustomTokenDerivationInputComponent, AppComponentContext by context {
private val cardanoDerivationPathValidator = CardanoDerivationPathValidator()
private val state: MutableStateFlow<CustomDerivationInputUM> = MutableStateFlow(
value = getInitialState(),
)
@ -73,6 +76,20 @@ internal class DefaultCustomTokenDerivationInputComponent @AssistedInject constr
return
}
val isInvalidForCardano = cardanoDerivationPathValidator.isInvalidForCardano(
networkId = params.selectedNetwork.id,
path = value,
)
if (isInvalidForCardano) {
state.update { state ->
state.copy(
error = resourceReference(R.string.custom_token_invalid_derivation_path),
isConfirmEnabled = false,
)
}
return
}
state.update { state ->
state.copy(
error = null,

View file

@ -33,6 +33,7 @@ import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork
import com.tangem.features.managetokens.entity.item.DerivationPathUM
import com.tangem.features.managetokens.entity.item.SelectableItemUM
import com.tangem.features.managetokens.impl.R
import com.tangem.features.managetokens.utils.CardanoDerivationPathValidator
import com.tangem.features.managetokens.utils.mapper.toCurrencyNetworkModel
import com.tangem.features.managetokens.utils.mapper.toDerivationPathModel
import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
@ -56,6 +57,7 @@ internal class CustomTokenSelectorModel @Inject constructor(
) : Model() {
private val params: CustomTokenSelectorComponent.Params = paramsContainer.require()
private val cardanoDerivationPathValidator = CardanoDerivationPathValidator()
val dialogNavigation: SlotNavigation<CustomTokenSelectorDialogConfig> = SlotNavigation()
@ -142,6 +144,14 @@ internal class CustomTokenSelectorModel @Inject constructor(
return@mapNotNullTo null // Skip default path
}
val isInvalidForCardano = cardanoDerivationPathValidator.isInvalidForCardano(
networkId = selector.selectedNetwork.id,
path = network.derivationPath.value,
)
if (isInvalidForCardano) {
return@mapNotNullTo null
}
network.toDerivationPathModel(
isSelected = network.id == selector.selectedDerivationPath?.id,
onSelectedStateChange = {

View file

@ -0,0 +1,36 @@
package com.tangem.features.managetokens.utils
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.models.network.Network
/**
* Validator for Cardano derivation paths.
* Cardano requires exactly 5 nodes in derivation path according to CIP-1852 standard.
*/
internal class CardanoDerivationPathValidator {
/**
* Checks if the derivation path is invalid for Cardano network.
* @return true if network is Cardano AND path has insufficient nodes
*/
fun isInvalidForCardano(networkId: Network.ID, path: String?): Boolean {
if (!isCardano(networkId) || path == null) return false
return !isValidDerivationPath(path)
}
private fun isCardano(networkId: Network.ID): Boolean {
return networkId.toBlockchain() == Blockchain.Cardano
}
private fun isValidDerivationPath(path: String): Boolean {
return runCatching {
DerivationPath(rawPath = path).nodes.size == REQUIRED_DERIVATION_NODES
}.getOrDefault(false)
}
private companion object {
const val REQUIRED_DERIVATION_NODES = 5
}
}