Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-26 15:27:06 +03:00
parent 3ef300ee56
commit 640e6433ff
7 changed files with 165 additions and 18 deletions

View file

@ -10,12 +10,17 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.common.util.hasDerivation
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.lib.crypto.DerivationManager
import com.tangem.lib.crypto.models.Currency
import com.tangem.lib.crypto.models.Currency.NonNativeToken
@ -28,6 +33,7 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware
import com.tangem.tap.scope
import com.tangem.tap.userWalletsListManager
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.coroutines.suspendCoroutine
@ -35,21 +41,14 @@ import com.tangem.tap.features.wallet.models.Currency as WalletModelCurrency
class DerivationManagerImpl(
private val appStateHolder: AppStateHolder,
private val currenciesRepository: CurrenciesRepository,
) : DerivationManager {
override suspend fun deriveMissingBlockchains(currency: Currency) = suspendCoroutine { continuation ->
val blockchain = Blockchain.fromNetworkId(currency.networkId)
val card = appStateHolder.getActualCard()
if (blockchain != null && card != null) {
val appToken = if (currency is NonNativeToken) {
Token(
symbol = currency.symbol,
contractAddress = currency.contractAddress,
decimals = currency.decimalCount,
)
} else {
null
}
val appToken = getAppToken(currency)
val scanResponse = appStateHolder.scanResponse
if (scanResponse != null) {
val blockchainNetwork = BlockchainNetwork(blockchain, scanResponse.derivationStyleProvider)
@ -70,6 +69,64 @@ class DerivationManagerImpl(
}
}
override suspend fun deriveAndAddTokens(currency: Currency) = suspendCoroutine { continuation ->
val selectedUserWallet = requireNotNull(
userWalletsListManager.selectedUserWalletSync,
) { "selectedUserWallet shouldn't be null" }
val scanResponse = selectedUserWallet.scanResponse
val blockchain = requireNotNull(
Blockchain.fromNetworkId(currency.networkId),
) { "unsupported blockchain" }
val derivationStyleProvider = scanResponse.derivationStyleProvider
val derivationPath = requireNotNull(
blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath,
) { "derivationPath shouldn't be null" }
val hasDerivation = scanResponse.hasDerivation(
blockchain,
derivationPath,
)
if (hasDerivation) {
scope.launch {
addToken(
userWalletId = selectedUserWallet.walletId,
blockchain = blockchain,
currency = currency,
derivationPath = derivationPath,
derivationStyleProvider = derivationStyleProvider,
)
}
} else {
val blockchainNetwork = BlockchainNetwork(blockchain, scanResponse.derivationStyleProvider)
val appCurrency = WalletModelCurrency.fromBlockchainNetwork(
blockchainNetwork,
getAppToken(currency),
)
deriveMissingBlockchains(
scanResponse = scanResponse,
currencyList = listOf(appCurrency),
onSuccess = { updatedScanResponse ->
scope.launch {
userWalletsListManager.update(
userWalletId = selectedUserWallet.walletId,
update = { it.copy(scanResponse = updatedScanResponse) },
)
addToken(
userWalletId = selectedUserWallet.walletId,
blockchain = blockchain,
currency = currency,
derivationPath = derivationPath,
derivationStyleProvider = derivationStyleProvider,
)
continuation.resumeWith(Result.success(derivationPath))
}
},
onFailure = {
continuation.resumeWith(Result.failure(it))
},
)
}
}
override fun getDerivationPathForBlockchain(networkId: String): String? {
val scanResponse = appStateHolder.scanResponse
val blockchain = Blockchain.fromNetworkId(networkId)
@ -91,6 +148,57 @@ class DerivationManagerImpl(
return false
}
private suspend fun addToken(
userWalletId: UserWalletId,
blockchain: Blockchain,
currency: Currency,
derivationPath: String,
derivationStyleProvider: DerivationStyleProvider,
) {
currenciesRepository.addCurrencies(
userWalletId,
listOf(
convertCurrency(
blockchain = blockchain,
currency = currency,
derivationPath = derivationPath,
derivationStyleProvider = derivationStyleProvider,
),
),
)
}
private fun convertCurrency(
blockchain: Blockchain,
currency: Currency,
derivationPath: String,
derivationStyleProvider: DerivationStyleProvider,
): CryptoCurrency {
val cryptoCurrencyFactory = CryptoCurrencyFactory()
return when (currency) {
is Currency.NativeToken -> {
cryptoCurrencyFactory.createCoin(
blockchain = blockchain,
extraDerivationPath = derivationPath,
derivationStyleProvider = derivationStyleProvider,
)
}
is NonNativeToken -> {
val sdkToken = Token(
symbol = currency.symbol,
contractAddress = currency.contractAddress,
decimals = currency.decimalCount,
)
cryptoCurrencyFactory.createToken(
sdkToken = sdkToken,
blockchain = blockchain,
extraDerivationPath = derivationPath,
derivationStyleProvider = derivationStyleProvider,
)
}
} as CryptoCurrency
}
private fun deriveMissingBlockchains(
scanResponse: ScanResponse,
currencyList: List<WalletModelCurrency>,
@ -204,6 +312,17 @@ class DerivationManagerImpl(
return TokensMiddleware.DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive)
}
private fun getAppToken(currency: Currency): Token? {
return if (currency is NonNativeToken) {
Token(
symbol = currency.symbol,
contractAddress = currency.contractAddress,
decimals = currency.decimalCount,
)
} else {
null
}
}
/**
* Simple error handler
* for now specifically handle only UserCancelled

View file

@ -94,8 +94,7 @@ class UserWalletManagerImpl(
UserWalletIdBuilder.card(it)
.build()
?.stringValue
}
?: ""
} ?: ""
}
override suspend fun isTokenAdded(currency: Currency, derivationPath: String?): Boolean {

View file

@ -6,6 +6,7 @@ import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
@ -66,9 +67,13 @@ class ProxyModule {
@Provides
@Singleton
fun provideDerivationManager(appStateHolder: AppStateHolder): DerivationManager {
fun provideDerivationManager(
appStateHolder: AppStateHolder,
currenciesRepository: CurrenciesRepository,
): DerivationManager {
return DerivationManagerImpl(
appStateHolder = appStateHolder,
currenciesRepository = currenciesRepository,
)
}

View file

@ -1,16 +1,26 @@
plugins {
alias(deps.plugins.kotlin.jvm)
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.kotlin.serialization)
id("configuration")
}
android {
namespace = "com.tangem.domain.referral"
}
dependencies {
/** Libs */
implementation(project(":core:utils"))
implementation(project(":libs:crypto"))
implementation(projects.core.utils)
/** Time */
/** Core modules */
implementation(projects.libs.crypto)
/** Feature Apis */
implementation(projects.features.wallet.api)
/** Dependencies */
implementation(deps.jodatime)
/** DI */

View file

@ -3,6 +3,7 @@ package com.tangem.feature.referral.domain
import com.tangem.feature.referral.domain.converter.TokensConverter
import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.TokenData
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.lib.crypto.DerivationManager
import com.tangem.lib.crypto.UserWalletManager
import com.tangem.lib.crypto.models.Currency
@ -12,6 +13,7 @@ internal class ReferralInteractorImpl(
private val derivationManager: DerivationManager,
private val userWalletManager: UserWalletManager,
private val tokensConverter: TokensConverter,
private val walletFeatureToggles: WalletFeatureToggles,
) : ReferralInteractor {
private val tokensForReferral = mutableListOf<TokenData>()
@ -30,7 +32,11 @@ internal class ReferralInteractorImpl(
override suspend fun startReferral(): ReferralData {
if (tokensForReferral.isNotEmpty()) {
val currency = tokensConverter.convert(tokensForReferral.first())
val derivationPath = deriveOrAddTokens(currency)
val derivationPath = if (walletFeatureToggles.isRedesignedScreenEnabled) {
derivationManager.deriveAndAddTokens(currency)
} else {
deriveAndAddTokens(currency)
}
val publicAddress = userWalletManager.getWalletAddress(currency.networkId, derivationPath)
return repository.startReferral(
walletId = userWalletManager.getWalletId(),
@ -43,7 +49,7 @@ internal class ReferralInteractorImpl(
}
}
private suspend fun deriveOrAddTokens(currency: Currency): String {
private suspend fun deriveAndAddTokens(currency: Currency): String {
val derivationPath = derivationManager.getDerivationPathForBlockchain(currency.networkId)
if (derivationPath.isNullOrEmpty()) error("derivationPath shouldn't be empty")
if (!derivationManager.hasDerivation(currency.networkId, derivationPath)) {

View file

@ -4,6 +4,7 @@ import com.tangem.feature.referral.domain.ReferralInteractor
import com.tangem.feature.referral.domain.ReferralInteractorImpl
import com.tangem.feature.referral.domain.ReferralRepository
import com.tangem.feature.referral.domain.converter.TokensConverter
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.lib.crypto.DerivationManager
import com.tangem.lib.crypto.UserWalletManager
import dagger.Module
@ -23,12 +24,14 @@ class ReferralDomainModule {
derivationManager: DerivationManager,
userWalletManager: UserWalletManager,
tokensConverter: TokensConverter,
walletFeatureToggles: WalletFeatureToggles,
): ReferralInteractor {
return ReferralInteractorImpl(
repository = referralRepository,
derivationManager = derivationManager,
userWalletManager = userWalletManager,
tokensConverter = tokensConverter,
walletFeatureToggles = walletFeatureToggles,
)
}
}

View file

@ -22,4 +22,9 @@ interface DerivationManager {
* Checks that given [networkId] has derivations for [derivationPath]
*/
fun hasDerivation(networkId: String, derivationPath: String): Boolean
/**
* Makes derivation for [Currency] if it is missing and adds token to wallet
*/
suspend fun deriveAndAddTokens(currency: Currency): String
}