diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index 6665cab485..1533313216 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -82,7 +82,7 @@ fun WalletManager?.getAddressData(): WalletDataModel.AddressData? { } fun WalletManager.Companion.stub(): T { - val wallet = Wallet(Blockchain.Unknown, setOf(), Wallet.PublicKey(byteArrayOf(), null, null), setOf()) + val wallet = Wallet(Blockchain.Unknown, setOf(), Wallet.PublicKey(byteArrayOf(), null), setOf()) return object : WalletManager(wallet) { override val currentHost: String = "" override suspend fun update() {} diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index d04cb9a62e..c45f4e3490 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -1,5 +1,6 @@ package com.tangem.tap.domain.tasks.product +import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.common.Blockchain import com.tangem.common.CompletionResult import com.tangem.common.card.Card @@ -217,7 +218,8 @@ private class ScanWalletProcessor( walletData = session.environment.walletData, primaryCard = primaryCard, ) - val derivations = collectDerivations(card, config, scanResponse.derivationStyleProvider) + val derivations = + collectDerivations(card, config, scanResponse.derivationStyleProvider) if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { callback(CompletionResult.Success(scanResponse)) return@launch @@ -242,60 +244,101 @@ private class ScanWalletProcessor( val userTokensRepository = userTokensRepository ?: return emptyList() val blockchainsToDerive = userTokensRepository.loadBlockchainsToDerive(card) .toMutableList() - .ifEmpty { - mutableListOf( - BlockchainNetwork( - blockchain = Blockchain.Bitcoin, - derivationStyleProvider = derivationStyleProvider, - ), - BlockchainNetwork( - blockchain = Blockchain.Ethereum, - derivationStyleProvider = derivationStyleProvider, - ), - ) - } + .ifEmpty { getDefaultBlockchains(derivationStyleProvider) } if (card.settings.isHDWalletAllowed) { - blockchainsToDerive.addAll( - listOf( - BlockchainNetwork( - blockchain = Blockchain.Ethereum, - derivationStyleProvider = derivationStyleProvider, - ), - BlockchainNetwork( - blockchain = Blockchain.EthereumTestnet, - derivationStyleProvider = derivationStyleProvider, - ), - ), - ) + blockchainsToDerive += getEthereumBlockchains(derivationStyleProvider) } - if (additionalBlockchainsToDerive != null) { - blockchainsToDerive.addAll( - additionalBlockchainsToDerive.map { - BlockchainNetwork( - blockchain = it, - derivationStyleProvider = derivationStyleProvider, - ) - }, - ) + + additionalBlockchainsToDerive?.let { + blockchainsToDerive += getAdditionalBlockchainToDerive(derivationStyleProvider, it) } + + // we should generate second key for cardano + // because cardano address generation for wallet2 requires keys from 2 derivations + // https://developers.cardano.org/docs/get-started/cardano-serialization-lib/generating-keys/ + val secondCardanoNetwork = blockchainsToDerive + .find { it.blockchain == Blockchain.Cardano } + ?.let { getCardanoSecondNetwork(it) } + secondCardanoNetwork?.let { blockchainsToDerive.add(it) } + + // pay attention to this if (!card.useOldStyleDerivation) { - blockchainsToDerive.removeAll( - listOf( - Blockchain.BSC, Blockchain.BSCTestnet, - Blockchain.Polygon, Blockchain.PolygonTestnet, - Blockchain.RSK, - Blockchain.Fantom, Blockchain.FantomTestnet, - Blockchain.Avalanche, Blockchain.AvalancheTestnet, - ).map { - BlockchainNetwork( - blockchain = it, - derivationStyleProvider = derivationStyleProvider, - ) - }, + removeUnnecessaryBlockchains(blockchainsToDerive, derivationStyleProvider) + } + + return blockchainsToDerive.distinct() + } + + private fun getDefaultBlockchains( + derivationStyleProvider: DerivationStyleProvider, + ): MutableList { + return mutableListOf( + BlockchainNetwork( + blockchain = Blockchain.Bitcoin, + derivationStyleProvider = derivationStyleProvider, + ), + BlockchainNetwork( + blockchain = Blockchain.Ethereum, + derivationStyleProvider = derivationStyleProvider, + ), + ) + } + + private fun getEthereumBlockchains(derivationStyleProvider: DerivationStyleProvider): List { + return listOf( + BlockchainNetwork( + blockchain = Blockchain.Ethereum, + derivationStyleProvider = derivationStyleProvider, + ), + BlockchainNetwork( + blockchain = Blockchain.EthereumTestnet, + derivationStyleProvider = derivationStyleProvider, + ), + ) + } + + private fun getAdditionalBlockchainToDerive( + derivationStyleProvider: DerivationStyleProvider, + collection: Collection, + ): List { + return collection.map { + BlockchainNetwork( + blockchain = it, + derivationStyleProvider = derivationStyleProvider, ) } - return blockchainsToDerive.distinct() + } + + private fun getCardanoSecondNetwork(cardanoBlockchainNetwork: BlockchainNetwork): BlockchainNetwork? { + val cardanoStandardDerivation = cardanoBlockchainNetwork.derivationPath?.let { DerivationPath(it) } + ?: return null + val cardanoPatchedDerivation = CardanoUtils.extendedDerivationPath(cardanoStandardDerivation) + return BlockchainNetwork( + blockchain = Blockchain.Cardano, + derivationPath = cardanoPatchedDerivation.rawPath, + tokens = emptyList(), + ) + } + + private fun removeUnnecessaryBlockchains( + blockchainsToDerive: MutableList, + derivationStyleProvider: DerivationStyleProvider, + ) { + blockchainsToDerive.removeAll( + listOf( + Blockchain.BSC, Blockchain.BSCTestnet, + Blockchain.Polygon, Blockchain.PolygonTestnet, + Blockchain.RSK, + Blockchain.Fantom, Blockchain.FantomTestnet, + Blockchain.Avalanche, Blockchain.AvalancheTestnet, + ).map { + BlockchainNetwork( + blockchain = it, + derivationStyleProvider = derivationStyleProvider, + ) + }, + ) } private suspend fun collectDerivations( diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt index 12f3f24dd3..231e7d7bed 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt @@ -16,6 +16,7 @@ object Wallet2CardConfig : CardConfig { /** * Logic to determine primary curve for blockchain in TangemWallet 2.0 + * Order is important here */ override fun primaryCurve(blockchain: Blockchain): EllipticCurve? { // order is important, new curve is preferred for wallet 2 @@ -29,6 +30,10 @@ object Wallet2CardConfig : CardConfig { blockchain.getSupportedCurves().contains(EllipticCurve.Bls12381G2Aug) -> { EllipticCurve.Bls12381G2Aug } + // only for support cardano on Wallet2 + blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> { + EllipticCurve.Ed25519 + } else -> { Timber.e("Unsupported blockchain, curve not found") null diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt index f65ca67b3e..551f75119b 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt @@ -1,10 +1,13 @@ package com.tangem.domain.common.extensions +import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.common.* import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toMapKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.configs.CardConfig @@ -12,6 +15,7 @@ import com.tangem.domain.common.configs.Wallet2CardConfig import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse +import com.tangem.blockchain.common.CardanoAddressConfig fun WalletManagerFactory.makeWalletManagerForApp( scanResponse: ScanResponse, @@ -45,18 +49,19 @@ fun WalletManagerFactory.makeWalletManagerForApp( } scanResponse.card.settings.isHDWalletAllowed && seedKey != null && derivationParams != null -> { val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()] - val derivationPath = when (derivationParams) { - is DerivationParams.Default -> blockchain.derivationPath(derivationParams.style) - is DerivationParams.Custom -> derivationParams.path - } - val derivedKey = derivedKeys?.get(derivationPath) - ?: return null + val derivationPath = derivationParams.getPath(blockchain) + + val publicKey = makePublicKey( + seedKey = wallet.publicKey, + blockchain = blockchain, + derivationPath = derivationPath ?: return null, + derivedWalletKeys = derivedKeys ?: return null, + isWallet2 = scanResponse.cardTypesResolver.isWallet2(), + ) createWalletManager( blockchain = environmentBlockchain, - seedKey = wallet.publicKey, - derivedKey = derivedKey, - derivation = derivationParams, + publicKey = publicKey, ) } else -> { @@ -69,6 +74,45 @@ fun WalletManagerFactory.makeWalletManagerForApp( } } +private fun makePublicKey( + seedKey: ByteArray, + blockchain: Blockchain, + derivationPath: DerivationPath, + derivedWalletKeys: Map, + isWallet2: Boolean, +): Wallet.PublicKey { + val derivedKey = derivedWalletKeys[derivationPath] ?: error("No derivation found") + + val derivationKey = Wallet.HDKey( + path = derivationPath, + extendedPublicKey = derivedKey, + ) + + // we should generate second key for cardano + // because cardano address generation for wallet2 requires keys from 2 derivations + // https://developers.cardano.org/docs/get-started/cardano-serialization-lib/generating-keys/ + if (blockchain == Blockchain.Cardano) { + CardanoAddressConfig.useExtendedAddressing = isWallet2 + + if (isWallet2) { + val extendedDerivationPath = CardanoUtils.extendedDerivationPath(derivationPath) + val secondDerivedKey = derivedWalletKeys[extendedDerivationPath] ?: error("No derivation found") + + val secondDerivationKey = Wallet.HDKey(secondDerivedKey, extendedDerivationPath) + + return Wallet.PublicKey( + seedKey = seedKey, + derivationType = Wallet.PublicKey.DerivationType.Double(derivationKey, secondDerivationKey), + ) + } + } + + return Wallet.PublicKey( + seedKey = seedKey, + derivationType = Wallet.PublicKey.DerivationType.Plain(derivationKey), + ) +} + private fun getDerivationParams(card: CardDTO): DerivationParams? { return if (!card.settings.isHDWalletAllowed) { null diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 0588397643..e2d2bf63ad 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-322" +tangemBlockchainSdk = "develop-325" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-289" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds