Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-20 16:16:45 +03:00
parent 5bca4d6107
commit ac1a08f7f9
9 changed files with 155 additions and 87 deletions

View file

@ -52,31 +52,6 @@ class DerivationManagerImpl(
AddCryptoCurrenciesUseCase(currenciesRepository, networksRepository)
}
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 = getAppToken(currency)
val scanResponse = appStateHolder.scanResponse
if (scanResponse != null) {
val blockchainNetwork = BlockchainNetwork(blockchain, scanResponse.derivationStyleProvider)
val appCurrency = com.tangem.tap.domain.model.Currency.fromBlockchainNetwork(
blockchainNetwork,
appToken,
)
deriveMissingBlockchains(
scanResponse = scanResponse,
currencyList = listOf(appCurrency),
onSuccess = { continuation.resumeWith(Result.success(true)) },
) {
continuation.resumeWith(Result.failure(it))
}
}
} else {
continuation.resumeWith(Result.failure(IllegalStateException("no blockchain or card found")))
}
}
override suspend fun deriveAndAddTokens(currency: Currency) = suspendCoroutine { continuation ->
val selectedUserWallet = requireNotNull(
userWalletsListManager.selectedUserWalletSync,
@ -136,27 +111,6 @@ class DerivationManagerImpl(
}
}
override fun getDerivationPathForBlockchain(networkId: String): String? {
val scanResponse = appStateHolder.scanResponse
val blockchain = Blockchain.fromNetworkId(networkId)
if (scanResponse != null && blockchain != null) {
return blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())?.rawPath
}
return null
}
override fun hasDerivation(networkId: String, derivationPath: String): Boolean {
val scanResponse = appStateHolder.scanResponse
val blockchain = Blockchain.fromNetworkId(networkId)
if (scanResponse != null && blockchain != null) {
return scanResponse.hasDerivation(
blockchain,
derivationPath,
)
}
return false
}
private suspend fun addToken(
userWalletId: UserWalletId,
blockchain: Blockchain,

View file

@ -8,14 +8,22 @@ plugins {
dependencies {
/** Project*/
implementation(project(":core:datasource"))
implementation(project(":core:utils"))
implementation(project(":features:referral:domain"))
/** Project */
implementation(projects.core.datasource)
implementation(projects.core.utils)
/** Data modules */
implementation(projects.data.tokens)
/** Domain modules */
implementation(projects.domain.legacy)
implementation(projects.domain.models)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
implementation(projects.features.referral.domain)
/** Libs */
implementation(project(":libs:auth"))
implementation(projects.libs.auth)
/** Time */
implementation(deps.jodatime)
@ -23,4 +31,7 @@ dependencies {
/** DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
/** Tangem deps */
implementation(deps.tangem.blockchain)
}

View file

@ -1,11 +1,20 @@
package com.tangem.feature.referral.data
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.StartReferralBody
import com.tangem.datasource.demo.DemoModeDatasource
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.referral.converters.ReferralConverter
import com.tangem.feature.referral.domain.ReferralRepository
import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.TokenData
import com.tangem.lib.auth.AuthProvider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
@ -17,6 +26,7 @@ internal class ReferralRepositoryImpl @Inject constructor(
private val coroutineDispatcher: CoroutineDispatcherProvider,
private val demoModeDatasource: DemoModeDatasource,
private val authProvider: AuthProvider,
private val userWalletsStore: UserWalletsStore,
) : ReferralRepository {
override val isDemoMode: Boolean
@ -55,4 +65,38 @@ internal class ReferralRepositoryImpl @Inject constructor(
)
}
}
override suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency? {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("Wallet $userWalletId not found")
val derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider
val blockchain = Blockchain.fromNetworkId(tokenData.networkId)
?: error("Blockchain ${tokenData.networkId} not found")
val cryptoCurrencyFactory = CryptoCurrencyFactory()
val contractAddress = tokenData.contractAddress
val decimalCount = tokenData.decimalCount
return if (contractAddress != null && decimalCount != null) {
val sdkToken = Token(
name = tokenData.name,
symbol = tokenData.symbol,
contractAddress = contractAddress,
decimals = decimalCount,
id = tokenData.id,
)
cryptoCurrencyFactory.createToken(
sdkToken = sdkToken,
blockchain = blockchain,
extraDerivationPath = null,
derivationStyleProvider = derivationStyleProvider,
)
} else {
cryptoCurrencyFactory.createCoin(
blockchain = blockchain,
extraDerivationPath = null,
derivationStyleProvider = derivationStyleProvider,
)
}
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.feature.referral.di
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.demo.DemoModeDatasource
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.feature.referral.converters.ReferralConverter
import com.tangem.feature.referral.data.ReferralRepositoryImpl
import com.tangem.feature.referral.domain.ReferralRepository
@ -25,6 +26,7 @@ class ReferralRepositoryModule {
coroutineDispatcherProvider: CoroutineDispatcherProvider,
demoModeDatasource: DemoModeDatasource,
authProvider: AuthProvider,
userWalletsStore: UserWalletsStore,
): ReferralRepository {
return ReferralRepositoryImpl(
referralApi = tangemTechApi,
@ -32,6 +34,7 @@ class ReferralRepositoryModule {
coroutineDispatcher = coroutineDispatcherProvider,
demoModeDatasource = demoModeDatasource,
authProvider = authProvider,
userWalletsStore = userWalletsStore,
)
}
}

View file

@ -17,13 +17,23 @@ dependencies {
/** Core modules */
implementation(projects.libs.crypto)
/** Domain modules */
implementation(projects.domain.card)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
/** Feature Apis */
implementation(projects.features.tester.api)
implementation(projects.features.wallet.api)
/** Dependencies */
implementation(deps.arrow.core)
implementation(deps.jodatime)
implementation(deps.timber)
/** DI */
implementation(deps.hilt.core)
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
}

View file

@ -1,16 +1,27 @@
package com.tangem.feature.referral.domain
import arrow.core.getOrElse
import com.tangem.domain.card.DerivePublicKeysUseCase
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
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.tester.api.TesterFeatureToggles
import com.tangem.lib.crypto.DerivationManager
import com.tangem.lib.crypto.UserWalletManager
import timber.log.Timber
@Suppress("LongParameterList")
internal class ReferralInteractorImpl(
private val repository: ReferralRepository,
private val derivationManager: DerivationManager,
private val userWalletManager: UserWalletManager,
private val tokensConverter: TokensConverter,
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
private val testerFeatureToggles: TesterFeatureToggles,
) : ReferralInteractor {
private val tokensForReferral = mutableListOf<TokenData>()
@ -27,21 +38,59 @@ internal class ReferralInteractorImpl(
}
override suspend fun startReferral(): ReferralData {
if (tokensForReferral.isNotEmpty()) {
val currency = tokensConverter.convert(tokensForReferral.first())
val derivationPath = derivationManager.deriveAndAddTokens(currency)
val publicAddress = userWalletManager.getWalletAddress(currency.networkId, derivationPath)
return repository.startReferral(
walletId = userWalletManager.getWalletId(),
networkId = currency.networkId,
tokenId = currency.id,
address = publicAddress,
)
return if (tokensForReferral.isNotEmpty()) {
if (testerFeatureToggles.isDerivePublicKeysRefactoringEnabled) {
startReferralNew(tokenData = tokensForReferral.first())
} else {
// TODO: delete [REDACTED_JIRA]
startReferralLegacy(tokenData = tokensForReferral.first())
}
} else {
error("Tokens for ref is empty")
}
}
private suspend fun startReferralNew(tokenData: TokenData): ReferralData {
val userWallet = getSelectedWalletSyncUseCase().getOrElse {
error("Failed to get selected wallet: $it")
}
val cryptoCurrency = repository.getCryptoCurrency(userWalletId = userWallet.walletId, tokenData = tokenData)
derivePublicKeysUseCase(userWallet.walletId, listOfNotNull(cryptoCurrency)).getOrElse {
Timber.e("Failed to derive public keys: $it")
throw it
}
addCryptoCurrenciesUseCase(
userWalletId = userWallet.walletId,
currencies = listOfNotNull(cryptoCurrency),
)
val publicAddress = userWalletManager.getWalletAddress(
networkId = tokenData.networkId,
derivationPath = cryptoCurrency?.network?.derivationPath?.value,
)
return repository.startReferral(
walletId = userWalletManager.getWalletId(),
networkId = tokenData.networkId,
tokenId = tokenData.id,
address = publicAddress,
)
}
private suspend fun startReferralLegacy(tokenData: TokenData): ReferralData {
val currency = tokensConverter.convert(tokenData)
val derivationPath = derivationManager.deriveAndAddTokens(currency)
val publicAddress = userWalletManager.getWalletAddress(currency.networkId, derivationPath)
return repository.startReferral(
walletId = userWalletManager.getWalletId(),
networkId = currency.networkId,
tokenId = currency.id,
address = publicAddress,
)
}
private fun saveReferralTokens(tokens: List<TokenData>) {
tokensForReferral.clear()
tokensForReferral.addAll(tokens)

View file

@ -1,6 +1,9 @@
package com.tangem.feature.referral.domain
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.TokenData
interface ReferralRepository {
@ -11,4 +14,6 @@ interface ReferralRepository {
/** Starts user referral program */
suspend fun startReferral(walletId: String, networkId: String, tokenId: String, address: String): ReferralData
suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency?
}

View file

@ -1,34 +1,46 @@
package com.tangem.feature.referral.domain.di
import com.tangem.domain.card.DerivePublicKeysUseCase
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
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.tester.api.TesterFeatureToggles
import com.tangem.lib.crypto.DerivationManager
import com.tangem.lib.crypto.UserWalletManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
@Module
@InstallIn(SingletonComponent::class)
@InstallIn(ViewModelComponent::class)
class ReferralDomainModule {
@Provides
@Singleton
@ViewModelScoped
fun provideReferralInteractor(
referralRepository: ReferralRepository,
derivationManager: DerivationManager,
userWalletManager: UserWalletManager,
tokensConverter: TokensConverter,
derivePublicKeysUseCase: DerivePublicKeysUseCase,
getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
testerFeatureToggles: TesterFeatureToggles,
): ReferralInteractor {
return ReferralInteractorImpl(
repository = referralRepository,
derivationManager = derivationManager,
userWalletManager = userWalletManager,
tokensConverter = tokensConverter,
derivePublicKeysUseCase = derivePublicKeysUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase,
testerFeatureToggles = testerFeatureToggles,
)
}
}

View file

@ -2,29 +2,9 @@ package com.tangem.lib.crypto
import com.tangem.lib.crypto.models.Currency
// FIXME: Migration [REDACTED_JIRA]
@Deprecated(message = "Use DerivePublicKeysUseCase instead")
interface DerivationManager {
/**
* Derives missing blockchain and returns flag true if did it successfully
*
* @param currency to derive (Native token or not)
*/
suspend fun deriveMissingBlockchains(currency: Currency): Boolean
/**
* Returns derivationPath or null for blockchain with given networkId
*
* @param networkId
*/
fun getDerivationPathForBlockchain(networkId: String): String?
/**
* 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
*/