Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-21 11:27:27 +04:00
parent e07246d25d
commit 9491a9cae6
12 changed files with 82 additions and 50 deletions

View file

@ -1,23 +0,0 @@
package com.tangem.tap.common.extensions
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.Locale
// TODO: move extensions to utils
fun BigDecimal.toFormattedString(
decimals: Int,
roundingMode: RoundingMode = RoundingMode.DOWN,
locale: Locale = Locale.US,
): String {
val symbols = DecimalFormatSymbols(locale)
val df = DecimalFormat()
df.decimalFormatSymbols = symbols
df.maximumFractionDigits = decimals
df.minimumFractionDigits = 0
df.isGroupingUsed = true
df.roundingMode = roundingMode
return df.format(this)
}

View file

@ -17,7 +17,6 @@ import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
// TODO remove it after test after resolve [REDACTED_JIRA]
internal class ResetBackupCardTask( internal class ResetBackupCardTask(
private val userWalletId: UserWalletId, private val userWalletId: UserWalletId,
) : CardSessionRunnable<Boolean> { ) : CardSessionRunnable<Boolean> {

View file

@ -28,5 +28,4 @@ class TestingCoroutineDispatcherProvider(
override val io: CoroutineDispatcher = Dispatchers.Unconfined, override val io: CoroutineDispatcher = Dispatchers.Unconfined,
override val default: CoroutineDispatcher = Dispatchers.Unconfined, override val default: CoroutineDispatcher = Dispatchers.Unconfined,
override val single: CoroutineDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher(), override val single: CoroutineDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher(),
) : CoroutineDispatcherProvider ) : CoroutineDispatcherProvider

View file

@ -49,6 +49,7 @@ class CryptoCurrencyFactory(
blockchain: Blockchain, blockchain: Blockchain,
extraDerivationPath: String?, extraDerivationPath: String?,
userWallet: UserWallet, userWallet: UserWallet,
accountIndex: DerivationIndex? = null,
): CryptoCurrency.Token? { ): CryptoCurrency.Token? {
if (blockchain == Blockchain.Unknown) { if (blockchain == Blockchain.Unknown) {
Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain")
@ -59,6 +60,7 @@ class CryptoCurrencyFactory(
blockchain = blockchain, blockchain = blockchain,
extraDerivationPath = extraDerivationPath, extraDerivationPath = extraDerivationPath,
userWallet = userWallet, userWallet = userWallet,
accountIndex = accountIndex,
) ?: return null ) ?: return null
val id = getTokenId(network, sdkToken) val id = getTokenId(network, sdkToken)
@ -75,11 +77,16 @@ class CryptoCurrencyFactory(
) )
} }
fun createCoin(chainId: Int, extraDerivationPath: String?, userWallet: UserWallet): CryptoCurrency.Coin? { fun createCoin(
chainId: Int,
extraDerivationPath: String?,
userWallet: UserWallet,
accountIndex: DerivationIndex? = null,
): CryptoCurrency.Coin? {
val blockchain: Blockchain? = Chain.entries.find { it.id == chainId }?.blockchain val blockchain: Blockchain? = Chain.entries.find { it.id == chainId }?.blockchain
return if (blockchain != null) { return if (blockchain != null) {
createCoin(blockchain, extraDerivationPath, userWallet) createCoin(blockchain, extraDerivationPath, userWallet, accountIndex)
} else { } else {
Timber.e("Unable to get blockchain from chainId == $chainId") Timber.e("Unable to get blockchain from chainId == $chainId")
null null

View file

@ -53,7 +53,7 @@ class UserTokensResponseFactory @Inject constructor() {
fun createDefaultResponse( fun createDefaultResponse(
userWallet: UserWallet?, userWallet: UserWallet?,
networkFactory: NetworkFactory, networkFactory: NetworkFactory,
accountId: AccountId? = null, accountId: AccountId?,
): UserTokensResponse { ): UserTokensResponse {
val tokens = userWallet?.let { val tokens = userWallet?.let {
getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig()) getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig())

View file

@ -9,6 +9,7 @@ import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.StartReferralBody import com.tangem.datasource.api.tangemTech.models.StartReferralBody
import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.referral.converters.ReferralConverter import com.tangem.feature.referral.converters.ReferralConverter
@ -20,7 +21,6 @@ import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject import javax.inject.Inject
@Suppress("LongParameterList")
internal class ReferralRepositoryImpl @Inject constructor( internal class ReferralRepositoryImpl @Inject constructor(
private val referralApi: TangemTechApi, private val referralApi: TangemTechApi,
private val referralConverter: ReferralConverter, private val referralConverter: ReferralConverter,
@ -74,7 +74,11 @@ internal class ReferralRepositoryImpl @Inject constructor(
} }
} }
override suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency? { override suspend fun getCryptoCurrency(
userWalletId: UserWalletId,
tokenData: TokenData,
accountIndex: DerivationIndex?,
): CryptoCurrency? {
val userWallet = withContext(coroutineDispatcher.io) { val userWallet = withContext(coroutineDispatcher.io) {
userWalletsStore.getSyncOrNull(userWalletId) ?: error("Wallet $userWalletId not found") userWalletsStore.getSyncOrNull(userWalletId) ?: error("Wallet $userWalletId not found")
} }
@ -97,12 +101,14 @@ internal class ReferralRepositoryImpl @Inject constructor(
blockchain = blockchain, blockchain = blockchain,
extraDerivationPath = null, extraDerivationPath = null,
userWallet = userWallet, userWallet = userWallet,
accountIndex = accountIndex,
) )
} else { } else {
cryptoCurrencyFactory.createCoin( cryptoCurrencyFactory.createCoin(
blockchain = blockchain, blockchain = blockchain,
extraDerivationPath = null, extraDerivationPath = null,
userWallet = userWallet, userWallet = userWallet,
accountIndex = accountIndex,
) )
} }
} }

View file

@ -1,6 +1,7 @@
package com.tangem.feature.referral.domain package com.tangem.feature.referral.domain
import com.tangem.domain.models.PortfolioId import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.referral.domain.models.ReferralData import com.tangem.feature.referral.domain.models.ReferralData
@ -12,5 +13,9 @@ interface ReferralInteractor {
suspend fun startReferral(portfolioId: PortfolioId): ReferralData suspend fun startReferral(portfolioId: PortfolioId): ReferralData
suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency? suspend fun getCryptoCurrency(
userWalletId: UserWalletId,
tokenData: TokenData,
accountIndex: DerivationIndex?,
): CryptoCurrency?
} }

View file

@ -2,8 +2,11 @@ package com.tangem.feature.referral.domain
import arrow.core.getOrElse import arrow.core.getOrElse
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.domain.account.producer.SingleAccountProducer
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.models.PortfolioId import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
@ -23,6 +26,7 @@ internal class ReferralInteractorImpl(
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase, private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val singleAccountSupplier: SingleAccountSupplier,
) : ReferralInteractor { ) : ReferralInteractor {
private val tokensForReferral = mutableListOf<TokenData>() private val tokensForReferral = mutableListOf<TokenData>()
@ -44,7 +48,23 @@ internal class ReferralInteractorImpl(
error("Failed to get user wallet $userWalletId: $it") error("Failed to get user wallet $userWalletId: $it")
} }
val cryptoCurrency = repository.getCryptoCurrency(userWalletId = userWallet.walletId, tokenData = tokenData) val accountIndex = when (portfolioId) {
is PortfolioId.Account -> {
val account = singleAccountSupplier.getSyncOrNull(
params = SingleAccountProducer.Params(accountId = portfolioId.accountId),
)
?: error("Account not found: ${portfolioId.accountId}")
account.derivationIndex
}
is PortfolioId.Wallet -> null
}
val cryptoCurrency = getCryptoCurrency(
userWalletId = portfolioId.userWalletId,
tokenData = tokenData,
accountIndex = accountIndex,
)
?: error("Failed to create crypto currency") ?: error("Failed to create crypto currency")
when (portfolioId) { when (portfolioId) {
@ -65,13 +85,10 @@ internal class ReferralInteractorImpl(
} }
.onLeft(Timber::e) .onLeft(Timber::e)
val publicAddress = when (portfolioId) { val publicAddress = userWalletManager.getWalletAddress(
is PortfolioId.Account -> TODO("account") networkId = tokenData.networkId,
is PortfolioId.Wallet -> userWalletManager.getWalletAddress( derivationPath = cryptoCurrency.network.derivationPath.value,
networkId = tokenData.networkId, )
derivationPath = cryptoCurrency.network.derivationPath.value,
)
}
return repository.startReferral( return repository.startReferral(
walletId = userWalletManager.getWalletId(), walletId = userWalletManager.getWalletId(),
@ -81,8 +98,17 @@ internal class ReferralInteractorImpl(
) )
} }
override suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency? = override suspend fun getCryptoCurrency(
repository.getCryptoCurrency(userWalletId = userWalletId, tokenData = tokenData) userWalletId: UserWalletId,
tokenData: TokenData,
accountIndex: DerivationIndex?,
): CryptoCurrency? {
return repository.getCryptoCurrency(
userWalletId = userWalletId,
tokenData = tokenData,
accountIndex = accountIndex,
)
}
private fun saveReferralTokens(tokens: List<TokenData>) { private fun saveReferralTokens(tokens: List<TokenData>) {
tokensForReferral.clear() tokensForReferral.clear()

View file

@ -1,5 +1,6 @@
package com.tangem.feature.referral.domain package com.tangem.feature.referral.domain
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.referral.domain.models.ReferralData import com.tangem.feature.referral.domain.models.ReferralData
@ -16,5 +17,10 @@ interface ReferralRepository {
/** Starts user referral program */ /** Starts user referral program */
suspend fun startReferral(walletId: String, networkId: String, tokenId: String, address: String): ReferralData suspend fun startReferral(walletId: String, networkId: String, tokenId: String, address: String): ReferralData
suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency? /** Returns [CryptoCurrency] by [tokenData] for specific [userWalletId] and optional [accountIndex] */
suspend fun getCryptoCurrency(
userWalletId: UserWalletId,
tokenData: TokenData,
accountIndex: DerivationIndex?,
): CryptoCurrency?
} }

View file

@ -3,6 +3,7 @@ package com.tangem.feature.referral.domain.di
import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -27,6 +28,7 @@ class ReferralDomainModule {
getUserWalletUseCase: GetUserWalletUseCase, getUserWalletUseCase: GetUserWalletUseCase,
addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase, manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
singleAccountSupplier: SingleAccountSupplier,
): ReferralInteractor { ): ReferralInteractor {
return ReferralInteractorImpl( return ReferralInteractorImpl(
repository = referralRepository, repository = referralRepository,
@ -35,6 +37,7 @@ class ReferralDomainModule {
getUserWalletUseCase = getUserWalletUseCase, getUserWalletUseCase = getUserWalletUseCase,
addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase, addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase,
manageCryptoCurrenciesUseCase = manageCryptoCurrenciesUseCase, manageCryptoCurrenciesUseCase = manageCryptoCurrenciesUseCase,
singleAccountSupplier = singleAccountSupplier,
) )
} }
} }

View file

@ -121,15 +121,20 @@ internal class ReferralModel @Inject constructor(
flow4 = portfolioFetcher.data, flow4 = portfolioFetcher.data,
) { pair, isBalanceHidden, appCurrency, portfolios -> ) { pair, isBalanceHidden, appCurrency, portfolios ->
val selectedAccount = pair?.second ?: return@combine null val selectedAccount = pair?.second ?: return@combine null
val awardCryptoCurrency = referralInteractor.getCryptoCurrency(
userWalletId = params.userWalletId,
tokenData = referralData.getToken(),
) ?: return@combine null
val cryptoPortfolio = when (selectedAccount) { val cryptoPortfolio = when (selectedAccount) {
is AccountStatus.CryptoPortfolio -> selectedAccount is AccountStatus.CryptoPortfolio -> selectedAccount
} }
val awardCryptoCurrency = referralInteractor.getCryptoCurrency(
userWalletId = params.userWalletId,
tokenData = referralData.getToken(),
accountIndex = cryptoPortfolio.account.derivationIndex,
) ?: return@combine null
val accountAwardToken = cryptoPortfolio.tokenList.flattenCurrencies() val accountAwardToken = cryptoPortfolio.tokenList.flattenCurrencies()
.find { it.currency.id == awardCryptoCurrency.id } .find { it.currency.id == awardCryptoCurrency.id }
return@combine AccountAwardConverter( return@combine AccountAwardConverter(
isBalanceHidden = isBalanceHidden, isBalanceHidden = isBalanceHidden,
awardCryptoCurrency = awardCryptoCurrency, awardCryptoCurrency = awardCryptoCurrency,
@ -142,12 +147,12 @@ internal class ReferralModel @Inject constructor(
} }
private fun createInitiallyUiState() = ReferralStateHolder( private fun createInitiallyUiState() = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState( headerState = HeaderState(
onBackClicked = appRouter::pop, onBackClicked = appRouter::pop,
), ),
referralInfoState = ReferralInfoState.Loading, referralInfoState = ReferralInfoState.Loading,
errorSnackbar = null, errorSnackbar = null,
analytics = ReferralStateHolder.Analytics( analytics = Analytics(
onAgreementClicked = ::onAgreementClicked, onAgreementClicked = ::onAgreementClicked,
onCopyClicked = ::onCopyClicked, onCopyClicked = ::onCopyClicked,
onShareClicked = ::onShareClicked, onShareClicked = ::onShareClicked,

View file

@ -1,5 +1,4 @@
# docs https://docs.gradle.org/current/userguide/platforms.html # docs https://docs.gradle.org/current/userguide/platforms.html
# TODO: update versions refs https://tangem.atlassian.net/browse/AND-3195
[versions] [versions]
# region Classpath # region Classpath