Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-14 21:45:25 +03:00
commit 4771312415
326 changed files with 6051 additions and 3487 deletions

View file

@ -5,8 +5,8 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.domain.userWalletList.asLockable
import kotlinx.coroutines.*
import timber.log.Timber
import kotlin.time.Duration

View file

@ -46,6 +46,7 @@ fun Blockchain.getGreyedOutIconRes(): Int {
Blockchain.Telos, Blockchain.TelosTestnet -> R.drawable.ic_telos_no_color
Blockchain.AlephZero, Blockchain.AlephZeroTestnet -> R.drawable.ic_azero_no_color
Blockchain.OctaSpace, Blockchain.OctaSpaceTestnet -> R.drawable.ic_octaspace_no_color
Blockchain.Chia, Blockchain.ChiaTestnet -> R.drawable.ic_chia_no_color
else -> R.drawable.ic_tangem_logo
}
}

View file

@ -12,6 +12,9 @@ internal class RuntimeUserWalletsStore(
private val walletsStateHolder: WalletsStateHolder,
) : UserWalletsStore {
override val selectedUserWalletOrNull: UserWallet?
get() = walletsStateHolder.userWalletsListManager?.selectedUserWalletSync
override suspend fun getSyncOrNull(key: UserWalletId): UserWallet? {
return walletsStateHolder.userWalletsListManager
?.userWallets

View file

@ -0,0 +1,22 @@
package com.tangem.tap.di.domain
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
@Module
@InstallIn(ViewModelComponent::class)
internal object AppCurrencyDomainModule {
@Provides
@ViewModelScoped
fun provideGetSelectedAppCurrencyUseCase(
appCurrencyRepository: AppCurrencyRepository,
): GetSelectedAppCurrencyUseCase {
return GetSelectedAppCurrencyUseCase(appCurrencyRepository)
}
}

View file

@ -1,9 +1,9 @@
package com.tangem.tap.di.domain
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.repository.TokensRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -18,23 +18,34 @@ internal object TokensDomainModule {
@Provides
@ViewModelScoped
fun provideGetTokenListUseCase(
tokensRepository: TokensRepository,
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetTokenListUseCase {
return GetTokenListUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers)
return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideGetCurrencyUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetCurrencyUseCase {
return GetCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideGetPrimaryCurrencyUseCase(
tokensRepository: TokensRepository,
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetPrimaryCurrencyUseCase {
return GetPrimaryCurrencyUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers)
return GetPrimaryCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
}
@Provides
@ -55,9 +66,9 @@ internal object TokensDomainModule {
@Provides
@ViewModelScoped
fun provideApplyTokenListSortingUseCase(
tokensRepository: TokensRepository,
currenciesRepository: CurrenciesRepository,
dispatchers: CoroutineDispatcherProvider,
): ApplyTokenListSortingUseCase {
return ApplyTokenListSortingUseCase(tokensRepository, dispatchers)
return ApplyTokenListSortingUseCase(currenciesRepository, dispatchers)
}
}

View file

@ -2,9 +2,7 @@ package com.tangem.tap.di.domain
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.WalletsStateHolder
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.domain.wallets.usecase.*
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -21,6 +19,12 @@ internal object WalletsDomainModule {
return GetWalletsUseCase(walletsStateHolder = walletsStateHolder)
}
@Provides
@ViewModelScoped
fun providesGetSelectedWalletUseCase(walletsStateHolder: WalletsStateHolder): GetSelectedWalletUseCase {
return GetSelectedWalletUseCase(walletsStateHolder = walletsStateHolder)
}
@Provides
@ViewModelScoped
fun providesSaveWalletUseCase(walletsStateHolder: WalletsStateHolder): SaveWalletUseCase {
@ -32,4 +36,16 @@ internal object WalletsDomainModule {
fun providesGetExploreUrlUseCase(walletsManagersFacade: WalletManagersFacade): GetExploreUrlUseCase {
return GetExploreUrlUseCase(walletsManagersFacade = walletsManagersFacade)
}
@Provides
@ViewModelScoped
fun providesUnlockWalletUseCase(walletsStateHolder: WalletsStateHolder): UnlockWalletsUseCase {
return UnlockWalletsUseCase(walletsStateHolder = walletsStateHolder)
}
@Provides
@ViewModelScoped
fun providesSelectWalletUseCase(walletsStateHolder: WalletsStateHolder): SelectWalletUseCase {
return SelectWalletUseCase(walletsStateHolder = walletsStateHolder)
}
}

View file

@ -16,6 +16,7 @@ import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.ScanTask
@ -80,7 +81,10 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit
suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult<CreateProductWalletTaskResponse> {
return runTaskAsync(
CreateProductWalletTask(scanResponse.cardTypesResolver),
CreateProductWalletTask(
cardTypesResolver = scanResponse.cardTypesResolver,
derivationStyleProvider = scanResponse.derivationStyleProvider,
),
scanResponse.card.cardId,
Message(resources.getString(R.string.initial_message_create_wallet_body)),
)
@ -90,15 +94,20 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit
scanResponse: ScanResponse,
mnemonic: String,
): CompletionResult<CreateProductWalletTaskResponse> {
return when (val seedResult = DefaultMnemonic(mnemonic, tangemSdk.wordlist).generateSeed()) {
is CompletionResult.Success -> runTaskAsync(
CreateProductWalletTask(scanResponse.cardTypesResolver, seedResult.data),
scanResponse.card.cardId,
Message(resources.getString(R.string.initial_message_create_wallet_body)),
)
is CompletionResult.Failure -> CompletionResult.Failure(seedResult.error)
val mnemonic = try {
DefaultMnemonic(mnemonic, tangemSdk.wordlist)
} catch (e: TangemSdkError.MnemonicException) {
return CompletionResult.Failure(e)
}
return runTaskAsync(
CreateProductWalletTask(
scanResponse.cardTypesResolver,
derivationStyleProvider = scanResponse.derivationStyleProvider,
mnemonic,
),
scanResponse.card.cardId,
Message(resources.getString(R.string.initial_message_create_wallet_body)),
)
}
private fun sendScanResultsToAnalytics(result: CompletionResult<ScanResponse>) {
@ -246,12 +255,13 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit
}
companion object {
@Deprecated("Use [DefaultCardSdkProvider] instead")
val config = Config(
linkedTerminal = true,
allowUntrustedCards = true,
filter = CardFilter(
allowedCardTypes = FirmwareVersion.FirmwareType.values().toList(),
maxFirmwareVersion = FirmwareVersion(major = 6, minor = 21),
maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33),
),
)
}

View file

@ -26,10 +26,8 @@ sealed class TapError(
val stateError: String,
) : TapError(R.string.common_custom_string, listOf("Unsupported state: $stateError"))
object ScanCardError : TapError(R.string.scan_card_error)
object UnknownBlockchain : TapError(R.string.wallet_error_unsupported_blockchain_subtitle)
object NoInternetConnection : TapError(R.string.wallet_notification_no_internet)
object BlockchainInternalError : TapError(R.string.send_error_blockchain_internal)
object AmountExceedsBalance : TapError(R.string.send_validation_amount_exceeds_balance)
data class AmountLowerExistentialDeposit(
override val args: List<Any>,
@ -43,7 +41,7 @@ sealed class TapError(
object DustChange : TapError(R.string.send_error_dust_change)
sealed class WalletManager {
object CreationError : CustomError("Can't create wallet manager")
object CreationError : CustomError(customMessage = "Can't create wallet manager")
class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount)
class InternalError(message: String) : CustomError(message)
object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later)

View file

@ -8,6 +8,7 @@ import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.core.analytics.Analytics
import com.tangem.datasource.config.ConfigManager
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
@ -20,6 +21,8 @@ import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.walletStores.WalletStoresError
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
import com.tangem.tap.domain.walletconnect2.domain.models.Account
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.disclaimer.createDisclaimer
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
@ -92,7 +95,8 @@ class TapWalletManager(
null
}
scope.launch {
store.state.daggerGraphState.walletConnectInteractor?.startListening(
val wcInteractor = store.state.daggerGraphState.walletConnectInteractor ?: return@launch
wcInteractor.startListening(
userWalletId = userWallet.walletId.stringValue,
cardId = cardId,
)
@ -106,6 +110,9 @@ class TapWalletManager(
store.dispatchOnMain(WalletAction.LoadData.Success)
store.state.globalState.topUpController?.loadDataSuccess()
store.dispatchWithMain(WalletAction.Warnings.CheckHashesCount.VerifyOnlineIfNeeded)
val wcInteractor = store.state.daggerGraphState.walletConnectInteractor
wcInteractor?.setUserChains(getAccountsForWc(wcInteractor))
}
.doOnFailure { error ->
val errorAction = when (error) {
@ -135,6 +142,23 @@ class TapWalletManager(
}
}
private fun getAccountsForWc(wcInteractor: WalletConnectInteractor): List<Account> {
return store.state.walletState.walletManagers
.mapNotNull {
val wallet = it.wallet
val chainId = wcInteractor.blockchainHelper.networkIdToChainIdOrNull(
wallet.blockchain.toNetworkId(),
)
chainId?.let {
Account(
chainId,
wallet.address,
wallet.publicKey.derivationPath?.rawPath,
)
}
}
}
fun updateConfigManager(data: ScanResponse) {
val configManager = store.state.globalState.configManager

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.configurable.warningMessage
import com.tangem.blockchain.common.Blockchain
import com.tangem.utils.extensions.removeByReplace
import com.tangem.utils.extensions.removeBy
import com.tangem.wallet.R
import java.util.concurrent.CopyOnWriteArrayList
@ -44,12 +44,12 @@ class WarningMessagesManager {
}
fun removeWarnings(origin: WarningMessage.Origin) {
warningsList.removeByReplace { it.origin == origin }
warningsList.removeBy { it.origin == origin }
sortByPriority()
}
fun removeWarnings(messageRes: Int) {
warningsList.removeByReplace { it.messageResId == messageRes }
warningsList.removeBy { it.messageResId == messageRes }
}
fun containsWarning(warning: WarningMessage) = warning in warningsList

View file

@ -1,11 +1,15 @@
package com.tangem.tap.domain.model.builders
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
@ -47,7 +51,7 @@ private class BlockchainNetworkWalletStoreBuilderImpl(
}
override fun build(): WalletStoreModel {
val cardDerivationStyle = userWallet.scanResponse.card.derivationStyle
val cardDerivationStyle = userWallet.scanResponse.derivationStyleProvider.getDerivationStyle()
val blockchainWalletData = blockchainNetwork.getBlockchainWalletData(walletManager, cardDerivationStyle)
val tokensWalletsData = blockchainNetwork.getTokensWalletsData(
walletManager = walletManager,

View file

@ -11,10 +11,13 @@ import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toMapKey
import com.tangem.common.map
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.common.extensions.derivationPath
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.KeyWalletPublicKey
import com.tangem.operations.CommandResponse
@ -56,7 +59,8 @@ private data class CreateWalletResponse(
class CreateProductWalletTask(
private val cardTypesResolver: CardTypesResolver,
private val seed: ByteArray? = null,
private val derivationStyleProvider: DerivationStyleProvider,
private val mnemonic: Mnemonic? = null,
) : CardSessionRunnable<CreateProductWalletTaskResponse> {
override val allowsRequestAccessCodeFromRepository: Boolean = false
@ -76,7 +80,7 @@ class CreateProductWalletTask(
cardTypesResolver.isTangemTwins() ->
throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
else -> CreateWalletTangemWallet(seed)
else -> CreateWalletTangemWallet(mnemonic, derivationStyleProvider)
}
commandProcessor.proceed(cardDto, session) {
when (it) {
@ -131,8 +135,12 @@ private class CreateWalletTangemNote(private val cardTypesResolver: CardTypesRes
}
}
/**
* Uses for multiWallet 1st and 2nd
*/
private class CreateWalletTangemWallet(
private val seed: ByteArray?,
private val mnemonic: Mnemonic?,
private val derivationStyleProvider: DerivationStyleProvider,
) : ProductCommandProcessor<CreateProductWalletTaskResponse> {
private var primaryCard: PrimaryCard? = null
@ -142,8 +150,9 @@ private class CreateWalletTangemWallet(
session: CardSession,
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit,
) {
val config = CardConfig.createConfig(card)
val walletsOnCard = card.wallets.map { it.curve }.toSet()
val curves = card.supportedCurves.intersect(CURVES_FOR_WALLETS).subtract(walletsOnCard).toList()
val curves = card.supportedCurves.intersect(config.mandatoryCurves.toSet()).subtract(walletsOnCard).toList()
if (curves.isEmpty()) {
val createWalletResponses = card.wallets.map { wallet ->
@ -152,8 +161,7 @@ private class CreateWalletTangemWallet(
proceedWithCreatedWallets(card, createWalletResponses, session, callback)
return
}
CreateWalletsTask(curves, seed).run(session) { result ->
CreateWalletsTask(curves, mnemonic).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
proceedWithCreatedWallets(
@ -242,9 +250,9 @@ private class CreateWalletTangemWallet(
val blockchainsForCurve = getBlockchains(response.cardId, card).filter {
it.getSupportedCurves().contains(response.wallet.curve)
}
val derivationPaths = blockchainsForCurve.mapNotNull {
val derivationPaths = blockchainsForCurve.mapNotNull { blockchain ->
isBlockchainsForCurvesExist = true
it.derivationPath(card.derivationStyle)
blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())
}
if (derivationPaths.isNotEmpty()) {
map[response.wallet.publicKey.toMapKey()] = derivationPaths

View file

@ -5,6 +5,8 @@ import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemSdkError
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.crypto.hdWallet.masterkey.AnyMasterKeyFactory
import com.tangem.operations.CommandResponse
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.CreateWalletTask
@ -18,7 +20,7 @@ class CreateWalletsResponse(
class CreateWalletsTask(
private val curves: List<EllipticCurve>,
private val seed: ByteArray? = null,
private val mnemonic: Mnemonic? = null,
) : CardSessionRunnable<CreateWalletsResponse> {
private val createdWalletsResponses = mutableListOf<CreateWalletResponse>()
@ -38,7 +40,10 @@ class CreateWalletsTask(
session: CardSession,
callback: (result: CompletionResult<CreateWalletsResponse>) -> Unit,
) {
CreateWalletTask(curve, seed).run(session) { result ->
val extendedPrivateKey = mnemonic?.let {
AnyMasterKeyFactory(mnemonic = it, passphrase = "").makeMasterKey(curve)
}
CreateWalletTask(curve, extendedPrivateKey).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
createdWalletsResponses.add(result.data)

View file

@ -14,13 +14,15 @@ import com.tangem.common.tlv.TlvDecoder
import com.tangem.crypto.CryptoUtils
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.isExcluded
import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.common.TwinsHelper
import com.tangem.domain.common.extensions.getPrimaryCurve
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
@ -51,7 +53,7 @@ class ScanProductTask(
}
val cardDto = CardDTO(card)
val error = getErrorIfExcludedCard(cardDto, card)
val error = getErrorIfExcludedCard(cardDto)
if (error != null) {
callback(CompletionResult.Failure(error))
return
@ -81,11 +83,9 @@ class ScanProductTask(
}
}
private fun getErrorIfExcludedCard(cardDto: CardDTO, card: Card): TangemError? {
private fun getErrorIfExcludedCard(cardDto: CardDTO): TangemError? {
if (cardDto.isExcluded) return TapSdkError.CardForDifferentApp
if (cardDto.isNotSupportedInThatRelease) return TapSdkError.CardNotSupportedByRelease
// todo check isImported to prevent using old app with imported wallet, remove before wallet 2.0 enabled ([REDACTED_TASK_KEY])
if (card.wallets.any { it.isImported }) return TapSdkError.CardNotSupportedByRelease
return null
}
}
@ -209,32 +209,24 @@ private class ScanWalletProcessor(
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
val productType = ProductType.Wallet
val config = CardConfig.createConfig(card)
scope.launch {
val derivations = collectDerivations(card)
val scanResponse = ScanResponse(
card = card,
productType = productType,
walletData = session.environment.walletData,
primaryCard = primaryCard,
)
val derivations = collectDerivations(card, config, scanResponse.derivationStyleProvider)
if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) {
callback(
CompletionResult.Success(
ScanResponse(
card = card,
productType = productType,
walletData = session.environment.walletData,
primaryCard = primaryCard,
),
),
)
callback(CompletionResult.Success(scanResponse))
return@launch
}
DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
val response = ScanResponse(
card = card,
productType = productType,
walletData = session.environment.walletData,
derivedKeys = result.data.entries,
primaryCard = primaryCard,
)
val response = scanResponse.copy(derivedKeys = result.data.entries)
callback(CompletionResult.Success(response))
}
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
@ -243,7 +235,10 @@ private class ScanWalletProcessor(
}
}
private suspend fun getBlockchainsToDerive(card: CardDTO): List<BlockchainNetwork> {
private suspend fun getBlockchainsToDerive(
card: CardDTO,
derivationStyleProvider: DerivationStyleProvider,
): List<BlockchainNetwork> {
val userTokensRepository = userTokensRepository ?: return emptyList()
val blockchainsToDerive = userTokensRepository.loadBlockchainsToDerive(card)
.toMutableList()
@ -251,11 +246,11 @@ private class ScanWalletProcessor(
mutableListOf(
BlockchainNetwork(
blockchain = Blockchain.Bitcoin,
card = card,
derivationStyleProvider = derivationStyleProvider,
),
BlockchainNetwork(
blockchain = Blockchain.Ethereum,
card = card,
derivationStyleProvider = derivationStyleProvider,
),
)
}
@ -265,11 +260,11 @@ private class ScanWalletProcessor(
listOf(
BlockchainNetwork(
blockchain = Blockchain.Ethereum,
card = card,
derivationStyleProvider = derivationStyleProvider,
),
BlockchainNetwork(
blockchain = Blockchain.EthereumTestnet,
card = card,
derivationStyleProvider = derivationStyleProvider,
),
),
)
@ -279,7 +274,7 @@ private class ScanWalletProcessor(
additionalBlockchainsToDerive.map {
BlockchainNetwork(
blockchain = it,
card = card,
derivationStyleProvider = derivationStyleProvider,
)
},
)
@ -295,7 +290,7 @@ private class ScanWalletProcessor(
).map {
BlockchainNetwork(
blockchain = it,
card = card,
derivationStyleProvider = derivationStyleProvider,
)
},
)
@ -303,12 +298,16 @@ private class ScanWalletProcessor(
return blockchainsToDerive.distinct()
}
private suspend fun collectDerivations(card: CardDTO): Map<ByteArrayKey, List<DerivationPath>> {
val blockchains = getBlockchainsToDerive(card)
private suspend fun collectDerivations(
card: CardDTO,
config: CardConfig,
derivationStyleProvider: DerivationStyleProvider,
): Map<ByteArrayKey, List<DerivationPath>> {
val blockchains = getBlockchainsToDerive(card, derivationStyleProvider)
val derivations = mutableMapOf<ByteArrayKey, List<DerivationPath>>()
blockchains.forEach { blockchain ->
val curve = blockchain.blockchain.getPrimaryCurve()
val curve = config.primaryCurve(blockchain.blockchain)
val wallet = card.wallets.firstOrNull { it.curve == curve } ?: return@forEach
if (wallet.chainCode == null) return@forEach

View file

@ -1,27 +0,0 @@
package com.tangem.tap.domain.tokens
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.models.scan.CardDTO
object CurrenciesRepository {
fun getBlockchains(cardFirmware: CardDTO.FirmwareVersion, isTestNet: Boolean = false): List<Blockchain> {
val blockchains = if (cardFirmware < FirmwareVersion.MultiWalletAvailable) {
Blockchain.secp256k1Blockchains(isTestNet)
} else {
Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet)
}
return excludeUnsupportedBlockchains(blockchains)
}
// Use this list to temporarily exclude a blockchain from the list of tokens.
private fun excludeUnsupportedBlockchains(blockchains: List<Blockchain>): List<Blockchain> {
return blockchains.toMutableList().apply {
removeAll(
listOf(
// Any blockchain
),
)
}
}
}

View file

@ -34,6 +34,11 @@ class CreateSecondTwinWalletTask(
return
}
if (!TwinsHelper.isTwinsCompatible(firstCardId, card.cardId)) {
callback(CompletionResult.Failure(IncompatibleTwinCard))
return
}
session.setMessage(preparingMessage)
PurgeWalletCommand(publicKey).run(session) { response ->
when (response) {

View file

@ -0,0 +1,12 @@
package com.tangem.tap.domain.twins
import com.tangem.common.core.TangemError
import com.tangem.tap.tangemSdkManager
import com.tangem.wallet.R
object IncompatibleTwinCard : TangemError(code = 50005) {
override var customMessage: String = tangemSdkManager.getString(
R.string.twin_error_wrong_twin,
)
override val messageResId: Int? = null
}

View file

@ -1,34 +0,0 @@
package com.tangem.tap.domain.userWalletList
import com.tangem.common.core.TangemError
import com.tangem.wallet.R
sealed class UserWalletsListError(code: Int) : TangemError(code) {
override val silent: Boolean
get() = (cause as? TangemError)?.silent == true
override val messageResId: Int? = null
object WalletAlreadySaved : UserWalletsListError(code = 60001) {
override var customMessage: String = "This wallet has already been saved, you can add another one"
override val messageResId: Int = R.string.user_wallet_list_error_wallet_already_saved
}
object EncryptionKeyInvalidated : UserWalletsListError(code = 60002) {
override var customMessage: String = "Encryption key invalidated"
}
object BiometricsAuthenticationDisabled : UserWalletsListError(code = 60005) {
override var customMessage: String = "Biometrics authentication disabled"
}
data class BiometricsAuthenticationLockout(val isPermanent: Boolean) : UserWalletsListError(code = 60003) {
override var customMessage: String = "Biometric authentication lockout, permanent: $isPermanent"
}
data class UnableToUnlockUserWallets(override val cause: Throwable? = null) : UserWalletsListError(code = 60004) {
override var customMessage: String = "An error has occurred, please scan your card to log in"
override val messageResId: Int = R.string.user_wallet_list_error_unable_to_unlock
}
}

View file

@ -1,69 +0,0 @@
package com.tangem.tap.domain.userWalletList
import com.tangem.common.CompletionResult
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
/**
* Indicates that the [UserWalletsListManager] implements [UserWalletsListManager.Lockable]
* */
val UserWalletsListManager.isLockable: Boolean
get() = this is UserWalletsListManager.Lockable
/**
* Indicates that the [UserWalletsListManager] is locked
*
* @return If [UserWalletsListManager] not implements [UserWalletsListManager.Lockable] returns [Flow] which
* produces only one false value
*
* @see UserWalletsListManager.Lockable.isLockedSync
* */
val UserWalletsListManager.isLocked: Flow<Boolean>
get() = asLockable()?.isLocked ?: flowOf(false)
/**
* Indicates that the [UserWalletsListManager] is locked
*
* @return If [UserWalletsListManager] not implements [UserWalletsListManager.Lockable] returns false
*
* @see UserWalletsListManager.Lockable.isLockedSync
* */
val UserWalletsListManager.isLockedSync: Boolean
get() = asLockable()?.isLockedSync ?: false
/**
* Call [UserWalletsListManager.Lockable.unlock] if [UserWalletsListManager] implements [UserWalletsListManager.Lockable]
*
* @return If [UserWalletsListManager] not implements [UserWalletsListManager.Lockable]
* returns [CompletionResult.Failure] with [UserWalletsListError.UnableToUnlockUserWallets]
*
* If [UserWalletsListManager] implements [UserWalletsListManager.Lockable]
* returns [CompletionResult.Success] with selected [UserWallet]
*
* @see UserWalletsListManager.Lockable.unlock
* */
suspend fun UserWalletsListManager.unlockIfLockable(): CompletionResult<UserWallet> {
return asLockable()?.unlock() ?: CompletionResult.Failure(UserWalletsListError.UnableToUnlockUserWallets())
}
/**
* Call [UserWalletsListManager.Lockable.lock] if [UserWalletsListManager] implements [UserWalletsListManager.Lockable]
* or do nothing otherwise
*
* @see UserWalletsListManager.Lockable.lock
* */
fun UserWalletsListManager.lockIfLockable() {
asLockable()?.lock()
}
/**
* Safe cast [UserWalletsListManager] to [UserWalletsListManager.Lockable]
*
* @return If [UserWalletsListManager] not implements [UserWalletsListManager.Lockable] then returns null or
* [UserWalletsListManager.Lockable] otherwise
* */
fun UserWalletsListManager.asLockable(): UserWalletsListManager.Lockable? {
return this as? UserWalletsListManager.Lockable
}

View file

@ -2,10 +2,10 @@ package com.tangem.tap.domain.userWalletList.implementation
import com.tangem.common.*
import com.tangem.common.extensions.guard
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.userWalletList.UserWalletsListError
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.repository.SelectedUserWalletRepository
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository

View file

@ -2,10 +2,10 @@ package com.tangem.tap.domain.userWalletList.implementation
import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.userWalletList.UserWalletsListError
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*

View file

@ -8,8 +8,8 @@ import com.tangem.common.biometric.BiometricManager
import com.tangem.common.biometric.BiometricStorage
import com.tangem.common.core.TangemSdkError
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.userWalletList.UserWalletsListError
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
import kotlinx.coroutines.Dispatchers

View file

@ -12,7 +12,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository
import com.tangem.tap.domain.userWalletList.utils.publicInformation
import com.tangem.utils.extensions.plusOrReplace
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@ -29,7 +29,7 @@ internal class DefaultUserWalletsPublicInformationRepository(
getAll()
.flatMap { savedInformation ->
val infoToSave = withContext(Dispatchers.Default) {
savedInformation.plusOrReplace(userWallet.publicInformation) {
savedInformation.addOrReplace(userWallet.publicInformation) {
userWallet.walletId == it.walletId
}
}

View file

@ -1,9 +1,11 @@
package com.tangem.tap.domain.walletCurrencies.implementation
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.common.*
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.extensions.derivationPath
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.legacy.WalletManagersRepository
import com.tangem.domain.wallets.models.UserWallet
@ -61,7 +63,9 @@ internal class DefaultWalletCurrenciesManager(
}
val card = userWallet.scanResponse.card
val currenciesToAddWithMissingBlockchains = currenciesToAdd.addMissingBlockchainsIfNeeded(card)
val currenciesToAddWithMissingBlockchains = currenciesToAdd.addMissingBlockchainsIfNeeded(
userWallet.scanResponse.derivationStyleProvider,
)
listeners.forEach { it.willCurrenciesAdd(userWallet, currenciesToAddWithMissingBlockchains) }
updateWalletStores(
@ -167,13 +171,15 @@ internal class DefaultWalletCurrenciesManager(
return networks
}
private fun List<Currency>.addMissingBlockchainsIfNeeded(card: CardDTO): List<Currency> {
private fun List<Currency>.addMissingBlockchainsIfNeeded(
derivationStyleProvider: DerivationStyleProvider,
): List<Currency> {
if (this.isEmpty()) return this
val currencies = this.asSequence()
return currencies
.groupBy { currency ->
findBlockchainCurrency(currency, currencies, card.derivationStyle)
findBlockchainCurrency(currency, currencies, derivationStyleProvider.getDerivationStyle())
}
.mapValues { (blockchainCurrency, blockchainCurrencies) ->
findBlockchainTokens(blockchainCurrency, blockchainCurrencies)

View file

@ -31,7 +31,7 @@ import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.plusOrReplace
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.firstOrNull
import timber.log.Timber
@ -429,7 +429,7 @@ internal class DefaultWalletAmountsRepository(
withContext(Dispatchers.Default) {
WalletManagerStorage.update { prevManagers ->
val newManagersForUserWallet = prevManagers[userWalletId].orEmpty()
.plusOrReplace(walletManager) {
.addOrReplace(walletManager) {
it.wallet.blockchain == walletManager.wallet.blockchain
}

View file

@ -100,6 +100,28 @@ class WalletConnectRepositoryImpl @Inject constructor(
Timber.d("sessionProposal: $sessionProposal")
this@WalletConnectRepositoryImpl.sessionProposal = sessionProposal
val missingNetworks = findMissingNetworks(
namespaces = sessionProposal.requiredNamespaces,
userNamespaces = this@WalletConnectRepositoryImpl.userNamespaces ?: emptyMap(),
)
if (missingNetworks.isNotEmpty()) {
Timber.w("Not added blockchains: $missingNetworks")
scope.launch {
_events.emit(
WalletConnectEvents.SessionApprovalError(
WalletConnectError.ApprovalErrorMissingNetworks(missingNetworks.toList()),
),
)
}
return
}
val optionalWithoutMissingNetworks = removeMissingNetworks(
namespaces = sessionProposal.optionalNamespaces,
userNamespaces = this@WalletConnectRepositoryImpl.userNamespaces ?: emptyMap(),
)
scope.launch {
_events.emit(
WalletConnectEvents.SessionProposal(
@ -108,7 +130,7 @@ class WalletConnectRepositoryImpl @Inject constructor(
sessionProposal.url,
sessionProposal.icons,
sessionProposal.requiredNamespaces.values.flatMap { it.chains ?: emptyList() },
sessionProposal.optionalNamespaces.values.flatMap { it.chains ?: emptyList() },
optionalWithoutMissingNetworks.toList(),
),
)
}
@ -211,6 +233,10 @@ class WalletConnectRepositoryImpl @Inject constructor(
}
}
override fun setUserNamespaces(userNamespaces: Map<NetworkNamespace, List<Account>>) {
this.userNamespaces = userNamespaces
}
override fun pair(uri: String) {
Web3Wallet.pair(Wallet.Params.Pair(uri))
}
@ -220,23 +246,6 @@ class WalletConnectRepositoryImpl @Inject constructor(
val sessionProposal: Wallet.Model.SessionProposal = requireNotNull(this.sessionProposal)
val missingNetworks = findMissingNetworks(
namespaces = sessionProposal.requiredNamespaces,
userNamespaces = userNamespaces,
)
if (missingNetworks.isNotEmpty()) {
Timber.e("Not added blockchains: $missingNetworks")
scope.launch {
_events.emit(
WalletConnectEvents.SessionApprovalError(
WalletConnectError.ApprovalErrorMissingNetworks(missingNetworks.toList()),
),
)
}
return
}
val userChains = userNamespaces.flatMap { namespace ->
namespace.value.map { it.chainId to "${it.chainId}:${it.walletAddress}" }
}.groupBy { pair -> pair.first }
@ -433,4 +442,13 @@ class WalletConnectRepositoryImpl @Inject constructor(
val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } }
return requiredChains.subtract(userChains.toSet())
}
private fun removeMissingNetworks(
namespaces: Map<String, Wallet.Model.Namespace.Proposal>,
userNamespaces: Map<NetworkNamespace, List<Account>>,
): Collection<String> {
val wcProvidedChains = namespaces.values.flatMap { it.chains ?: emptyList() }
val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } }
return wcProvidedChains.intersect(userChains.toSet())
}
}

View file

@ -44,6 +44,15 @@ class WalletConnectInteractor(
}
}
fun setUserChains(accounts: List<Account>) {
val userNamespaces: Map<NetworkNamespace, List<Account>> = accounts
.groupBy { account ->
blockchainHelper.getNamespaceFromFullChainIdOrNull(account.chainId)
?.let { NetworkNamespace(it) }
}.filterNotNull()
walletConnectRepository.setUserNamespaces(userNamespaces)
}
private suspend fun subscribeToEvents() {
events
.onEach { wcEvent ->

View file

@ -11,6 +11,8 @@ interface WalletConnectRepository {
fun init(projectId: String)
fun setUserNamespaces(userNamespaces: Map<NetworkNamespace, List<Account>>)
fun updateSessions()
fun pair(uri: String)

View file

@ -7,8 +7,10 @@ import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toMapKey
import com.tangem.common.flatMap
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.extensions.derivationPath
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.common.util.hasDerivation
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.domain.models.scan.ScanResponse
@ -68,10 +70,11 @@ class DefaultCustomTokenInteractor(
currencyList: List<Currency>,
onSuccess: suspend (ScanResponse) -> Unit,
) {
val derivationDataList = listOfNotNull(
getDerivations(EllipticCurve.Secp256k1, scanResponse, currencyList),
getDerivations(EllipticCurve.Ed25519, scanResponse, currencyList),
)
val config = CardConfig.createConfig(scanResponse.card)
val derivationDataList = currencyList.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencyList) }
}
val derivations = derivationDataList.associate(TokensMiddleware.DerivationData::derivations)
if (derivations.isEmpty()) {
@ -114,7 +117,7 @@ class DefaultCustomTokenInteractor(
val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter {
it.getSupportedCurves().contains(curve)
}.mapNotNull {
it.derivationPath(scanResponse.card.derivationStyle)
it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
val customTokensCandidates = currencyList.filter {

View file

@ -29,7 +29,7 @@ internal fun AddCustomTokenFloatingButton(model: AddCustomTokenFloatingButton, m
.imePadding()
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
text = stringResource(id = R.string.common_add),
text = stringResource(id = R.string.custom_token_add_token),
iconResId = R.drawable.ic_plus_24,
enabled = model.isEnabled,
onClick = model.onClick,

View file

@ -11,14 +11,14 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.HDWalletError
import com.tangem.domain.AddCustomTokenError
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.extensions.*
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.customtoken.impl.domain.CustomTokenInteractor
@ -599,7 +599,7 @@ internal class AddCustomTokenViewModel @Inject constructor(
if (blockchain == null) return null
val derivationStyle = if (!isDerivationPathSelected()) {
reduxStateHolder.scanResponse?.card?.derivationStyle
reduxStateHolder.scanResponse?.derivationStyleProvider?.getDerivationStyle()
} else {
DerivationStyle.LEGACY
}

View file

@ -15,6 +15,7 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.isLockedSync
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
@ -27,7 +28,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
import com.tangem.tap.domain.userWalletList.isLockedSync
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction

View file

@ -1,18 +1,19 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.common.extensions.guard
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.extensions.derivationPath
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
@ -357,9 +358,13 @@ class WalletConnectMiddleware {
handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain)
}
private fun getAvailableBlockchains(card: CardDTO, walletState: WalletState): List<Blockchain> {
private fun getAvailableBlockchains(
derivationStyleProvider: DerivationStyleProvider,
walletState: WalletState,
): List<Blockchain> {
return walletState.currencies.filter {
it.isBlockchain() && !it.isCustomCurrency(card.derivationStyle) && it.blockchain.isEvm()
it.isBlockchain() &&
!it.isCustomCurrency(derivationStyleProvider.getDerivationStyle()) && it.blockchain.isEvm()
}.map { it.blockchain }
}
@ -390,7 +395,7 @@ class WalletConnectMiddleware {
walletPublicKey = wallet.publicKey.seedKey,
derivedPublicKey = derivedKey,
derivationPath = wallet.publicKey.derivationPath,
derivationStyle = scanResponse.card.derivationStyle,
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
blockchain = wallet.blockchain,
)
@ -403,7 +408,6 @@ class WalletConnectMiddleware {
}
private fun handleScanResponse(scanResponse: ScanResponse, session: WalletConnectSession, blockchain: Blockchain) {
val card = scanResponse.card
if (!scanResponse.cardTypesResolver.isMultiwalletAllowed()) {
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
return
@ -415,7 +419,11 @@ class WalletConnectMiddleware {
NewWcSessionData(session = updatedSession, scanResponse = scanResponse, blockchain = blockchain),
),
)
val blockchains = if (blockchain.isEvm()) getAvailableBlockchains(card, walletState) else emptyList()
val blockchains = if (blockchain.isEvm()) {
getAvailableBlockchains(scanResponse.derivationStyleProvider, walletState)
} else {
emptyList()
}
store.dispatch(
GlobalAction.ShowDialog(
WalletConnectDialog.ApproveWcSession(session = updatedSession, networks = blockchains),
@ -433,8 +441,9 @@ class WalletConnectMiddleware {
} else {
blockchain
}
val derivation = blockchainToMake.derivationPath(store.state.globalState.scanResponse?.card?.derivationStyle)
?.rawPath
val derivation = blockchainToMake.derivationPath(
style = store.state.globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle(),
)?.rawPath
val blockchainNetwork = BlockchainNetwork(
blockchain = blockchainToMake,
derivationPath = derivation,

View file

@ -2,9 +2,9 @@ package com.tangem.tap.features.details.redux.walletconnect
import com.squareup.moshi.JsonClass
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.redux.StateDialog

View file

@ -12,7 +12,7 @@ import com.tangem.wallet.R
object ClipboardOrScanQrDialog {
fun create(wcUri: String, context: Context): AlertDialog {
return AlertDialog.Builder(context).apply {
setTitle(context.getString(R.string.wallet_connect_title))
setTitle(context.getString(R.string.common_select_action))
setMessage(context.getText(R.string.wallet_connect_clipboard_alert))
setPositiveButton(context.getText(R.string.wallet_connect_paste_from_clipboard)) { _, _ ->
store.dispatch(WalletConnectAction.OpenSession(wcUri))

View file

@ -188,7 +188,7 @@ fun StoriesScreen(
contentDescription = null,
)
Text(
text = stringResource(id = R.string.search_tokens_title),
text = stringResource(id = R.string.common_search_tokens),
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
textAlign = TextAlign.Center,

View file

@ -1,11 +1,6 @@
package com.tangem.tap.features.home.compose.views
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
@ -51,7 +46,7 @@ fun HomeButtons(
},
content = {
Text(
text = stringResource(id = R.string.welcome_unlock_card),
text = stringResource(id = R.string.home_button_scan),
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
textAlign = TextAlign.Center,

View file

@ -6,7 +6,6 @@ import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.canSkipBackup
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.ProductType
@ -38,10 +37,7 @@ object OnboardingHelper {
}
}
// TODO for Shiba disabled check wallet 2, and only check canSkipBackup, enable when release wallet 2
// ([REDACTED_TASK_KEY])
// response.cardTypesResolver.isWallet2() -> {
!response.card.canSkipBackup -> {
response.cardTypesResolver.isWallet2() -> {
val emptyWallets = response.card.wallets.isEmpty()
val activationInProgress = cardInfoStorage.isActivationInProgress(cardId)
val backupNotActive = response.card.backupStatus?.isActive != true

View file

@ -151,7 +151,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
btnAlternativeAction.isVisible = false
}
tvHeader.setText(R.string.onboarding_top_up_header)
tvHeader.setText(R.string.onboarding_topup_title)
if (state.balanceNonCriticalError == null) {
tvBody.setText(R.string.onboarding_top_up_body)
} else {

View file

@ -6,6 +6,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.postUi
@ -92,7 +93,7 @@ private fun handleOtherCardsAction(action: Action) {
val blockchainNetwork =
BlockchainNetwork(
blockchain = primaryBlockchain,
card = updatedCard,
derivationStyleProvider = updatedResponse.derivationStyleProvider,
)
.updateTokens(
listOfNotNull(primaryToken),
@ -102,11 +103,11 @@ private fun handleOtherCardsAction(action: Action) {
listOf(
BlockchainNetwork(
blockchain = Blockchain.Bitcoin,
card = updatedCard,
derivationStyleProvider = updatedResponse.derivationStyleProvider,
),
BlockchainNetwork(
blockchain = Blockchain.Ethereum,
card = updatedCard,
derivationStyleProvider = updatedResponse.derivationStyleProvider,
),
)
}

View file

@ -11,6 +11,7 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.isLockedSync
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
@ -21,7 +22,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.domain.userWalletList.isLockedSync
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.OnboardingHelper
@ -50,7 +50,7 @@ private val twinsWalletMiddleware: Middleware<AppState> = { dispatch, state ->
@Suppress("LongMethod", "ComplexMethod", "MagicNumber")
private fun handle(action: Action, dispatch: DispatchFunction) {
val action = action as? TwinCardsAction ?: return
if (action !is TwinCardsAction) return
val globalState = store.state.globalState
val onboardingManager = globalState.onboardingState.onboardingManager

View file

@ -350,7 +350,7 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
btnAlternativeAction.isVisible = false
}
tvHeader.setText(R.string.onboarding_top_up_header)
tvHeader.setText(R.string.onboarding_topup_title)
tvBody.setText(R.string.onboarding_top_up_body)
btnRefreshBalanceWidget.changeState(state.walletBalance.state)

View file

@ -12,6 +12,7 @@ import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.TapWorkarounds.canSkipBackup
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.Artwork
@ -125,7 +126,10 @@ private fun handleWalletAction(action: Action) {
} else {
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
}.map { blockchain ->
BlockchainNetwork(blockchain, result.data.card)
BlockchainNetwork(
blockchain = blockchain,
derivationStyleProvider = updatedResponse.derivationStyleProvider,
)
}
scope.launch {

View file

@ -3,11 +3,13 @@ package com.tangem.tap.features.onboarding.products.wallet.ui
import androidx.compose.runtime.collectAsState
import com.tangem.feature.onboarding.api.OnboardingSeedPhrase
import com.tangem.feature.onboarding.api.OnboardingSeedPhraseApi
import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseScreen
import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseViewModel
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletStep
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
@ -44,11 +46,27 @@ internal class OnboardingSeedPhraseStateHandler(
walletFragment.binding.onboardingWalletContainer.hide()
walletFragment.bindingSeedPhrase.onboardingSeedPhraseContainer.show()
walletFragment.bindingSeedPhrase.onboardingSeedPhraseContainer.setContent {
val subScreen = viewModel.currentScreen.collectAsState().value
setMainScreenToolbarTitle(walletFragment, subScreen)
onboardingSeedPhraseApi.ScreenContent(
uiState = viewModel.uiState,
subScreen = viewModel.currentScreen.collectAsState().value,
subScreen = subScreen,
progress = viewModel.progress.collectAsState(0).value.toFloat() / onboardingWalletMaxProgress,
)
}
}
private fun setMainScreenToolbarTitle(walletFragment: OnboardingWalletFragment, subScreen: SeedPhraseScreen) {
val titleResId = when (subScreen) {
SeedPhraseScreen.Intro,
SeedPhraseScreen.AboutSeedPhrase,
SeedPhraseScreen.YourSeedPhrase,
SeedPhraseScreen.CheckSeedPhrase,
-> R.string.onboarding_create_wallet_header
SeedPhraseScreen.ImportSeedPhrase -> R.string.onboarding_seed_intro_button_import
}
walletFragment.binding.toolbar.title = walletFragment.getString(titleResId)
}
}

View file

@ -257,7 +257,7 @@ class OnboardingWalletFragment :
prepareBackupView()
tvHeader.text = getText(R.string.onboarding_title_scan_origin_card)
tvBody.text = getString(
R.string.onboarding_subtitle_scan_origin_card,
R.string.onboarding_subtitle_scan_primary,
)
with(layoutButtonsCommon) {

View file

@ -11,6 +11,7 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.isLockable
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
@ -20,7 +21,6 @@ import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
import com.tangem.tap.domain.userWalletList.isLockable
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.launch

View file

@ -15,7 +15,6 @@ import com.tangem.core.navigation.NavigationAction
import com.tangem.tap.common.GlobalLayoutStateHandler
import com.tangem.tap.common.KeyboardObserver
import com.tangem.tap.common.extensions.getQuantityString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.shop.domain.models.ProductState
@ -153,11 +152,12 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu
animateProductSelection(state.selectedProduct)
handlePriceState(state)
handlePromoCodeState(state)
if (shopifyFeatureToggleManager.isDynamicSalesProductsEnabled) {
handleNotificationBlock(state)
} else {
handleOrderingDelayBlock(isVisible = state.isOrderingDelayBlockVisible)
}
// TODO: https://tangem.slack.com/archives/C01HARKDLQ0/p1691421861756069
// if (shopifyFeatureToggleManager.isDynamicSalesProductsEnabled) {
// handleNotificationBlock(state)
// } else {
// handleOrderingDelayBlock(isVisible = state.isOrderingDelayBlockVisible)
// }
handleButtonsState(state)
}
@ -198,20 +198,20 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu
pbPromoCode.show(state.promoCodeLoading)
}
private fun handleOrderingDelayBlock(isVisible: Boolean) {
if (isVisible) binding.tvSoldOutDesc.show() else binding.tvSoldOutDesc.hide()
}
private fun handleNotificationBlock(state: ShopState) {
if (isVisible) {
binding.tvSoldOutDesc.show()
getSelectedSalesProduct(state)?.notification?.let { notification ->
binding.tvSoldOutDesc.text = notification.description
}
} else {
binding.tvSoldOutDesc.hide()
}
}
// private fun handleOrderingDelayBlock(isVisible: Boolean) {
// if (isVisible) binding.tvSoldOutDesc.show() else binding.tvSoldOutDesc.hide()
// }
//
// private fun handleNotificationBlock(state: ShopState) {
// if (isVisible) {
// binding.tvSoldOutDesc.show()
// getSelectedSalesProduct(state)?.notification?.let { notification ->
// binding.tvSoldOutDesc.text = notification.description
// }
// } else {
// binding.tvSoldOutDesc.hide()
// }
// }
private fun handleButtonsState(state: ShopState) = with(binding) {
btnPayGooglePay.root.show(state.isGooglePayAvailable)

View file

@ -2,18 +2,20 @@ package com.tangem.tap.features.tokens.impl.domain
import androidx.paging.PagingData
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toMapKey
import com.tangem.common.flatMap
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.extensions.derivationPath
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.common.util.supportsHdWallet
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.*
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.global.GlobalAction
@ -24,10 +26,6 @@ import com.tangem.tap.features.tokens.legacy.redux.TokenWithBlockchain
import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.tap.userWalletsListManager
import com.tangem.tap.walletCurrenciesManager
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import timber.log.Timber
@ -51,11 +49,12 @@ internal class DefaultTokensListInteractor(
override suspend fun saveChanges(tokens: List<TokenWithBlockchain>, blockchains: List<Blockchain>) {
val scanResponse = requireNotNull(reduxStateHolder.scanResponse)
val derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle()
val currentTokens = store.state.tokensState.addedWallets
.toNonCustomTokensWithBlockchains(style = scanResponse.card.derivationStyle)
.toNonCustomTokensWithBlockchains(derivationStyle = derivationStyle)
val currentBlockchains = store.state.tokensState.addedWallets
.toNonCustomBlockchains(derivationStyle = scanResponse.card.derivationStyle)
.toNonCustomBlockchains(derivationStyle = derivationStyle)
val blockchainsToAdd = blockchains.filterNot(currentBlockchains::contains)
val blockchainsToRemove = currentBlockchains.filterNot(blockchains::contains)
@ -73,18 +72,18 @@ internal class DefaultTokensListInteractor(
remove(
tokens = tokensToRemove,
blockchains = blockchainsToRemove,
derivationStyle = scanResponse.card.derivationStyle,
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
)
add(tokens = tokensToAdd, blockchains = blockchainsToAdd, scanResponse = scanResponse)
}
private fun List<WalletDataModel>.toNonCustomTokensWithBlockchains(
style: DerivationStyle?,
derivationStyle: DerivationStyle?,
): List<TokenWithBlockchain> {
return this.map(WalletDataModel::currency)
.mapNotNull { currency ->
if (currency !is Currency.Token || currency.isCustomCurrency(style)) return@mapNotNull null
if (currency !is Currency.Token || currency.isCustomCurrency(derivationStyle)) return@mapNotNull null
TokenWithBlockchain(token = currency.token, blockchain = currency.blockchain)
}
.distinct()
@ -123,7 +122,7 @@ internal class DefaultTokensListInteractor(
val currenciesToAdd = convertToCurrencies(
tokens = tokens,
blockchains = blockchains,
derivationStyle = scanResponse.card.derivationStyle,
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
)
// TODO("[REDACTED_TASK_KEY] use DerivationManager")
@ -136,10 +135,11 @@ internal class DefaultTokensListInteractor(
}
private suspend fun deriveMissingBlockchains(scanResponse: ScanResponse, currencies: List<Currency>) {
val derivations = listOfNotNull(
getDerivations(EllipticCurve.Secp256k1, scanResponse, currencies),
getDerivations(EllipticCurve.Ed25519, scanResponse, currencies),
).associate(transform = TokensMiddleware.DerivationData::derivations)
val config = CardConfig.createConfig(scanResponse.card)
val derivations = currencies.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencies) }
}.associate(transform = TokensMiddleware.DerivationData::derivations)
if (derivations.isEmpty()) {
submitAdd(scanResponse, currencies)
@ -184,7 +184,7 @@ internal class DefaultTokensListInteractor(
.map(Currency::blockchain)
.distinct()
.filter { it.getSupportedCurves().contains(curve) }
.mapNotNull { it.derivationPath(scanResponse.card.derivationStyle) }
.mapNotNull { it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) }
val customTokensCandidates = currencyList
.filter { it.blockchain.getSupportedCurves().contains(curve) }

View file

@ -234,7 +234,7 @@ private fun Preview_TokensListScreen_Read() {
TokensListScreen(
stateHolder = TokensListStateHolder.ReadContent(
toolbarState = TokensListToolbarState.Title.Read(
titleResId = R.string.search_tokens_title,
titleResId = R.string.common_search_tokens,
onBackButtonClick = {},
onSearchButtonClick = {},
),

View file

@ -11,12 +11,7 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@ -186,7 +181,7 @@ private fun Preview_AddTokensToolbar_ReadAccess() {
TangemTheme {
TokensListToolbar(
state = Title.Read(
titleResId = R.string.search_tokens_title,
titleResId = R.string.common_search_tokens,
onBackButtonClick = {},
onSearchButtonClick = {},
),

View file

@ -102,14 +102,14 @@ internal class TokensListViewModel @Inject constructor(
private fun getInitialToolbarState(): TokensListToolbarState {
return if (args.isManageAccess) {
TokensListToolbarState.Title.Manage(
titleResId = R.string.main_manage_tokens,
titleResId = R.string.add_tokens_title,
onBackButtonClick = actionsHandler::onBackButtonClick,
onSearchButtonClick = actionsHandler::onSearchButtonClick,
onAddCustomTokenClick = actionsHandler::onAddCustomTokenClick,
)
} else {
TokensListToolbarState.Title.Read(
titleResId = R.string.search_tokens_title,
titleResId = R.string.common_search_tokens,
onBackButtonClick = actionsHandler::onBackButtonClick,
onSearchButtonClick = actionsHandler::onSearchButtonClick,
)

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.tokens.legacy.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.tap.domain.model.WalletDataModel
import org.rekotlin.Action

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.tokens.legacy.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.ByteArrayKey
@ -13,7 +13,9 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.DomainWrapped
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.extensions.derivationPath
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.common.util.hasDerivation
import com.tangem.domain.common.util.supportsHdWallet
import com.tangem.domain.features.addCustomToken.CustomCurrency
@ -21,7 +23,7 @@ import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.domainStore
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.ManageTokens
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
@ -30,11 +32,6 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.tap.userWalletsListManager
import com.tangem.tap.walletCurrenciesManager
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Middleware
@ -72,7 +69,7 @@ object TokensMiddleware {
currencies = convertToCurrencies(
blockchains = blockchainsToRemove,
tokens = tokensToRemove,
derivationStyle = scanResponse.card.derivationStyle,
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
),
)
@ -87,7 +84,7 @@ object TokensMiddleware {
val currencyList = convertToCurrencies(
blockchains = blockchainsToAdd,
tokens = tokensToAdd,
derivationStyle = scanResponse.card.derivationStyle,
derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(),
)
if (scanResponse.supportsHdWallet()) {
@ -123,10 +120,11 @@ object TokensMiddleware {
currencyList: List<Currency>,
onSuccess: (ScanResponse) -> Unit,
) {
val derivationDataList = listOfNotNull(
getDerivations(EllipticCurve.Secp256k1, scanResponse, currencyList),
getDerivations(EllipticCurve.Ed25519, scanResponse, currencyList),
)
val config = CardConfig.createConfig(scanResponse.card)
val derivationDataList = currencyList.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencyList) }
}
val derivations = derivationDataList.associate { it.derivations }
if (derivations.isEmpty()) {
onSuccess(scanResponse)
@ -175,7 +173,7 @@ object TokensMiddleware {
val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter {
it.getSupportedCurves().contains(curve)
}.mapNotNull {
it.derivationPath(scanResponse.card.derivationStyle)
it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
val customTokensCandidates = currencyList.filter {

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.tokens.legacy.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.models.Currency

View file

@ -1,8 +1,9 @@
package com.tangem.tap.features.wallet.models
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.derivationPath
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.toCoinId
import com.tangem.domain.features.addCustomToken.CustomCurrency

View file

@ -13,6 +13,7 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.userwallets.GetCardImageUseCase
import com.tangem.domain.wallets.legacy.lockIfLockable
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
@ -24,7 +25,6 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.userWalletList.lockIfLockable
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen

View file

@ -1,11 +1,7 @@
package com.tangem.tap.features.wallet.ui
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.annotation.ColorRes
@ -23,8 +19,8 @@ import com.tangem.common.doOnResult
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.feature.swap.api.SwapFeatureToggleManager
import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.sdk.extensions.dpToPx
@ -32,14 +28,7 @@ import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.common.TestActions
import com.tangem.tap.common.analytics.events.DetailsScreen
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.appendIfNotNull
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
import com.tangem.tap.common.extensions.getColor
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.utils.SafeStoreSubscriber
@ -57,15 +46,7 @@ import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.test.TestWallet
import com.tangem.tap.features.wallet.ui.utils.assembleWarnings
import com.tangem.tap.features.wallet.ui.utils.getAvailableActions
import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy
import com.tangem.tap.features.wallet.ui.utils.isAvailableToSell
import com.tangem.tap.features.wallet.ui.utils.isAvailableToSwap
import com.tangem.tap.features.wallet.ui.utils.mainButton
import com.tangem.tap.features.wallet.ui.utils.shouldShowMultipleAddress
import com.tangem.tap.features.wallet.ui.utils.*
import com.tangem.tap.store
import com.tangem.tap.userWalletsListManagerSafe
import com.tangem.tap.walletCurrenciesManager
@ -336,10 +317,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
private fun handleCurrencyIcon(currency: Currency) = with(binding.lWalletDetails.lBalance) {
ivCurrency.load(
currency = currency,
derivationStyle = store.state.globalState
.scanResponse
?.card
?.derivationStyle,
derivationStyle = store.state.globalState.scanResponse
?.derivationStyleProvider?.getDerivationStyle(),
)
}

View file

@ -7,7 +7,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.tap.common.analytics.events.Portfolio
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
@ -77,10 +77,8 @@ class WalletAdapter : ListAdapter<WalletDataModel, WalletAdapter.WalletsViewHold
ivCurrency.load(
currency = wallet.currency,
derivationStyle = store.state.globalState
.scanResponse
?.card
?.derivationStyle,
derivationStyle = store.state.globalState.scanResponse
?.derivationStyleProvider?.getDerivationStyle(),
)
lContent.tvCurrency.text = wallet.currency.currencyName

View file

@ -7,7 +7,7 @@ import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.sdk.extensions.dpToPx
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.wallet.databinding.ViewCurrencyIconBinding

View file

@ -6,7 +6,7 @@ import com.badoo.mvicore.modelWatcher
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Portfolio
import com.tangem.tap.common.entities.FiatCurrency
@ -104,13 +104,13 @@ class MultiWalletView : WalletView() {
watcher.invoke(state)
binding.btnAddToken.setOnClickListener {
val card = store.state.globalState.scanResponse!!.card
Analytics.send(Portfolio.ButtonManageTokens())
store.dispatch(
TokensAction.SetArgs.ManageAccess(
wallets = state.walletsDataFromStores,
derivationStyle = card.derivationStyle,
derivationStyle = store.state.globalState.scanResponse
?.derivationStyleProvider?.getDerivationStyle(),
),
)
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))

View file

@ -8,6 +8,7 @@ import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.unlockIfLockable
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.*
@ -20,7 +21,6 @@ import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.userWalletList.unlockIfLockable
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay

View file

@ -4,11 +4,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.common.core.TangemError
import com.tangem.core.analytics.Analytics
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.isLocked
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.common.analytics.events.MyWallets
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.domain.userWalletList.UserWalletsListError
import com.tangem.tap.domain.userWalletList.isLocked
import com.tangem.tap.features.details.ui.cardsettings.TextReference
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState

View file

@ -251,7 +251,7 @@ private fun LoadedTokensInfo(
SpacerH2()
Text(
text = pluralStringResource(
id = R.plurals.tokens_count,
id = R.plurals.token_count,
count = tokensCount,
tokensCount,
),

View file

@ -9,6 +9,7 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.wallets.legacy.unlockIfLockable
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
@ -16,7 +17,6 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.userWalletList.unlockIfLockable
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
import com.tangem.tap.features.signin.redux.SignInAction
import com.tangem.tap.proxy.redux.DaggerGraphState

View file

@ -3,9 +3,9 @@ package com.tangem.tap.features.welcome.ui
import androidx.lifecycle.ViewModel
import com.tangem.common.core.TangemError
import com.tangem.core.analytics.Analytics
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.tap.common.analytics.events.SignIn
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.userWalletList.UserWalletsListError
import com.tangem.tap.features.details.ui.cardsettings.TextReference
import com.tangem.tap.features.welcome.redux.WelcomeAction
import com.tangem.tap.features.welcome.redux.WelcomeState

View file

@ -10,8 +10,10 @@ import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.extensions.derivationPath
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.lib.crypto.DerivationManager
@ -46,13 +48,13 @@ class DerivationManagerImpl(
} else {
null
}
val blockchainNetwork = BlockchainNetwork(blockchain, card)
val appCurrency = com.tangem.tap.features.wallet.models.Currency.fromBlockchainNetwork(
blockchainNetwork,
appToken,
)
val scanResponse = appStateHolder.scanResponse
if (scanResponse != null) {
val blockchainNetwork = BlockchainNetwork(blockchain, scanResponse.derivationStyleProvider)
val appCurrency = com.tangem.tap.features.wallet.models.Currency.fromBlockchainNetwork(
blockchainNetwork,
appToken,
)
deriveMissingBlockchains(
scanResponse = scanResponse,
currencyList = listOf(appCurrency),
@ -70,7 +72,7 @@ class DerivationManagerImpl(
val scanResponse = appStateHolder.scanResponse
val blockchain = Blockchain.fromNetworkId(networkId)
if (scanResponse != null && blockchain != null) {
return blockchain.derivationPath(appStateHolder.getActualCard()?.derivationStyle)?.rawPath
return blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())?.rawPath
}
return null
}
@ -93,10 +95,11 @@ class DerivationManagerImpl(
onSuccess: (ScanResponse) -> Unit,
onFailure: (Exception) -> Unit,
) {
val derivationDataList = listOfNotNull(
getDerivations(EllipticCurve.Secp256k1, scanResponse, currencyList),
getDerivations(EllipticCurve.Ed25519, scanResponse, currencyList),
)
val config = CardConfig.createConfig(scanResponse.card)
val derivationDataList = currencyList.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencyList) }
}
val derivations = derivationDataList.associate { it.derivations }
if (derivations.isEmpty()) {
onSuccess(scanResponse)
@ -162,7 +165,7 @@ class DerivationManagerImpl(
val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter {
it.getSupportedCurves().contains(curve)
}.mapNotNull {
it.derivationPath(scanResponse.card.derivationStyle)
it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
val customTokensCandidates = currencyList.filter {

View file

@ -1,86 +0,0 @@
package com.tangem.tap.proxy
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.TransactionStatus
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem
import com.tangem.blockchain.common.txhistory.TransactionHistoryState
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.lib.crypto.TxHistoryManager
import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryItem
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryState
import com.tangem.lib.crypto.models.txhistory.ProxyTransactionStatus
class TxHistoryManagerImpl(
private val appStateHolder: AppStateHolder,
) : TxHistoryManager {
override suspend fun checkTxHistoryState(networkId: String, derivationPath: String?): ProxyTransactionHistoryState {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain, derivationPath)
val state = walletManager.getTransactionHistoryState(address = walletManager.wallet.address)
return state.mapToProxy()
}
override suspend fun getTxHistoryItems(
networkId: String,
derivationPath: String?,
page: Int,
pageSize: Int,
): List<ProxyTransactionHistoryItem> {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain, derivationPath)
val itemsResult = walletManager.getTransactionsHistory(
address = walletManager.wallet.address,
page = page,
pageSize = pageSize,
)
return when (itemsResult) {
is Result.Success -> itemsResult.data.items.map { historyItem -> historyItem.mapToProxy() }
is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage)
}
}
private fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager {
val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList())
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
return requireNotNull(walletManager) { "no wallet manager found" }
}
private fun TransactionHistoryState.mapToProxy(): ProxyTransactionHistoryState {
return when (this) {
TransactionHistoryState.Success.Empty -> ProxyTransactionHistoryState.Success.Empty
is TransactionHistoryState.Failed.FetchError -> ProxyTransactionHistoryState.Failed.FetchError(exception)
TransactionHistoryState.NotImplemented -> ProxyTransactionHistoryState.NotImplemented
is TransactionHistoryState.Success.HasTransactions ->
ProxyTransactionHistoryState.Success.HasTransactions(txCount)
}
}
private fun TransactionHistoryItem.mapToProxy() = ProxyTransactionHistoryItem(
txHash = txHash,
timestamp = timestamp,
direction = when (val direction = direction) {
is TransactionHistoryItem.TransactionDirection.Incoming ->
ProxyTransactionHistoryItem.TransactionDirection.Incoming(direction.from)
is TransactionHistoryItem.TransactionDirection.Outgoing ->
ProxyTransactionHistoryItem.TransactionDirection.Outgoing(direction.to)
},
status = when (status) {
TransactionStatus.Confirmed -> ProxyTransactionStatus.Confirmed
TransactionStatus.Unconfirmed -> ProxyTransactionStatus.Unconfirmed
},
type = when (type) {
TransactionHistoryItem.TransactionType.Transfer -> ProxyTransactionHistoryItem.TransactionType.Transfer
},
amount = ProxyAmount(
currencySymbol = amount.currencySymbol,
value = requireNotNull(amount.value) { "Amount value must not be null" },
decimals = amount.decimals,
),
)
}

View file

@ -8,7 +8,6 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider
import com.tangem.lib.crypto.DerivationManager
import com.tangem.lib.crypto.TransactionManager
import com.tangem.lib.crypto.TxHistoryManager
import com.tangem.lib.crypto.UserWalletManager
import com.tangem.tap.proxy.*
import dagger.Module
@ -59,12 +58,6 @@ class ProxyModule {
)
}
@Provides
@Singleton
fun provideTxHistoryManager(appStateHolder: AppStateHolder): TxHistoryManager {
return TxHistoryManagerImpl(appStateHolder = appStateHolder)
}
// regions FeatureConsumers
@Provides
@Singleton