diff --git a/data/common/detekt-baseline-debug.xml b/data/common/detekt-baseline-debug.xml
deleted file mode 100644
index c9e3828304..0000000000
--- a/data/common/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
- MultilineLambdaItParameter:ApiResponseRaise.kt${ Timber.e(it, "Unable to perform safe API call") onError(it) }
- MultilineLambdaItParameter:DefaultCardCryptoCurrencyFactory.kt$DefaultCardCryptoCurrencyFactory${ cryptoCurrencyFactory.createCoin( blockchain = it, extraDerivationPath = null, userWallet = userWallet, ) }
- MultilineLambdaItParameter:UserTokensResponseFactory.kt$UserTokensResponseFactory${ getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig) .map { blockchain -> val derivationPath = networkFactory.createDerivationPath( blockchain = blockchain, extraDerivationPath = null, cardDerivationStyleProvider = userWallet.derivationStyleProvider, ).value UserTokensResponse.Token( id = blockchain.toCoinId(), accountId = accountId?.value, networkId = blockchain.toNetworkId(), derivationPath = derivationPath, name = blockchain.getCoinName(), symbol = blockchain.currency, decimals = blockchain.decimals(), contractAddress = null, ) } }
- MultilineLambdaItParameter:UserTokensSaver.kt$UserTokensSaver${ if (accountsFeatureToggles.isFeatureEnabled) { it.enrichByAccountId(userWalletId = userWalletId) } else { it } }
- NamedArguments:CryptoCurrencyFactory.kt$CryptoCurrencyFactory$createCoin(blockchain, extraDerivationPath, userWallet, accountIndex)
- NoNameShadowing:DefaultETagsStore.kt$DefaultETagsStore$key
- UnnecessaryLet:DefaultCardCryptoCurrencyFactory.kt$DefaultCardCryptoCurrencyFactory$let(::listOf)
- UseOrEmpty:UserTokensResponseAddressesEnricher.kt$UserTokensResponseAddressesEnricher$withTimeoutOrNull( FETCH_TIMEOUT_SECONDS.seconds, { multiNetworkStatusSupplier.invoke(MultiNetworkStatusProducer.Params(userWalletId)).first() }, ) ?: emptySet()
-
-
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/api/ApiResponseRaise.kt b/data/common/src/main/kotlin/com/tangem/data/common/api/ApiResponseRaise.kt
index bda26c7b94..4cdf5d1178 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/api/ApiResponseRaise.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/api/ApiResponseRaise.kt
@@ -66,8 +66,8 @@ suspend inline fun safeApiCall(
crossinline onError: suspend (ApiResponseError) -> T,
): T = recover(
block = { call(ApiResponseRaise(raise = this)) },
- recover = {
- Timber.e(it, "Unable to perform safe API call")
- onError(it)
+ recover = { error ->
+ Timber.e(error, "Unable to perform safe API call")
+ onError(error)
},
)
\ No newline at end of file
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/DefaultETagsStore.kt b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/DefaultETagsStore.kt
index 90921f262e..3fe201e5de 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/DefaultETagsStore.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/DefaultETagsStore.kt
@@ -18,9 +18,9 @@ internal class DefaultETagsStore(
) : ETagsStore {
override suspend fun getSyncOrNull(userWalletId: UserWalletId, key: ETagsStore.Key): String? {
- val key = getAccountsETagKey(userWalletId = userWalletId, key = key)
+ val storeKey = getAccountsETagKey(userWalletId = userWalletId, key = key)
- return appPreferencesStore.getSyncOrNull(key = key)
+ return appPreferencesStore.getSyncOrNull(key = storeKey)
}
override suspend fun store(userWalletId: UserWalletId, key: ETagsStore.Key, value: String) {
@@ -29,14 +29,14 @@ internal class DefaultETagsStore(
return
}
- val key = getAccountsETagKey(userWalletId = userWalletId, key = key)
+ val storeKey = getAccountsETagKey(userWalletId = userWalletId, key = key)
- appPreferencesStore.store(key = key, value = value)
+ appPreferencesStore.store(key = storeKey, value = value)
}
override suspend fun clear(userWalletId: UserWalletId, key: ETagsStore.Key) {
- val key = getAccountsETagKey(userWalletId = userWalletId, key = key)
- appPreferencesStore.editData { it.remove(key) }
+ val storeKey = getAccountsETagKey(userWalletId = userWalletId, key = key)
+ appPreferencesStore.editData { it.remove(storeKey) }
}
private fun getAccountsETagKey(userWalletId: UserWalletId, key: ETagsStore.Key): Preferences.Key {
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt
index 2541ac4d46..14bb34f708 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt
@@ -86,7 +86,12 @@ class CryptoCurrencyFactory(
val blockchain: Blockchain? = Chain.entries.find { it.id == chainId }?.blockchain
return if (blockchain != null) {
- createCoin(blockchain, extraDerivationPath, userWallet, accountIndex)
+ createCoin(
+ blockchain = blockchain,
+ extraDerivationPath = extraDerivationPath,
+ userWallet = userWallet,
+ accountIndex = accountIndex,
+ )
} else {
Timber.e("Unable to get blockchain from chainId == $chainId")
null
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt
index f10df788e9..626f998bfb 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt
@@ -89,7 +89,7 @@ internal class DefaultCardCryptoCurrencyFactory(
}
// single-currency wallet
- return createPrimaryCurrencyForSingleCurrencyCard(userWallet).let(::listOf)
+ return listOf(createPrimaryCurrencyForSingleCurrencyCard(userWallet))
}
override suspend fun createCurrenciesForMultiCurrencyCard(
@@ -106,9 +106,9 @@ internal class DefaultCardCryptoCurrencyFactory(
val blockchains = getDefaultWalletBlockchains(userWallet, demoConfig)
- return blockchains.mapNotNull {
+ return blockchains.mapNotNull { blockchain ->
cryptoCurrencyFactory.createCoin(
- blockchain = it,
+ blockchain = blockchain,
extraDerivationPath = null,
userWallet = userWallet,
)
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAddressesEnricher.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAddressesEnricher.kt
index 2b155a84fc..aff3f5c5fa 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAddressesEnricher.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseAddressesEnricher.kt
@@ -27,7 +27,7 @@ class UserTokensResponseAddressesEnricher @Inject constructor(
withTimeoutOrNull(
FETCH_TIMEOUT_SECONDS.seconds,
{ multiNetworkStatusSupplier.invoke(MultiNetworkStatusProducer.Params(userWalletId)).first() },
- ) ?: emptySet()
+ ).orEmpty()
} else {
emptySet()
}
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseFactory.kt
index 77f5282614..85bf2f102f 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensResponseFactory.kt
@@ -55,8 +55,8 @@ class UserTokensResponseFactory @Inject constructor() {
networkFactory: NetworkFactory,
accountId: AccountId?,
): UserTokensResponse {
- val tokens = userWallet?.let {
- getDefaultWalletBlockchains(userWallet = it, demoConfig = DemoConfig)
+ val tokens = if (userWallet != null) {
+ getDefaultWalletBlockchains(userWallet = userWallet, demoConfig = DemoConfig)
.map { blockchain ->
val derivationPath = networkFactory.createDerivationPath(
blockchain = blockchain,
@@ -75,12 +75,14 @@ class UserTokensResponseFactory @Inject constructor() {
contractAddress = null,
)
}
+ } else {
+ emptyList()
}
return UserTokensResponse(
group = UserTokensResponse.GroupType.NONE,
sort = UserTokensResponse.SortType.MANUAL,
- tokens = tokens.orEmpty(),
+ tokens = tokens,
)
}
}
\ No newline at end of file
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt
index bcac2b6715..990995357d 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt
@@ -91,11 +91,11 @@ class UserTokensSaver(
return this
.enrichByAddress(userWalletId = userWalletId)
- .let {
+ .let { response ->
if (accountsFeatureToggles.isFeatureEnabled) {
- it.enrichByAccountId(userWalletId = userWalletId)
+ response.enrichByAccountId(userWalletId = userWalletId)
} else {
- it
+ response
}
}
}
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt
index 1e7906cfbb..d3291a9f0c 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt
@@ -53,6 +53,7 @@ class NetworkFactory @Inject constructor(
blockchain = blockchain,
excludedBlockchains = excludedBlockchains,
),
+ accountIndex = accountIndex,
)
}
@@ -118,6 +119,7 @@ class NetworkFactory @Inject constructor(
accountIndex = accountIndex,
),
canHandleTokens = canHandleTokens,
+ accountIndex = accountIndex,
)
}
@@ -125,8 +127,10 @@ class NetworkFactory @Inject constructor(
blockchain: Blockchain,
derivationPath: Network.DerivationPath,
canHandleTokens: Boolean,
+ accountIndex: DerivationIndex? = null,
): Network? {
if (!blockchain.isBlockchainSupported()) return null
+ if (blockchain == Blockchain.Chia && accountIndex != DerivationIndex.Main) return null
return runCatching {
Network(
diff --git a/data/visa/detekt-baseline-debug.xml b/data/visa/detekt-baseline-debug.xml
index 4c936a4a08..bf83464b1c 100644
--- a/data/visa/detekt-baseline-debug.xml
+++ b/data/visa/detekt-baseline-debug.xml
@@ -14,7 +14,6 @@
NullCheckOnMutableProperty:VisaLibLoader.kt$VisaLibLoader$if (config != null) return@withLock requireNotNull(config)
NullCheckOnMutableProperty:VisaLibLoader.kt$VisaLibLoader$if (provider != null) return@withLock requireNotNull(provider)
NullableToStringCall:DefaultOnboardingRepository.kt$DefaultOnboardingRepository$${error.message}
- NullableToStringCall:DefaultTangemPayCardDetailsRepository.kt$DefaultTangemPayCardDetailsRepository$${response.result?.status}
NullableToStringCall:TangemPayRequestPerformer.kt$TangemPayRequestPerformer$${error.message}
RedundantSuspendModifier:DefaultVisaRepository.kt$DefaultVisaRepository$suspend
SuspendFunSwallowedCancellation:DefaultVisaRepository.kt$DefaultVisaRepository$runCatching
diff --git a/detekt_baseline_report.txt b/detekt_baseline_report.txt
index b8cef02ecb..8bd49ad41c 100644
--- a/detekt_baseline_report.txt
+++ b/detekt_baseline_report.txt
@@ -1,7 +1,7 @@
==========================================
Detekt Baseline Updater & Issue Counter
==========================================
-Date: 2025-11-19 15:21:36
+Date: 2025-11-20 16:34:32
Updating detekt baseline for debug variant...
@@ -13,13 +13,13 @@ Counting issues in baseline files...
==========================================
Summary:
- Total Issues: 1778
- Modules with Issues: 91
+ Total Issues: 1744
+ Modules with Issues: 90
Average Issues per Module: 19
Progress:
- Fixed: 24 out of 1802 (1%)
- Remaining: 1778
+ Fixed: 58 out of 1802 (3%)
+ Remaining: 1744
==========================================
All Modules with Issues (sorted by count)
@@ -33,7 +33,7 @@ features/onboarding-v2/impl 137
features/onramp/impl 89
features/send-v2/impl 80
features/swap/impl 73
-features/hot-wallet/impl 66
+features/hot-wallet/impl 61
features/staking/impl 56
data/wallet-connect 55
features/swap-v2/impl 53
@@ -49,9 +49,8 @@ domain/tokens 28
features/swap/domain 27
core/ui 27
common/ui 26
-features/tangempay/details/impl 24
-data/visa 24
-features/wallet-settings/impl 21
+data/visa 23
+features/tangempay/details/impl 22
data/nft 20
features/swap/data 15
data/swap 13
@@ -71,7 +70,6 @@ data/networks 9
features/welcome/impl 8
features/home/impl 8
domain/transaction 8
-data/common 8
data/account 8
libs/tangem-sdk-api 7
data/txhistory 7
@@ -88,6 +86,7 @@ features/account/impl 4
features/account/api 4
data/promo 4
core/config-toggles 4
+features/wallet-settings/impl 3
features/txhistory/impl 3
features/tangempay/onboarding/impl 3
features/create-wallet-start/impl 3
diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/utils/CryptoCurrencyBalanceFetcher.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/utils/CryptoCurrencyBalanceFetcher.kt
index adde054406..f492131ba6 100644
--- a/domain/account/status/src/main/java/com/tangem/domain/account/status/utils/CryptoCurrencyBalanceFetcher.kt
+++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/utils/CryptoCurrencyBalanceFetcher.kt
@@ -68,13 +68,11 @@ class CryptoCurrencyBalanceFetcher(
source to error
}
- check(errors.isEmpty()) {
+ if (errors.isNotEmpty()) {
val message = "Failed to fetch next sources for $userWalletId:\n" +
errors.joinToString(separator = "\n") { "${it.first.name} – ${it.second}" }
Timber.e(message)
-
- message
}
}
}
diff --git a/features/hot-wallet/impl/detekt-baseline-debug.xml b/features/hot-wallet/impl/detekt-baseline-debug.xml
index 49d05f161c..75e6e8dacb 100644
--- a/features/hot-wallet/impl/detekt-baseline-debug.xml
+++ b/features/hot-wallet/impl/detekt-baseline-debug.xml
@@ -3,8 +3,6 @@
BooleanPropertyNaming:AccessCodeModel.kt$AccessCodeModel$var buttonClicked = false
- BooleanPropertyNaming:AccessCodeUM.kt$AccessCodeUM$val buttonEnabled: Boolean
- BooleanPropertyNaming:AccessCodeUM.kt$AccessCodeUM$val buttonInProgress: Boolean
BooleanPropertyNaming:AddExistingWalletImportUM.kt$AddExistingWalletImportUM$val importWalletEnabled: Boolean
BooleanPropertyNaming:AddExistingWalletImportUM.kt$AddExistingWalletImportUM$val importWalletProgress: Boolean
BooleanPropertyNaming:AddExistingWalletImportUM.kt$AddExistingWalletImportUM$val readyToImport: Boolean
@@ -24,8 +22,6 @@
BooleanPropertyNaming:WalletBackupUM.kt$WalletBackupUM$val backedUp: Boolean
BooleanPropertyNaming:WalletHardwareBackupUM.kt$WalletHardwareBackupUM$val showPurchaseBlock: Boolean = false
MaxChainedCallsOnSameLine:UpgradeWalletModel.kt$UpgradeWalletModel$it.card.wallets.map { it.curve }.toSet().isNotEmpty()
- MultilineLambdaItParameter:AccessCodeModel.kt$AccessCodeModel${ Timber.e(it) uiState.update { it.copy(buttonInProgress = false) } }
- MultilineLambdaItParameter:AccessCodeModel.kt$AccessCodeModel${ it.copy( accessCode = value, buttonEnabled = if (params.accessCodeToConfirm != null) { value == params.accessCodeToConfirm } else { value.length == uiState.value.accessCodeLength }, accessCodeColor = when { params.accessCodeToConfirm == null -> PinTextColor.Primary value.length != uiState.value.accessCodeLength -> PinTextColor.Primary value == params.accessCodeToConfirm -> PinTextColor.Primary else -> PinTextColor.WrongCode }, ) }
MultilineLambdaItParameter:AddExistingWalletImportModel.kt$AddExistingWalletImportModel${ Timber.e(it) setImportProgress(false) }
MultilineLambdaItParameter:AddExistingWalletImportModel.kt$AddExistingWalletImportModel${ setImportProgress(false) when (it) { is SaveWalletError.DataError -> Timber.e(it.toString(), "Unable to save user wallet") is SaveWalletError.WalletAlreadySaved -> { uiMessageSender.send( SnackbarMessage(resourceReference(R.string.hw_import_seed_phrase_already_imported)), ) } } }
MultilineLambdaItParameter:CreateHardwareWalletModel.kt$CreateHardwareWalletModel${ delay(HIDE_PROGRESS_DELAY) setLoading(false) when (it) { is SaveWalletError.DataError -> Timber.e(it.toString(), "Unable to save user wallet") is SaveWalletError.WalletAlreadySaved -> { userWalletsListRepository.unlock( userWalletId = userWallet.walletId, unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(scanResponse), ).onRight { router.replaceAll(AppRoute.Wallet) } } } }
@@ -56,7 +52,6 @@
MultilineLambdaItParameter:UpgradeWalletModel.kt$UpgradeWalletModel${ // Check if user attempted to upgrade before but something went wrong and a full reset is required val userWallet = coldUserWalletBuilderFactory.create(it).build() val sameWalletButNotFinishedBackup by lazy { userWallet?.walletId == params.userWalletId && BackupValidator.isValidFull(it.card).not() } val otherWalletAndAlreadyCreated by lazy { userWallet?.walletId != params.userWalletId && it.card.wallets.map { it.curve }.toSet().isNotEmpty() } if (userWallet != null && (sameWalletButNotFinishedBackup || otherWalletAndAlreadyCreated)) { startResetCardsFlow.emit(userWallet) return@doOnSuccess } delay(DELAY_SDK_DIALOG_CLOSE) tangemSdkManager.changeDisplayedCardIdNumbersCount(it) navigateToUpgradeFlow(it) }
MultilineLambdaItParameter:ViewPhraseContent.kt${ EnumeratedTwoColumnGridItem( index = it + 1, mnemonic = "word${it + 1}", ) }
MultilineLambdaItParameter:ViewPhraseModel.kt$ViewPhraseModel${ it.copy( words = words.mapIndexed { index, s -> EnumeratedTwoColumnGridItem(index + 1, s) }.toImmutableList(), ) }
- NoNameShadowing:AccessCodeModel.kt$AccessCodeModel${ it.copy(buttonInProgress = false) }
NoNameShadowing:CreateMobileWalletModel.kt$CreateMobileWalletModel${ it.copy(createButtonLoading = false) }
NoNameShadowing:ManualBackupCheckModel.kt$ManualBackupCheckModel${ it.copy(completeButtonProgress = false) }
PropertyUsedBeforeDeclaration:AddExistingWalletImportModel.kt$AddExistingWalletImportModel$uiState
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt
index 5bda3aeb4f..4bbeec4cee 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt
@@ -28,10 +28,12 @@ internal class AvailableToAddDataConverter @Inject constructor(
availableNetworks: Set,
marketParams: TokenMarketParams,
): AvailableToAddData {
- suspend fun AccountStatus.getAvailableToAddAccount(wallet: UserWallet): AvailableToAddAccount {
+ suspend fun AccountStatus.getAvailableToAddAccount(wallet: UserWallet): AvailableToAddAccount? {
val currencies = availableNetworks
.mapNotNull { createCryptoCurrency(wallet, it, marketParams, this.account) }
+ if (currencies.isEmpty()) return null
+
val addedNetworks = getAccountCurrencyStatusUseCase.invokeSync(wallet.walletId, currencies)
.fold(
ifEmpty = { emptySet() },
@@ -52,12 +54,15 @@ internal class AvailableToAddDataConverter @Inject constructor(
suspend fun getAvailableToAddWallet(
entry: Map.Entry,
): AvailableToAddWallet {
- val (walletId, balance) = entry
+ val (_, balance) = entry
val wallet = balance.userWallet
val filteredNetworks = wallet.filteredAvailableNetworks(availableNetworks)
val accounts = balance.accountsBalance.accountStatuses
val availableToAddAccounts: Map = accounts
- .map { it.account.accountId to it.getAvailableToAddAccount(wallet) }
+ .mapNotNull { accountStatus ->
+ val availableToAddAccount = accountStatus.getAvailableToAddAccount(wallet) ?: return@mapNotNull null
+ accountStatus.account.accountId to availableToAddAccount
+ }
.filter { (_, account) -> account.availableToAddNetworks.isNotEmpty() }
.toMap()
return AvailableToAddWallet(
diff --git a/features/tangempay/details/impl/detekt-baseline-debug.xml b/features/tangempay/details/impl/detekt-baseline-debug.xml
index 6c37bced10..8e67fe2685 100644
--- a/features/tangempay/details/impl/detekt-baseline-debug.xml
+++ b/features/tangempay/details/impl/detekt-baseline-debug.xml
@@ -9,12 +9,10 @@
BooleanPropertyNaming:TangemPayChangePinUM.kt$TangemPayChangePinUM$val submitButtonLoading: Boolean
BooleanPropertyNaming:TangemPayDetailsScreen.kt$var showDropdownMenu by rememberSaveable { mutableStateOf(false) }
BooleanPropertyNaming:TangemPayDetailsUM.kt$TangemPayDetailsUM$val addFundsEnabled: Boolean
- BooleanPropertyNaming:TangemPayFreezeUnfreezeStateTransformer.kt$TangemPayFreezeUnfreezeStateTransformer$private val frozen: Boolean
BooleanPropertyNaming:TangemPayTxHistoryListManager.kt$TangemPayTxHistoryListManager$val clearUiBatches = state.status is PaginationStatus.InitialLoading && batchListState.status is PaginationStatus.Paginating
CanBeNonNullable:TangemPayTxHistoryDetailsModel.kt$TangemPayTxHistoryDetailsModel$txHash: String?
MaxChainedCallsOnSameLine:TangemPayTxHistoryUiManager.kt$TangemPayTxHistoryUiManager$prevBatch?.data?.lastOrNull()?.date?.millis?.toDateFormatWithTodayYesterday()
MultilineLambdaItParameter:TangemPayAddFundsContent.kt${ key(it.title) { TangemPayTopUpItem(state = it) } }
- MultilineLambdaItParameter:TangemPayCardDetailsBlockModel.kt$TangemPayCardDetailsBlockModel${ uiState.update( transformer = DetailsRevealedStateTransformer( details = it, onClickHide = ::hideCardDetails, ), ) }
MultilineLambdaItParameter:TangemPayDetailsScreen.kt${ TangemDropdownItem( item = it.dropdownItem, dismissParent = { showDropdownMenu = false }, ) }
MultilineLambdaItParameter:TangemPayTxHistoryUiManager.kt$TangemPayTxHistoryUiManager${ it.status !is PaginationStatus.None && it.status !is PaginationStatus.InitialLoading && it.status !is PaginationStatus.InitialLoadingError }
NullCheckOnMutableProperty:GoogleWalletUtil.kt$GoogleWalletUtil$if (walletIntent != null) { walletIntent } else { try { context.packageManager.getLaunchIntentForPackage(WALLET_PACKAGE_NAME) ?.apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } .also { walletIntent = it } } catch (exception: Exception) { Timber.tag(TAG).e(exception) null } }
diff --git a/libs/crypto/build.gradle.kts b/libs/crypto/build.gradle.kts
index 2adf84b11c..4b9238c713 100644
--- a/libs/crypto/build.gradle.kts
+++ b/libs/crypto/build.gradle.kts
@@ -32,8 +32,7 @@ dependencies {
// endregion
// region Test libraries
- testImplementation(deps.test.junit5)
+ testImplementation(projects.test.core)
testRuntimeOnly(deps.test.junit5.engine)
- testImplementation(deps.test.truth)
// endregion
}
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/derivation/AccountNodeRecognizer.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/derivation/AccountNodeRecognizer.kt
index 37c9b75e4d..d89a55b6bc 100644
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/derivation/AccountNodeRecognizer.kt
+++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/derivation/AccountNodeRecognizer.kt
@@ -25,17 +25,15 @@ class AccountNodeRecognizer(private val blockchain: Blockchain) {
val nodesCount = derivationPath.nodes.size
val index = when {
- blockchain == Blockchain.Tezos -> {
- UTXO_BLOCKCHAIN_NODE_INDEX.takeIf { nodesCount == 4 }
- }
- blockchain == Blockchain.Quai || blockchain.isUTXO -> {
- UTXO_BLOCKCHAIN_NODE_INDEX.takeIf { nodesCount == 5 }
- }
- !blockchain.isUTXO -> {
- (nodesCount - 1).takeIf { nodesCount == 3 || nodesCount == 5 }
- }
- else -> null
+ // region Ethereum-like blockchains
+ blockchain == Blockchain.Tezos -> ACCOUNT_NODE_INDEX
+ blockchain == Blockchain.Quai -> ACCOUNT_NODE_INDEX
+ blockchain.isEvm() -> ADDRESS_INDEX_NODE_INDEX
+ // endregion
+ blockchain.isUTXO -> ACCOUNT_NODE_INDEX
+ else -> ACCOUNT_NODE_INDEX
}
+ .takeIf { nodesCount >= it }
if (index == null) {
Timber.e("Cannot determine account node index for ${blockchain.fullName}: ${derivationPath.rawPath}")
@@ -251,6 +249,7 @@ class AccountNodeRecognizer(private val blockchain: Blockchain) {
}
private companion object {
- const val UTXO_BLOCKCHAIN_NODE_INDEX = 2
+ const val ACCOUNT_NODE_INDEX = 2
+ const val ADDRESS_INDEX_NODE_INDEX = 4
}
}
\ No newline at end of file
diff --git a/libs/crypto/src/test/kotlin/com/tangem/lib/crypto/derivation/AccountNodeRecognizerTest.kt b/libs/crypto/src/test/kotlin/com/tangem/lib/crypto/derivation/AccountNodeRecognizerTest.kt
index 89b8cfd373..4d004e0616 100644
--- a/libs/crypto/src/test/kotlin/com/tangem/lib/crypto/derivation/AccountNodeRecognizerTest.kt
+++ b/libs/crypto/src/test/kotlin/com/tangem/lib/crypto/derivation/AccountNodeRecognizerTest.kt
@@ -3,175 +3,214 @@ package com.tangem.lib.crypto.derivation
import com.google.common.truth.Truth
import com.tangem.blockchain.common.Blockchain
import com.tangem.crypto.hdWallet.DerivationPath
+import com.tangem.domain.models.network.Network
+import com.tangem.test.core.ProvideTestModels
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.TestInstance
+import org.junit.jupiter.params.ParameterizedTest
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class AccountNodeRecognizerTest {
private val utxoBlockchain = Blockchain.Bitcoin
- private val ethLikeBlockchain = Blockchain.Ethereum
@Nested
- inner class RecognizeAsDerivationPath {
+ @TestInstance(TestInstance.Lifecycle.PER_CLASS)
+ inner class Recognize {
- @Test
- fun `returns account node value for a derivation path with 5 nodes and UTXO blockchain`() {
+ @ParameterizedTest
+ @ProvideTestModels
+ fun recognize(model: TestModel) {
// Arrange
- val recognizer = AccountNodeRecognizer(utxoBlockchain)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/1'/0/0")
+ val recognizer = AccountNodeRecognizer(blockchain = model.blockchain)
+ val derivationPath = DerivationPath(rawPath = model.derivationPath)
// Act
val actual = recognizer.recognize(derivationPath)
// Assert
- val expected = 1
- Truth.assertThat(actual).isEqualTo(expected)
+ Truth.assertThat(actual).isEqualTo(model.expected)
}
- @Test
- fun `returns account node value for a derivation path with 4 nodes and UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(utxoBlockchain)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/1'/0")
-
- // Act
- val actual = recognizer.recognize(derivationPath)
-
- // Assert
- Truth.assertThat(actual).isNull()
+ private fun provideTestModels(): List {
+ return provideEthLikeTestModels() +
+ provideUTXOTestModels() +
+ provideOtherBlockchainTests()
}
- @Test
- fun `returns account node value for a derivation path with 5 nodes and non-UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/1'/2/3")
+ private fun provideEthLikeTestModels() = listOf(
+ // region Tezos blockchain
+ TestModel(
+ blockchain = Blockchain.Tezos,
+ derivationPath = "m/44'/1729'/1'/0/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Tezos,
+ derivationPath = "m/44'/1729'/1'/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Tezos,
+ derivationPath = "m/44'/1729'/1'",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Tezos,
+ derivationPath = "m/44'/1729'",
+ expected = null,
+ ),
+ // endregion
- // Act
- val actual = recognizer.recognize(derivationPath)
+ // region Quai blockchain
+ TestModel(
+ blockchain = Blockchain.Quai,
+ derivationPath = "m/44'/994'/1'/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Quai,
+ derivationPath = "m/44'/994'/1'",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Quai,
+ derivationPath = "m/44'/994'",
+ expected = null,
+ ),
+ // endregion
- // Assert
- val expected = 3
- Truth.assertThat(actual).isEqualTo(expected)
- }
+ // region Ethereum-like blockchain
+ TestModel(
+ blockchain = Blockchain.Ethereum,
+ derivationPath = "m/44'/60'/0'/0/1",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Ethereum,
+ derivationPath = "m/44'/60'/0'/0",
+ expected = null,
+ ),
+ TestModel(
+ blockchain = Blockchain.Ethereum,
+ derivationPath = "m/44'/60'/0'",
+ expected = null,
+ ),
+ TestModel(
+ blockchain = Blockchain.Ethereum,
+ derivationPath = "m/44'/60'",
+ expected = null,
+ ),
+ // endregion
+ )
- @Test
- fun `returns account node value for a derivation path with 4 nodes and non-UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/1'/2")
+ private fun provideUTXOTestModels() = listOf(
+ // region Bitcoin blockchain
+ TestModel(
+ blockchain = Blockchain.Bitcoin,
+ derivationPath = "m/44'/0'/1'/0/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Bitcoin,
+ derivationPath = "m/44'/0'/1'/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Bitcoin,
+ derivationPath = "m/44'/0'/1'",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Bitcoin,
+ derivationPath = "m/44'/0'",
+ expected = null,
+ ),
+ TestModel(
+ blockchain = Blockchain.Bitcoin,
+ derivationPath = "m/44'",
+ expected = null,
+ ),
+ // endregion
+ )
- // Act
- val actual = recognizer.recognize(derivationPath)
+ private fun provideOtherBlockchainTests(): List = listOf(
+ // region Solana blockchain
+ TestModel(
+ blockchain = Blockchain.Solana,
+ derivationPath = "m/44'/501'/1'",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Solana,
+ derivationPath = "m/44'/501'",
+ expected = null,
+ ),
+ // endregion
- // Assert
- Truth.assertThat(actual).isNull()
- }
+ // region Cardano blockchain
+ TestModel(
+ blockchain = Blockchain.Cardano,
+ derivationPath = "m/1852'/1815'/1'/0/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Cardano,
+ derivationPath = "m/1852'/1815'/1'/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Cardano,
+ derivationPath = "m/1852'/1815'/1'",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Cardano,
+ derivationPath = "m/1852'/1815'",
+ expected = null,
+ ),
+ // endregion
- @Test
- fun `returns account node value for a derivation path with 3 nodes and non-UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/1'")
-
- // Act
- val actual = recognizer.recognize(derivationPath)
-
- // Assert
- val expected = 1
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns account node value for a derivation path with 4 nodes and Tezos blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Tezos)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/0/5'")
-
- // Act
- val actual = recognizer.recognize(derivationPath)
-
- // Assert
- val expected = 0
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns account node value for a derivation path with 5 nodes and Tezos blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Tezos)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/0'/5/1")
-
- // Act
- val actual = recognizer.recognize(derivationPath)
-
- // Assert
- Truth.assertThat(actual).isNull()
- }
-
- @Test
- fun `returns account node value for a derivation path with 4 nodes and Quai blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Quai)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/0/5'")
-
- // Act
- val actual = recognizer.recognize(derivationPath)
-
- // Assert
- Truth.assertThat(actual).isNull()
- }
-
- @Test
- fun `returns account node value for a derivation path with 5 nodes and Quai blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Quai)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'/0'/5/1")
-
- // Act
- val actual = recognizer.recognize(derivationPath)
-
- // Assert
- val expected = 0
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns null if derivation path is shorter than expected`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPath = DerivationPath(rawPath = "m/44'/0'")
-
- // Act
- val actual = recognizer.recognize(derivationPath)
-
- // Assert
- Truth.assertThat(actual).isNull()
- }
+ // region Tron blockchain
+ TestModel(
+ blockchain = Blockchain.Tron,
+ derivationPath = "m/44'/195'/1'/0/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Tron,
+ derivationPath = "m/44'/195'/1'/0",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Tron,
+ derivationPath = "m/44'/195'/1'",
+ expected = 1,
+ ),
+ TestModel(
+ blockchain = Blockchain.Tron,
+ derivationPath = "m/44'/195'",
+ expected = null,
+ ),
+ // endregion
+ )
}
+ data class TestModel(
+ val blockchain: Blockchain,
+ val derivationPath: String,
+ val expected: Long?,
+ )
+
@Nested
inner class RecognizeAsString {
@Test
- fun `returns account node value for a derivation path with 5 nodes and UTXO blockchain`() {
+ fun `GIVEN empty derivation path THEN returns null`() {
// Arrange
val recognizer = AccountNodeRecognizer(utxoBlockchain)
- val derivationPathValue = "m/44'/0'/1'/0/0"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- val expected = 1
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns account node value for a derivation path with 4 nodes and UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(utxoBlockchain)
- val derivationPathValue = "m/44'/0'/1'/0"
+ val derivationPathValue = "invalid/path"
// Act
val actual = recognizer.recognize(derivationPathValue)
@@ -181,115 +220,7 @@ internal class AccountNodeRecognizerTest {
}
@Test
- fun `returns account node value for a derivation path with 5 nodes and non-UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPathValue = "m/44'/0'/1'/2/3"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- val expected = 3
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns account node value for a derivation path with 4 nodes and non-UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPathValue = "m/44'/0'/1'/2"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- Truth.assertThat(actual).isNull()
- }
-
- @Test
- fun `returns account node value for a derivation path with 3 nodes and non-UTXO blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPathValue = "m/44'/0'/1'"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- val expected = 1
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns account node value for a derivation path with 4 nodes and Tezos blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Tezos)
- val derivationPathValue = "m/44'/0'/0/5'"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- val expected = 0
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns account node value for a derivation path with 5 nodes and Tezos blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Tezos)
- val derivationPathValue = "m/44'/0'/0'/5/1"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- Truth.assertThat(actual).isNull()
- }
-
- @Test
- fun `returns account node value for a derivation path with 4 nodes and Quai blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Quai)
- val derivationPathValue = "m/44'/0'/0/5'"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- Truth.assertThat(actual).isNull()
- }
-
- @Test
- fun `returns account node value for a derivation path with 5 nodes and Quai blockchain`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(Blockchain.Quai)
- val derivationPathValue = "m/44'/0'/0'/5/1"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- val expected = 0
- Truth.assertThat(actual).isEqualTo(expected)
- }
-
- @Test
- fun `returns null if derivation path is shorter than expected`() {
- // Arrange
- val recognizer = AccountNodeRecognizer(ethLikeBlockchain)
- val derivationPathValue = "m/44'/0'"
-
- // Act
- val actual = recognizer.recognize(derivationPathValue)
-
- // Assert
- Truth.assertThat(actual).isNull()
- }
-
- @Test
- fun `returns null if derivation path string is invalid`() {
+ fun `GIVEN invalid derivation path THEN returns null`() {
// Arrange
val recognizer = AccountNodeRecognizer(utxoBlockchain)
val derivationPathValue = "invalid/path"
@@ -301,4 +232,34 @@ internal class AccountNodeRecognizerTest {
Truth.assertThat(actual).isNull()
}
}
+
+ @Nested
+ inner class RecognizeAsNetworkDerivationPath {
+
+ @Test
+ fun `GIVEN empty derivation path THEN returns null`() {
+ // Arrange
+ val recognizer = AccountNodeRecognizer(utxoBlockchain)
+ val derivationPath = Network.DerivationPath.None
+
+ // Act
+ val actual = recognizer.recognize(derivationPath)
+
+ // Assert
+ Truth.assertThat(actual).isNull()
+ }
+
+ @Test
+ fun `GIVEN invalid derivation path THEN returns null`() {
+ // Arrange
+ val recognizer = AccountNodeRecognizer(utxoBlockchain)
+ val derivationPathValue = Network.DerivationPath.Card("invalid/path")
+
+ // Act
+ val actual = recognizer.recognize(derivationPathValue)
+
+ // Assert
+ Truth.assertThat(actual).isNull()
+ }
+ }
}
\ No newline at end of file