Updated on 2026-08-14

This commit is contained in:
Tangem 2022-04-08 22:45:47 +04:00
parent 14a4abd0cb
commit 17b31dd192
37 changed files with 1196 additions and 548 deletions

View file

@ -106,6 +106,7 @@ object TangemSdk {
is TangemSdkError.IssuerSignatureLoadingFailed -> TangemSdkError.IssuerSignatureLoadingFailed()
is TangemSdkError.BackupFailedFirmware -> TangemSdkError.BackupFailedFirmware()
is TangemSdkError.UserForgotTheCode -> TangemSdkError.UserForgotTheCode()
is TangemSdkError.BackupFailedIncompatibleBatch -> TangemSdkError.BackupFailedIncompatibleBatch()
}
}

View file

@ -39,7 +39,7 @@ fun Picasso.loadCurrenciesIcon(
}
url != null -> {
if (token != null) {
setTokenImage(imageView, textView, token)
setTokenImage(imageView, textView, token, blockchain)
}
this.load(url)
.transform(RoundedCornersTransform())
@ -76,7 +76,7 @@ private fun setOfflineCurrencyImage(
) {
when (token) {
null -> setBlockchainImage(imageView, textView, blockchain)
else -> setTokenImage(imageView, textView, token)
else -> setTokenImage(imageView, textView, token, blockchain)
}
}
@ -95,9 +95,10 @@ private fun setTokenImage(
imageView: ImageFilterView,
textView: TextView,
token: Token,
tokenBlockchain: Blockchain
) {
imageView.setImageResource(R.drawable.shape_circle)
if (token.blockchain.isTestnet()) {
if (tokenBlockchain.isTestnet()) {
imageView.saturation = 0f
} else {
imageView.setColorFilter(token.getColor())

View file

@ -17,7 +17,7 @@ import com.tangem.tap.domain.configurable.config.ConfigManager
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.tokens.CardCurrencies
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.models.getPendingTransactions
@ -33,7 +33,7 @@ import java.math.BigDecimal
class TapWalletManager {
val walletManagerFactory: WalletManagerFactory
by lazy { WalletManagerFactory(blockchainSdkConfig) }
by lazy { WalletManagerFactory(blockchainSdkConfig) }
private val coinMarketCapService = CoinMarketCapService()
private val blockchainSdkConfig by lazy {
@ -45,6 +45,7 @@ class TapWalletManager {
suspend fun loadWalletData(walletManager: WalletManager) {
val blockchain = walletManager.wallet.blockchain
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
val result = if (walletManagersThrottler.isStillThrottled(blockchain)) {
walletManagersThrottler.geValue(blockchain)!!
} else {
@ -53,21 +54,26 @@ class TapWalletManager {
when (result) {
is Result.Success -> {
checkForRentWarning(walletManager)
dispatchOnMain(WalletAction.LoadWallet.Success(result.data))
dispatchOnMain(WalletAction.LoadWallet.Success(result.data, blockchainNetwork))
}
is Result.Failure -> {
when (result.error) {
is TapError.WalletManagerUpdate.NoAccountError -> {
dispatchOnMain(WalletAction.LoadWallet.NoAccount(
walletManager.wallet,
(result.error as TapError.WalletManagerUpdate.NoAccountError).customMessage
))
dispatchOnMain(
WalletAction.LoadWallet.NoAccount(
walletManager.wallet,
blockchainNetwork,
(result.error as TapError.WalletManagerUpdate.NoAccountError).customMessage
)
)
}
else -> {
dispatchOnMain(WalletAction.LoadWallet.Failure(
walletManager.wallet,
result.error.localizedMessage
))
dispatchOnMain(
WalletAction.LoadWallet.Failure(
walletManager.wallet,
result.error.localizedMessage
)
)
}
}
}
@ -84,8 +90,8 @@ class TapWalletManager {
suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, wallet: Wallet) {
val currencies = wallet.getTokens()
.map { Currency.Token(it) }
.plus(Currency.Blockchain(wallet.blockchain))
.map { Currency.Token(it, wallet.blockchain, wallet.publicKey.derivationPath?.rawPath) }
.plus(Currency.Blockchain(wallet.blockchain, wallet.publicKey.derivationPath?.rawPath))
loadFiatRate(fiatCurrency, currencies)
}
@ -129,7 +135,8 @@ class TapWalletManager {
configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled)
configManager?.turnOff(ConfigManager.isTopUpEnabled)
} else if (blockchain == Blockchain.Bitcoin
|| data.walletData?.blockchain == Blockchain.Bitcoin.id) {
|| data.walletData?.blockchain == Blockchain.Bitcoin.id
) {
configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled)
configManager?.resetToDefault(ConfigManager.isTopUpEnabled)
} else {
@ -160,8 +167,10 @@ class TapWalletManager {
loadMultiWalletData(data, blockchain, primaryWalletManager)
} else {
dispatchOnMain(
WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager),
WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))
WalletAction.MultiWallet.AddBlockchains(
listOf(BlockchainNetwork.fromWalletManager(primaryWalletManager)),
listOf(primaryWalletManager)
)
)
}
} else {
@ -176,32 +185,39 @@ class TapWalletManager {
}
private suspend fun loadMultiWalletData(
scanResponse: ScanResponse, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
scanResponse: ScanResponse,
primaryBlockchain: Blockchain?,
primaryWalletManager: WalletManager?
) {
val primaryTokens = primaryWalletManager?.cardTokens?.toList() ?: emptyList()
val savedCurrencies = currenciesRepository.loadCardCurrencies(scanResponse.card.cardId)
val savedCurrencies = currenciesRepository.loadSavedCurrencies(scanResponse.card.cardId)
if (savedCurrencies == null) {
if (savedCurrencies.isEmpty()) {
if (primaryBlockchain != null && primaryWalletManager != null) {
val blockchainNetwork = BlockchainNetwork.fromWalletManager(primaryWalletManager)
dispatchOnMain(
WalletAction.MultiWallet.SaveCurrencies(
CardCurrencies(blockchains = listOf(primaryBlockchain), tokens = primaryTokens)
WalletAction.MultiWallet.SaveCurrencies(listOf(blockchainNetwork)),
WalletAction.MultiWallet.AddBlockchains(
listOf(blockchainNetwork),
listOf(primaryWalletManager)
),
WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager),
WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain)),
WalletAction.MultiWallet.AddTokens(primaryTokens.toList())
WalletAction.MultiWallet.AddTokens(primaryTokens.toList(), blockchainNetwork)
)
} else {
val blockchains = listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains.toList())
val blockchainNetworks = listOf(
BlockchainNetwork(Blockchain.Bitcoin,null, emptyList()),
BlockchainNetwork(Blockchain.Ethereum,null, emptyList())
)
val walletManagers = walletManagerFactory.makeWalletManagersForApp(
scanResponse,
blockchainNetworks
)
dispatchOnMain(
WalletAction.MultiWallet.SaveCurrencies(CardCurrencies(
blockchains = blockchains,
tokens = emptyList()
)),
WalletAction.MultiWallet.AddWalletManagers(walletManagers),
WalletAction.MultiWallet.AddBlockchains(blockchains.toList()),
WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks),
WalletAction.MultiWallet.AddBlockchains(blockchainNetworks, walletManagers),
)
}
dispatchOnMain(
@ -209,22 +225,25 @@ class TapWalletManager {
WalletAction.MultiWallet.FindTokensInUse,
)
} else {
val blockchains = savedCurrencies.blockchains.toList()
val walletManagers = if (
primaryTokens.isNotEmpty() &&
primaryWalletManager != null && primaryBlockchain != null
primaryWalletManager != null &&
primaryBlockchain != null
) {
val blockchainsWithoutPrimary = blockchains.filterNot { it == primaryBlockchain }
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchainsWithoutPrimary)
.plus(primaryWalletManager)
val blockchainsWithoutPrimary = savedCurrencies.filterNot { it.blockchain == primaryBlockchain }
walletManagerFactory.makeWalletManagersForApp(
scanResponse,
blockchainsWithoutPrimary
).plus(primaryWalletManager)
} else {
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains)
walletManagerFactory.makeWalletManagersForApp(scanResponse, savedCurrencies)
}
dispatchOnMain(
WalletAction.MultiWallet.AddWalletManagers(walletManagers),
WalletAction.MultiWallet.AddBlockchains(blockchains),
WalletAction.MultiWallet.AddTokens(savedCurrencies.tokens.toList()),
WalletAction.MultiWallet.AddBlockchains(savedCurrencies, walletManagers),
)
savedCurrencies.map {
dispatchOnMain(WalletAction.MultiWallet.AddTokens(it.tokens, it))
}
}
}
@ -268,10 +287,12 @@ class TapWalletManager {
when (val result = rentProvider.minimalBalanceForRentExemption()) {
is com.tangem.blockchain.extensions.Result.Success -> {
fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean = balance < rentExempt
fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean =
balance < rentExempt
val balance = walletManager.wallet.fundsAvailable(AmountType.Coin)
val outgoingTxs = walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing)
val outgoingTxs =
walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing)
val rentExempt = result.data
val show = if (outgoingTxs.isEmpty()) {
isNeedToShowWarning(balance, rentExempt)
@ -283,11 +304,13 @@ class TapWalletManager {
if (!show) return
val currency = walletManager.wallet.blockchain.currency
dispatchOnMain(WalletAction.SetWalletRent(
blockchain = walletManager.wallet.blockchain,
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
))
dispatchOnMain(
WalletAction.SetWalletRent(
blockchain = BlockchainNetwork.fromWalletManager(walletManager),
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
)
)
}
is com.tangem.blockchain.extensions.Result.Failure -> {}
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.domain
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.common.card.Card
import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.FirmwareVersion
@ -13,7 +14,7 @@ import java.util.*
object TapWorkarounds {
fun isStart2CoinIssuer(cardIssuer: String?): Boolean {
return cardIssuer?.toLowerCase(Locale.US) == START_2_COIN_ISSUER
return cardIssuer?.lowercase(Locale.US) == START_2_COIN_ISSUER
}
val Card.isStart2Coin: Boolean
@ -22,13 +23,25 @@ object TapWorkarounds {
val Card.isTestCard: Boolean
get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH)
val Card.useOldStyleDerivation: Boolean
get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95"
val Card.derivationStyle: DerivationStyle?
get() = if (!settings.isHDWalletAllowed) {
null
} else if (useOldStyleDerivation) {
DerivationStyle.LEGACY
} else {
DerivationStyle.NEW
}
fun Card.isExcluded(): Boolean {
val excludedBatch = excludedBatches.contains(batchId)
val excludedIssuerName = excludedIssuers.contains(issuer.name.uppercase(Locale.ROOT))
return excludedBatch || excludedIssuerName
}
fun Card.isNotSupportedInThatRelease():Boolean {
fun Card.isNotSupportedInThatRelease(): Boolean {
return false
}
@ -44,27 +57,27 @@ object TapWorkarounds {
private const val TEST_CARD_ID_STARTS_WITH = "FF99"
private val excludedBatches = listOf(
"0027",
"0030",
"0031",
"0035"
"0027",
"0030",
"0031",
"0035"
)
private val excludedIssuers = listOf(
"TTM BANK"
"TTM BANK"
)
private val tangemWalletBatches = listOf("AC01")
private val tangemNoteBatches = mapOf(
"AB01" to Blockchain.Bitcoin,
"AB02" to Blockchain.Ethereum,
"AB03" to Blockchain.CardanoShelley,
"AB04" to Blockchain.Dogecoin,
"AB05" to Blockchain.BSC,
"AB06" to Blockchain.XRP,
"AB07" to Blockchain.Bitcoin,
"AB08" to Blockchain.Ethereum,
"AB01" to Blockchain.Bitcoin,
"AB02" to Blockchain.Ethereum,
"AB03" to Blockchain.CardanoShelley,
"AB04" to Blockchain.Dogecoin,
"AB05" to Blockchain.BSC,
"AB06" to Blockchain.XRP,
"AB07" to Blockchain.Bitcoin,
"AB08" to Blockchain.Ethereum,
)
}

View file

@ -1,26 +1,30 @@
package com.tangem.tap.domain.extensions
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.blockchain.common.*
import com.tangem.common.card.Card
import com.tangem.common.card.CardWallet
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.TapWorkarounds.useOldStyleDerivation
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.wallet.redux.Currency
fun WalletManagerFactory.makeWalletManagerForApp(
scanResponse: ScanResponse, blockchain: Blockchain
scanResponse: ScanResponse, blockchain: Blockchain, derivationParams: DerivationParams?
): WalletManager? {
val card = scanResponse.card
if (card.isTestCard && blockchain.getTestnetVersion() == null) return null
val supportedCurves = blockchain.getSupportedCurves() ?: return null
val supportedCurves = blockchain.getSupportedCurves()
val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) }
val wallet = selectWallet(wallets) ?: return null
val environmentBlockchain = if (card.isTestCard) blockchain.getTestnetVersion()!! else blockchain
val environmentBlockchain =
if (card.isTestCard) blockchain.getTestnetVersion()!! else blockchain
val seedKey = wallet.extendedPublicKey
return when {
@ -31,16 +35,21 @@ fun WalletManagerFactory.makeWalletManagerForApp(
environmentBlockchain, wallet.curve
)
}
seedKey != null -> {
seedKey != null && derivationParams != null -> {
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
val derivedKey = derivedKeys?.get(blockchain.derivationPath())
val derivationPath = when (derivationParams) {
is DerivationParams.Default -> blockchain.derivationPath(derivationParams.style)
is DerivationParams.Custom -> derivationParams.path
}
val derivedKey = derivedKeys?.get(derivationPath)
?: return null
makeWalletManager(
cardId = card.cardId,
blockchain = environmentBlockchain,
seedKey = wallet.publicKey,
derivedKey = derivedKey
derivedKey = derivedKey,
derivation = derivationParams
)
}
else -> {
@ -54,12 +63,44 @@ fun WalletManagerFactory.makeWalletManagerForApp(
}
}
fun WalletManagerFactory.makeWalletManagerForApp(
scanResponse: ScanResponse, blockchainNetwork: BlockchainNetwork
): WalletManager? {
return makeWalletManagerForApp(
scanResponse,
blockchain = blockchainNetwork.blockchain,
derivationParams = getDerivationParams(blockchainNetwork.derivationPath, scanResponse.card)
)
}
private fun getDerivationParams(derivationPath: String?, card: Card): DerivationParams? {
return derivationPath?.let {
DerivationParams.Custom(
DerivationPath(it)
)
} ?: if (!card.settings.isHDWalletAllowed) {
null
} else if (card.useOldStyleDerivation) {
DerivationParams.Default(DerivationStyle.LEGACY)
} else {
DerivationParams.Default(DerivationStyle.NEW)
}
}
fun WalletManagerFactory.makeWalletManagerForApp(
scanResponse: ScanResponse, currency: Currency
): WalletManager? {
return makeWalletManagerForApp(
scanResponse,
blockchain = currency.blockchain,
derivationParams = getDerivationParams(currency.derivationPath, scanResponse.card)
)
}
fun WalletManagerFactory.makeWalletManagersForApp(
scanResponse: ScanResponse, blockchains: List<Blockchain>,
scanResponse: ScanResponse, blockchains: List<BlockchainNetwork>,
): List<WalletManager> {
val isTestCard = scanResponse.card.isTestCard
val filteredBlockchains = blockchains.mapNotNull { if (isTestCard) it.getTestnetVersion() else it }
return filteredBlockchains.mapNotNull { makeWalletManagerForApp(scanResponse, it) }
return blockchains.mapNotNull { this.makeWalletManagerForApp(scanResponse, it) }
}
fun WalletManagerFactory.makePrimaryWalletManager(
@ -70,7 +111,12 @@ fun WalletManagerFactory.makePrimaryWalletManager(
} else {
scanResponse.getBlockchain()
}
return makeWalletManagerForApp(scanResponse, blockchain)
val derivationParams = getDerivationParams(null, scanResponse.card)
return makeWalletManagerForApp(
scanResponse = scanResponse,
blockchain = blockchain,
derivationParams = derivationParams
)
}
private fun selectWallet(wallets: List<CardWallet>): CardWallet? {

View file

@ -17,6 +17,7 @@ import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.CreateWalletTask
import com.tangem.tap.domain.ProductType
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.tasks.product.CreateWalletsTask
@ -177,7 +178,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
val blockchainsForCurve = getBlockchains(response.cardId).filter {
it.getSupportedCurves().contains(response.wallet.curve)
}
val derivationPaths = blockchainsForCurve.mapNotNull { it.derivationPath() }
val derivationPaths = blockchainsForCurve.mapNotNull { it.derivationPath(card.derivationStyle) }
if (derivationPaths.isNotEmpty()) {
map[response.wallet.publicKey.toMapKey()] = derivationPaths
}

View file

@ -31,8 +31,10 @@ import com.tangem.tap.domain.TapWorkarounds
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
import com.tangem.tap.domain.TapWorkarounds.isExcluded
import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.tap.domain.TapWorkarounds.useOldStyleDerivation
import com.tangem.tap.domain.extensions.getPrimaryCurve
import com.tangem.tap.domain.extensions.getSingleWallet
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.domain.tokens.CurrenciesRepository
import com.tangem.tap.domain.twins.TwinsHelper
import com.tangem.tap.preferencesStorage
@ -60,7 +62,6 @@ data class ScanResponse(
cardToken.symbol,
cardToken.contractAddress,
cardToken.decimals,
Blockchain.fromId(walletData.blockchain)
)
}
@ -272,28 +273,38 @@ private class ScanWalletProcessor(
}
}
private fun getBlockchainsToDerive(card: Card): List<Blockchain> {
private fun getBlockchainsToDerive(card: Card): List<BlockchainNetwork> {
val currenciesRepository = currenciesRepository ?: return emptyList()
val cardCurrencies = currenciesRepository.loadCardCurrencies(card.cardId)
val cardCurrencies = currenciesRepository.loadSavedCurrencies(card.cardId).toMutableList()
val blockchainsToDerive = if (cardCurrencies == null) {
mutableListOf(Blockchain.Bitcoin, Blockchain.Ethereum)
} else {
val tokenBlockchains = cardCurrencies.tokens.map { it.blockchain }
(cardCurrencies.blockchains + tokenBlockchains).toMutableList()
val blockchainsToDerive = cardCurrencies.ifEmpty {
mutableListOf(
BlockchainNetwork(Blockchain.Bitcoin, card),
BlockchainNetwork(Blockchain.Ethereum, card))
}
if (card.settings.isHDWalletAllowed) {
blockchainsToDerive.addAll(
listOf(
Blockchain.Ethereum,
Blockchain.Binance,
Blockchain.EthereumTestnet
BlockchainNetwork(Blockchain.Ethereum, card),
BlockchainNetwork(Blockchain.Binance, card),
BlockchainNetwork(Blockchain.EthereumTestnet, card)
)
)
}
if (additionalBlockchainsToDerive != null) {
blockchainsToDerive.addAll(additionalBlockchainsToDerive)
blockchainsToDerive.addAll(additionalBlockchainsToDerive.map { BlockchainNetwork(it, card) })
}
if (!card.useOldStyleDerivation) {
blockchainsToDerive.removeAll(
listOf(
Blockchain.BSC, Blockchain.BSCTestnet,
Blockchain.Polygon, Blockchain.PolygonTestnet,
Blockchain.RSK,
Blockchain.Fantom, Blockchain.FantomTestnet,
Blockchain.Avalanche, Blockchain.AvalancheTestnet,
).map { BlockchainNetwork(it, card) }
)
}
return blockchainsToDerive.distinct()
}
@ -303,13 +314,13 @@ private class ScanWalletProcessor(
val derivations = mutableMapOf<ByteArrayKey, List<DerivationPath>>()
blockchains.forEach { blockchain ->
val curve = blockchain.getPrimaryCurve()
val curve = blockchain.blockchain.getPrimaryCurve()
val wallet = card.wallets.firstOrNull { it.curve == curve } ?: return@forEach
if (wallet.chainCode == null) return@forEach
val key = wallet.publicKey.toMapKey()
val path = blockchain.derivationPath()
val path = blockchain.derivationPath?.let { DerivationPath(it) }
if (path != null) {
val addedDerivations = derivations[key]
if (addedDerivations != null) {

View file

@ -7,11 +7,14 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Types
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.card.Card
import com.tangem.common.card.FirmwareVersion
import com.tangem.tap.common.extensions.appendIf
import com.tangem.tap.common.extensions.readJsonFileToString
import com.tangem.tap.domain.extensions.getCustomIconUrl
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
import com.tangem.tap.domain.extensions.setCustomIconUrl
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.network.createMoshi
@ -30,55 +33,48 @@ class CurrenciesRepository(val context: Application) {
private val obsoleteTokensAdapter: JsonAdapter<List<ObsoleteTokenDao>> = moshi.adapter(
Types.newParameterizedType(List::class.java, ObsoleteTokenDao::class.java)
)
private val currenciesAdapter : JsonAdapter<CurrenciesFromJson> =
private val currenciesAdapter: JsonAdapter<CurrenciesFromJson> =
moshi.adapter(CurrenciesFromJson::class.java)
private val blockchainNetworkAdapter: JsonAdapter<List<BlockchainNetwork>> =
moshi.adapter(Types.newParameterizedType(List::class.java, BlockchainNetwork::class.java))
fun loadCardCurrencies(cardId: String): CardCurrencies? {
val blockchains = loadSavedBlockchains(cardId).toMutableSet()
if (DemoHelper.isDemoCardId(cardId)) {
blockchains.addAll(DemoHelper.config.demoBlockchains)
fun saveUpdatedCurrency(cardId: String, blockchainNetwork: BlockchainNetwork) {
var changed = false
val currencies = loadSavedCurrencies(cardId).map {
if (it == blockchainNetwork) {
changed = true
blockchainNetwork
} else {
it
}
}
if (blockchains.isEmpty()) return null
return CardCurrencies(loadSavedTokens(cardId), blockchains.toList())
val updatedCurrencies = if (changed) currencies else currencies + blockchainNetwork
saveCurrencies(cardId, updatedCurrencies.distinct())
}
fun saveCardCurrencies(cardId: String, currencies: CardCurrencies) {
saveTokens(cardId, currencies.tokens)
saveBlockchains(cardId, currencies.blockchains)
fun removeToken(cardId: String, token: Token, blockchainNetwork: BlockchainNetwork) {
val currencies = loadSavedCurrencies(cardId).map {
if (it == blockchainNetwork) {
it.copy(tokens = it.tokens.filterNot { it == token })
} else {
it
}
}
saveCurrencies(cardId, currencies)
}
fun saveAddedToken(cardId: String, token: Token) {
val tokens = loadSavedTokens(cardId) + token
saveTokens(cardId, tokens)
}
fun saveAddedTokens(cardId: String, tokens: Collection<Token>) {
saveTokens(cardId, loadSavedTokens(cardId) + tokens.distinct())
}
fun saveAddedBlockchain(cardId: String, blockchain: Blockchain) {
val blockchains = loadSavedBlockchains(cardId) + blockchain
saveBlockchains(cardId, blockchains.distinct())
}
fun removeToken(cardId: String, token: Token) {
val tokens = loadSavedTokens(cardId).filterNot { it == token }
saveTokens(cardId, tokens)
}
fun removeBlockchain(cardId: String, blockchain: Blockchain) {
val blockchains = loadSavedBlockchains(cardId).filterNot { it == blockchain }
saveBlockchains(cardId, blockchains)
fun removeBlockchain(cardId: String, blockchainNetwork: BlockchainNetwork) {
val currencies = loadSavedCurrencies(cardId).filterNot { it == blockchainNetwork }
saveCurrencies(cardId, currencies)
}
fun removeCurrencies(cardId: String) {
saveTokens(cardId, emptyList())
saveBlockchains(cardId, emptyList())
saveCurrencies(cardId, emptyList())
}
private fun loadSavedTokens(cardId: String): List<Token> {
@Deprecated("Use BlockchainNetwork instead")
private fun loadSavedTokens(cardId: String): List<TokenDao> {
val json = try {
context.readFileText(getFileNameForTokens(cardId))
} catch (exception: Exception) {
@ -86,17 +82,13 @@ class CurrenciesRepository(val context: Application) {
}
return try {
tokensAdapter.fromJson(json)!!.map { it.toToken() }.distinct()
tokensAdapter.fromJson(json) ?: emptyList()
} catch (exception: Exception) {
emptyList()
}
}
private fun saveTokens(cardId: String, tokens: List<Token>) {
val json = tokensAdapter.toJson(tokens.distinct().map { TokenDao.fromToken(it) })
context.rewriteFile(json, getFileNameForTokens(cardId))
}
@Deprecated("Use BlockchainNetwork instead")
private fun loadSavedBlockchains(cardId: String): List<Blockchain> {
return try {
val json = context.readFileText(getFileNameForBlockchains(cardId))
@ -106,8 +98,46 @@ class CurrenciesRepository(val context: Application) {
}
}
private fun saveBlockchains(cardId: String, blockchains: List<Blockchain>) {
val json = blockchainsAdapter.toJson(blockchains.distinct())
fun loadSavedCurrencies(cardId: String): List<BlockchainNetwork> {
if (DemoHelper.isDemoCardId(cardId)) {
return loadDemoCurrencies(cardId)
}
return try {
val json = context.readFileText(getFileNameForBlockchains(cardId))
blockchainNetworkAdapter.fromJson(json)?.distinct() ?: loadSavedCurrenciesOldWay(cardId)
} catch (exception: Exception) {
emptyList()
}
}
private fun loadDemoCurrencies(cardId: String): List<BlockchainNetwork> {
return DemoHelper.config.demoBlockchains.map {
BlockchainNetwork(
blockchain = it,
derivationPath = it.derivationPath(DerivationStyle.LEGACY)?.rawPath,
tokens = emptyList()
)
}
}
private fun loadSavedCurrenciesOldWay(cardId: String): List<BlockchainNetwork> {
val blockchains = loadSavedBlockchains(cardId)
val tokens = loadSavedTokens(cardId)
val blockchainNetworks = blockchains.map { blockchain ->
BlockchainNetwork(
blockchain = blockchain,
derivationPath = null,
tokens = tokens
.filter { it.blockchainDao.toBlockchain() == blockchain }
.map { it.toToken() }
)
}
saveCurrencies(cardId, blockchainNetworks) // migrate saved currencies
return blockchainNetworks
}
fun saveCurrencies(cardId: String, currencies: List<BlockchainNetwork>) {
val json = blockchainNetworkAdapter.toJson(currencies)
context.rewriteFile(json, getFileNameForBlockchains(cardId))
}
@ -174,9 +204,11 @@ class CurrenciesRepository(val context: Application) {
// 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(
removeAll(
listOf(
// Any blockchain
))
)
)
}
}
@ -214,24 +246,11 @@ data class TokenDao(
symbol = symbol,
contractAddress = contractAddress,
decimals = decimalCount,
blockchain = blockchainDao.toBlockchain()
).apply {
customIconUrl?.let { this.setCustomIconUrl(it) }
}
}
companion object {
fun fromToken(token: Token): TokenDao {
return TokenDao(
name = token.name,
symbol = token.symbol,
contractAddress = token.contractAddress,
decimalCount = token.decimals,
blockchainDao = BlockchainDao.fromBlockchain(token.blockchain),
customIconUrl = token.getCustomIconUrl()
)
}
}
}
@JsonClass(generateAdapter = true)
@ -276,29 +295,78 @@ data class ObsoleteTokenDao(
}
}
@JsonClass(generateAdapter = true)
data class CardCurrenciesDao(
val tokens: List<TokenDao>,
val blockchains: List<Blockchain>,
) {
fun toCardCurrencies(): CardCurrencies {
return CardCurrencies(
tokens = tokens.map { it.toToken() }.distinct(),
blockchains = blockchains
)
}
companion object {
fun fromCardCurrencies(cardCurrencies: CardCurrencies): CardCurrenciesDao {
return CardCurrenciesDao(
tokens = cardCurrencies.tokens.map { TokenDao.fromToken(it) }.distinct(),
blockchains = cardCurrencies.blockchains
)
}
}
}
//@JsonClass(generateAdapter = true)
//data class CardCurrenciesDao(
// val tokens: List<TokenDao>,
// val blockchains: List<Blockchain>,
//) {
// fun toCardCurrencies(): CardCurrencies {
// return CardCurrencies(
// tokens = tokens.map { it.toToken() }.distinct(),
// blockchains = blockchains
// )
// }
//
// companion object {
// fun fromCardCurrencies(cardCurrencies: CardCurrencies): CardCurrenciesDao {
// return CardCurrenciesDao(
// tokens = cardCurrencies.tokens.map { TokenDao.fromToken(it) }.distinct(),
// blockchains = cardCurrencies.blockchains
// )
// }
// }
//}
data class CardCurrencies(
val tokens: List<Token>,
val blockchains: List<Blockchain>,
)
)
@JsonClass(generateAdapter = true)
data class BlockchainNetwork(
val blockchain: Blockchain,
val derivationPath: String?,
val tokens: List<Token>
) {
constructor(blockchain: Blockchain, card: Card) : this(
blockchain = blockchain,
derivationPath = if (card.settings.isHDWalletAllowed) blockchain.derivationPath(card.derivationStyle)?.rawPath else null,
tokens = emptyList()
)
fun updateTokens(tokens: List<Token>): BlockchainNetwork {
return copy(
tokens = (this.tokens + tokens).distinct()
)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as BlockchainNetwork
if (blockchain != other.blockchain) return false
if (derivationPath != other.derivationPath) return false
return true
}
override fun hashCode(): Int {
var result = blockchain.hashCode()
result = 31 * result + (derivationPath?.hashCode() ?: 0)
return result
}
companion object {
fun fromWalletManager(walletManager: WalletManager): BlockchainNetwork {
return BlockchainNetwork(
walletManager.wallet.blockchain,
walletManager.wallet.publicKey.derivationPath?.rawPath,
walletManager.cardTokens.toList()
)
}
}
}

View file

@ -102,7 +102,7 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain {
fun Blockchain.toNetworkId(): String {
return when (this) {
Blockchain.Unknown -> "unknown"
Blockchain.Avalanche -> "avalaunch"
Blockchain.Avalanche -> "avalanche"
Blockchain.AvalancheTestnet -> "avalaunch"
Blockchain.Binance -> "binancecoin"
Blockchain.BinanceTestnet -> "binancecoin"
@ -123,7 +123,7 @@ fun Blockchain.toNetworkId(): String {
Blockchain.Litecoin -> "litecoin"
Blockchain.Polygon -> "matic-network"
Blockchain.PolygonTestnet -> "matic-networks"
Blockchain.RSK -> "rsk"
Blockchain.RSK -> "rootstock"
Blockchain.Stellar -> "stellar"
Blockchain.StellarTestnet -> "stellar"
Blockchain.Solana -> "solana"

View file

@ -36,7 +36,7 @@ internal class DemoOnboardingNoteMiddleware : DemoMiddleware {
val balanceAmount = config.getBalance(walletManager.wallet.blockchain)
val loadedBalance = noteState.walletBalance.copy(
value = balanceAmount.value!!,
currency = Currency.Blockchain(walletManager.wallet.blockchain),
currency = Currency.Blockchain(walletManager.wallet.blockchain, null),
state = ProgressState.Done,
error = null,
criticalError = null

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationParams
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.guard
import com.tangem.tap.common.extensions.dispatchOnMain
@ -10,14 +11,17 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.isMultiwalletAllowed
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.domain.walletconnect.BnbHelper
import com.tangem.tap.domain.walletconnect.WalletConnectManager
import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
import com.tangem.wallet.R
@ -164,14 +168,16 @@ class WalletConnectMiddleware {
chainId = chainId, peer = session.peerMeta
) ?: Blockchain.Ethereum
store.dispatch(GlobalAction.ScanCard(additionalBlockchainsToDerive = listOf(blockchain),
onSuccess = { scanResponse ->
handleScanResponse(scanResponse, session, blockchain)
},
onFailure = {
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
}, R.string.wallet_connect_scan_card_message
)
store.dispatch(
GlobalAction.ScanCard(
additionalBlockchainsToDerive = listOf(blockchain),
onSuccess = { scanResponse ->
handleScanResponse(scanResponse, session, blockchain)
},
onFailure = {
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
}, R.string.wallet_connect_scan_card_message
)
)
}
@ -229,22 +235,35 @@ class WalletConnectMiddleware {
}
return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) {
store.state.walletState.getWalletManager(blockchainToMake)
?: factory.makeWalletManagerForApp(scanResponse, blockchainToMake)
val derivationPath = blockchainToMake.derivationPath(card.derivationStyle)?.rawPath
store.state.walletState.getWalletManager(
Currency.Blockchain(blockchainToMake, derivationPath)
)
?: factory.makeWalletManagerForApp(
scanResponse,
blockchainToMake,
card.derivationStyle?.let { DerivationParams.Default(it) }
)
?.also { walletManager ->
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
BlockchainNetwork.fromWalletManager(walletManager), walletManager
)
)
}
} else {
val walletManager = factory.makeWalletManagerForApp(scanResponse, blockchainToMake)
if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains(
blockchainToMake
) != true
val walletManager = factory.makeWalletManagerForApp(
scanResponse,
blockchainToMake,
card.derivationStyle?.let { DerivationParams.Default(it) }
)
if (currenciesRepository.loadSavedCurrencies(card.cardId)
.find { it.blockchain == blockchainToMake } != null
) {
walletManager?.let {
currenciesRepository.saveAddedBlockchain(
currenciesRepository.saveUpdatedCurrency(
card.cardId,
blockchainToMake
BlockchainNetwork.fromWalletManager(walletManager)
)
}
}

View file

@ -41,7 +41,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber<HomeState
onShopButtonClick = { store.dispatch(HomeAction.GoToShop(getRegionProvider())) },
onSearchTokensClick = {
store.dispatch(TokensAction.AllowToAddTokens(false))
store.dispatch(TokensAction.LoadCurrencies)
store.dispatch(TokensAction.LoadCurrencies())
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
}
)

View file

@ -67,7 +67,9 @@ class OnboardingManager(
}
}
return balance.copy(currency = Currency.Blockchain(walletManager.wallet.blockchain))
return balance.copy(currency = Currency.Blockchain(
walletManager.wallet.blockchain, walletManager.wallet.publicKey.derivationPath?.rawPath)
)
}
fun activationStarted(cardId: String) {
@ -81,7 +83,7 @@ class OnboardingManager(
data class OnboardingWalletBalance(
val value: BigDecimal = BigDecimal.ZERO,
val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown),
val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown, null),
val hasIncomingTransaction: Boolean = false,
val state: ProgressState,
val error: TapError? = null,

View file

@ -112,7 +112,10 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading
val balanceIsLoading = noteState.walletBalance.copy(
currency = Currency.Blockchain(walletManager.wallet.blockchain),
currency = Currency.Blockchain(
walletManager.wallet.blockchain,
walletManager.wallet.publicKey.derivationPath?.rawPath
),
state = ProgressState.Loading,
error = null,
criticalError = null

View file

@ -214,7 +214,10 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
}
val isLoadedBefore = twinCardsState.walletBalance.state != ProgressState.Loading
val balanceIsLoading = twinCardsState.walletBalance.copy(
currency = Currency.Blockchain(walletManager.wallet.blockchain),
currency = Currency.Blockchain(
walletManager.wallet.blockchain,
walletManager.wallet.publicKey.derivationPath?.rawPath
),
state = ProgressState.Loading,
error = null,
criticalError = null

View file

@ -23,6 +23,7 @@ import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.extensions.minimalAmount
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.demo.DemoTransactionSender
import com.tangem.tap.features.demo.isDemoWallet
import com.tangem.tap.features.send.redux.*
@ -202,11 +203,11 @@ private fun sendTransaction(
}
scope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
dispatch(WalletAction.LoadWallet(walletManager.wallet.blockchain))
dispatch(WalletAction.LoadWallet(BlockchainNetwork.fromWalletManager(walletManager)))
}
delay(11000) // more than 10000 to avoid throttling
withContext(Dispatchers.Main) {
dispatch(WalletAction.LoadWallet(walletManager.wallet.blockchain))
dispatch(WalletAction.LoadWallet(BlockchainNetwork.fromWalletManager(walletManager)))
}
}
}

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.tokens.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.features.wallet.redux.WalletData
import org.rekotlin.Action
@ -12,15 +12,17 @@ sealed class TokensAction : Action {
data class AllowToAddTokens(val allow: Boolean) : TokensAction()
object LoadCurrencies : TokensAction() {
data class LoadCurrencies(val supportedBlockchains: List<Blockchain>? = null) : TokensAction() {
data class Success(val currencies: List<Currency>) : TokensAction()
}
data class SetAddedCurrencies(val wallets: List<WalletData>) : TokensAction()
data class SetAddedCurrencies(
val wallets: List<WalletData>, val derivationStyle: DerivationStyle?
) : TokensAction()
data class SetNonRemovableCurrencies(val wallets: List<WalletData>) : TokensAction()
data class SaveChanges(
val addedTokens: List<Token>,
val addedTokens: List<TokenWithBlockchain>,
val addedBlockchains: List<Blockchain>
) : TokensAction()
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.tokens.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationParams
import com.tangem.blockchain.common.Token
import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
@ -12,14 +13,16 @@ import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.scope
import com.tangem.tap.store
@ -34,7 +37,7 @@ class TokensMiddleware {
{ next ->
{ action ->
when (action) {
is TokensAction.LoadCurrencies -> handleLoadCurrencies()
is TokensAction.LoadCurrencies -> handleLoadCurrencies(action)
is TokensAction.SaveChanges -> handleSaveChanges(action)
}
next(action)
@ -42,11 +45,12 @@ class TokensMiddleware {
}
}
private fun handleLoadCurrencies() {
private fun handleLoadCurrencies(action: TokensAction.LoadCurrencies) {
val scanResponse = store.state.globalState.scanResponse
val isTestcard = scanResponse?.card?.isTestCard ?: false
val currencies = currenciesRepository.getSupportedTokens(isTestcard)
.filter(action.supportedBlockchains?.toSet())
store.dispatch(TokensAction.LoadCurrencies.Success(currencies))
}
@ -55,13 +59,15 @@ class TokensMiddleware {
val scanResponse = store.state.globalState.scanResponse ?: return
val currentTokens = store.state.tokensState.addedWallets.toTokens()
val currentBlockchains = store.state.tokensState.addedWallets.toBlockchains()
val currentBlockchains = store.state.tokensState.addedWallets.toBlockchains(
store.state.tokensState.derivationStyle
)
val blockchainsToAdd = action.addedBlockchains.filter { !currentBlockchains.contains(it) }
val blockchainsToRemove = currentBlockchains.filter { !action.addedBlockchains.contains(it) }
val tokensToAdd = action.addedTokens.filter { !currentTokens.contains(it) }
val tokensToRemove = currentTokens.filter { !action.addedTokens.contains(it) }
val tokensToAdd = action.addedTokens.filter { !currentTokens.contains(it.token) }
val tokensToRemove = currentTokens.filter { token -> !action.addedTokens.any { it.token == token } }
removeCurrenciesIfNeeded(blockchainsToRemove, tokensToRemove)
@ -73,7 +79,7 @@ class TokensMiddleware {
if (scanResponse.supportsHdWallet()) {
deriveMissingBlockchains(scanResponse, blockchainsToAdd, tokensToAdd)
} else {
submitAdd(blockchainsToAdd, tokensToAdd)
submitAdd(blockchainsToAdd, tokensToAdd, scanResponse)
store.dispatch(NavigationAction.PopBackTo())
}
}
@ -81,7 +87,7 @@ class TokensMiddleware {
private fun deriveMissingBlockchains(
scanResponse: ScanResponse,
blockchains: List<Blockchain>,
tokens: List<Token>
tokens: List<TokenWithBlockchain>
) {
val derivationDataList = listOfNotNull(
getDerivations(EllipticCurve.Secp256k1, scanResponse, blockchains, tokens),
@ -112,7 +118,7 @@ class TokensMiddleware {
derivedKeys = updatedDerivedKeys
)
store.dispatchOnMain(GlobalAction.SaveScanNoteResponse(updatedScanResponse))
submitAdd(blockchains, tokens)
submitAdd(blockchains, tokens, scanResponse)
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatchOnMain(NavigationAction.PopBackTo())
@ -128,13 +134,12 @@ class TokensMiddleware {
curve: EllipticCurve,
scanResponse: ScanResponse,
blockchains: List<Blockchain>,
tokens: List<Token>
tokens: List<TokenWithBlockchain>
): DerivationData? {
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val tokenBlockchains = tokens.map { it.blockchain }
val derivationPathsCandidates = (blockchains + tokenBlockchains).distinct()
.mapNotNull { it.derivationPath() }
val derivationPathsCandidates = (blockchains + tokens.map { it.blockchain }).distinct()
.mapNotNull { it.derivationPath(scanResponse.card.derivationStyle) }
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
val alreadyDerivedKeys: ExtendedPublicKeysMap =
@ -155,11 +160,20 @@ class TokensMiddleware {
val mapKeyOfWalletPublicKey: ByteArrayKey
)
private fun submitAdd(blockchains: List<Blockchain>, tokens: List<Token>) {
(blockchains.map {
WalletAction.MultiWallet.AddBlockchain(it)
private fun submitAdd(
blockchains: List<Blockchain>, tokens: List<TokenWithBlockchain>, scanResponse: ScanResponse,
) {
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
(blockchains.mapNotNull {
val walletManager = factory.makeWalletManagerForApp(
scanResponse, it,
scanResponse.card.derivationStyle?.let { DerivationParams.Default(it) }
) ?: return@mapNotNull null
WalletAction.MultiWallet.AddBlockchain(BlockchainNetwork.fromWalletManager(walletManager), walletManager)
} + tokens.map {
WalletAction.MultiWallet.AddToken(it)
val blockchainNetwork = BlockchainNetwork(it.blockchain, scanResponse.card)
WalletAction.MultiWallet.AddToken(it.token, blockchainNetwork)
}).forEach { store.dispatchOnMain(it) }
}

View file

@ -19,15 +19,17 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
tokensState.copy(currencies = action.currencies)
}
is TokensAction.SetAddedCurrencies -> {
tokensState.copy(
addedBlockchains = action.wallets.toBlockchains(),
addedTokens = action.wallets.toTokens(),
addedBlockchains = action.wallets.toBlockchains(action.derivationStyle),
addedTokens = action.wallets.toTokensWithBlockchains(action.derivationStyle),
addedWallets = action.wallets,
derivationStyle = action.derivationStyle
)
}
is TokensAction.SetNonRemovableCurrencies -> {
tokensState.copy(
nonRemovableBlockchains = action.wallets.toBlockchains(),
nonRemovableBlockchains = action.wallets.toBlockchains(tokensState.derivationStyle),
nonRemovableTokens = action.wallets.toTokensContractAddresses(),
)
}

View file

@ -1,19 +1,22 @@
package com.tangem.tap.features.tokens.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.domain.tokens.fromNetworkId
import com.tangem.tap.features.wallet.redux.WalletData
import org.rekotlin.StateType
data class TokensState(
val addedWallets: List<WalletData> = emptyList(),
val addedTokens: List<Token> = emptyList(),
val addedTokens: List<TokenWithBlockchain> = emptyList(),
val addedBlockchains: List<Blockchain> = emptyList(),
val nonRemovableTokens: List<ContractAddress> = emptyList(),
val nonRemovableBlockchains: List<Blockchain> = emptyList(),
val currencies: List<Currency> = emptyList(),
val allowToAdd: Boolean = true
val allowToAdd: Boolean = true,
val derivationStyle: DerivationStyle? = null
) : StateType
typealias ContractAddress = String
@ -26,6 +29,38 @@ fun List<WalletData>.toTokens(): List<Token> {
return mapNotNull { (it.currency as? com.tangem.tap.features.wallet.redux.Currency.Token)?.token }.distinct()
}
fun List<WalletData>.toBlockchains(): List<Blockchain> {
return mapNotNull { (it.currency as? com.tangem.tap.features.wallet.redux.Currency.Blockchain)?.blockchain }.distinct()
fun List<WalletData>.toTokensWithBlockchains(derivationStyle: DerivationStyle?): List<TokenWithBlockchain> {
return mapNotNull {
if (it.currency !is com.tangem.tap.features.wallet.redux.Currency.Token) return@mapNotNull null
if (it.currency.isCustomCurrency(derivationStyle)) return@mapNotNull null
TokenWithBlockchain(it.currency.token, it.currency.blockchain)
}.distinct()
}
fun List<WalletData>.toBlockchains(derivationStyle: DerivationStyle?): List<Blockchain> {
return mapNotNull {
if (it.currency.isCustomCurrency(derivationStyle)) {
null
} else {
(it.currency as? com.tangem.tap.features.wallet.redux.Currency.Blockchain)?.blockchain
}
}.distinct()
}
data class TokenWithBlockchain(
val token: Token,
val blockchain: Blockchain
)
fun List<Currency>.filter(supportedBlockchains: Set<Blockchain>?): List<Currency> {
if (supportedBlockchains == null) return this
return map {
it.copy(contracts =
it.contracts?.filter {
supportedBlockchains.contains(it.blockchain) && it.blockchain.canHandleTokens()
}
)
}.filterNot {
it.contracts.isNullOrEmpty() && !supportedBlockchains.contains(Blockchain.fromNetworkId(it.id))
}
}

View file

@ -16,17 +16,17 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.SubcomposeAsyncImage
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.common.extensions.getGreyedOutIconRes
import com.tangem.tap.common.extensions.getRoundIconRes
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.domain.tokens.fromNetworkId
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
import com.tangem.wallet.R
@Composable
fun CollapsedCurrencyItem(
currency: Currency,
addedTokens: List<Token>,
addedTokens: List<TokenWithBlockchain>,
addedBlockchains: List<Blockchain>,
onCurrencyClick: (String) -> Unit
) {
@ -69,7 +69,7 @@ fun CollapsedCurrencyItem(
if (currency.contracts != null) {
currency.contracts.map { contract ->
val added =
addedTokens.map { it.contractAddress }.contains(contract.address)
addedTokens.map { it.token.contractAddress }.contains(contract.address)
val icon = if (added) {
contract.blockchain.getRoundIconRes()
} else {

View file

@ -12,12 +12,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.common.compose.Keyboard
import com.tangem.tap.common.compose.keyboardAsState
import com.tangem.tap.common.extensions.pixelsToDp
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.domain.tokens.fromNetworkId
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
import com.tangem.tap.features.tokens.redux.TokensState
import com.tangem.tap.store
import com.tangem.wallet.R
@ -26,7 +26,7 @@ import com.tangem.wallet.R
fun CurrenciesScreen(
tokensState: MutableState<TokensState> = mutableStateOf(store.state.tokensState),
searchInput: MutableState<String>,
onSaveChanges: (List<Token>, List<Blockchain>) -> Unit
onSaveChanges: (List<TokenWithBlockchain>, List<Blockchain>) -> Unit
) {
val addedTokensState = remember { mutableStateOf(tokensState.value.addedTokens) }
@ -34,13 +34,13 @@ fun CurrenciesScreen(
val isKeyboardOpen by keyboardAsState()
val onAddCurrencyToggleClick = { currency: Currency, contract: Token? ->
if (contract != null) {
val onAddCurrencyToggleClick = { currency: Currency, token: TokenWithBlockchain? ->
if (token != null) {
val mutableList = addedTokensState.value.toMutableList()
if (mutableList.contains(contract)) {
mutableList.remove(contract)
if (mutableList.contains(token)) {
mutableList.remove(token)
} else {
mutableList.add(contract)
mutableList.add(token)
}
addedTokensState.value = mutableList
} else {

View file

@ -2,21 +2,21 @@ package com.tangem.tap.features.tokens.ui.compose
import androidx.compose.runtime.Composable
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.features.tokens.redux.ContractAddress
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
@Composable
fun CurrencyItem(
currency: Currency,
nonRemovableTokens: List<ContractAddress>,
nonRemovableBlockchains: List<Blockchain>,
addedTokens: List<Token>,
addedTokens: List<TokenWithBlockchain>,
addedBlockchains: List<Blockchain>,
allowToAdd: Boolean,
expanded: Boolean,
onCurrencyClick: (String) -> Unit,
onAddCurrencyToggled: (Currency, Token?) -> Unit
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit
) {
if (expanded) {
ExpandedCurrencyItem(

View file

@ -18,11 +18,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.SubcomposeAsyncImage
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.common.extensions.getRoundIconRes
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.domain.tokens.fromNetworkId
import com.tangem.tap.features.tokens.redux.ContractAddress
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
import com.tangem.wallet.R
@Composable
@ -30,11 +30,11 @@ fun ExpandedCurrencyItem(
currency: Currency,
nonRemovableTokens: List<ContractAddress>,
nonRemovableBlockchains: List<Blockchain>,
addedTokens: List<Token>,
addedTokens: List<TokenWithBlockchain>,
addedBlockchains: List<Blockchain>,
allowToAdd: Boolean,
onCurrencyClick: (String) -> Unit,
onAddCurrencyToggled: (Currency, Token?) -> Unit
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit
) {
val blockchain = Blockchain.fromNetworkId(currency.id)
val iconRes = if (blockchain == Blockchain.Unknown) {
@ -131,7 +131,7 @@ fun ExpandedCurrencyItem(
blockchains.map { blockchain ->
val contract = currency.contracts?.firstOrNull { it.blockchain == blockchain }
val added = if (contract != null) {
addedTokens.map { it.contractAddress }.contains(contract.address)
addedTokens.map { it.token.contractAddress }.contains(contract.address)
} else {
addedBlockchains.contains(blockchain)
}

View file

@ -8,9 +8,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.features.tokens.redux.ContractAddress
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
@Composable
@ -18,11 +18,11 @@ fun ListOfCurrencies(
currencies: List<Currency>,
nonRemovableTokens: List<ContractAddress>,
nonRemovableBlockchains: List<Blockchain>,
addedTokens: List<Token>,
addedTokens: List<TokenWithBlockchain>,
addedBlockchains: List<Blockchain>,
searchInput: String,
allowToAdd: Boolean,
onAddCurrencyToggled: (Currency, Token?) -> Unit
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit
) {
val expandedCurrencies = remember { mutableStateOf(listOf("")) }

View file

@ -21,13 +21,14 @@ import com.tangem.tap.common.extensions.getNetworkName
import com.tangem.tap.common.extensions.getRoundIconRes
import com.tangem.tap.domain.tokens.Contract
import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
@Composable
fun NetworkItem(
currency: Currency, contract: Contract?,
blockchain: Blockchain, allowToAdd: Boolean,
added: Boolean, canBeRemoved: Boolean,
onAddCurrencyToggled: (Currency, Token?) -> Unit
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit
) {
Row(
modifier = Modifier
@ -67,15 +68,16 @@ fun NetworkItem(
val token = if (contract != null) {
Token(
name = currency.name, symbol = currency.symbol, contractAddress = contract.address,
decimals = contract.decimalCount, blockchain = contract.blockchain
decimals = contract.decimalCount,
)
} else {
null
}
val tokenWithBlockchain = token?.let { TokenWithBlockchain(token, contract!!.blockchain) }
if (allowToAdd) Switch(
checked = added,
enabled = canBeRemoved,
onCheckedChange = { onAddCurrencyToggled(currency, token) },
onCheckedChange = { onAddCurrencyToggled(currency, tokenWithBlockchain) },
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
colors = SwitchDefaults.colors(
checkedThumbColor = Color(0xFF1ACE80)

View file

@ -8,7 +8,7 @@ import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.tokens.CardCurrencies
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.wallet.R
import org.rekotlin.Action
import java.math.BigDecimal
@ -24,9 +24,14 @@ sealed class WalletAction : Action {
}
data class LoadWallet(val blockchain: Blockchain? = null) : WalletAction() {
data class Success(val wallet: Wallet) : WalletAction()
data class NoAccount(val wallet: Wallet, val amountToCreateAccount: String) : WalletAction()
data class LoadWallet(val blockchain: BlockchainNetwork? = null) : WalletAction() {
data class Success(val wallet: Wallet, val blockchain: BlockchainNetwork) : WalletAction()
data class NoAccount(
val wallet: Wallet,
val blockchain: BlockchainNetwork,
val amountToCreateAccount: String
) : WalletAction()
data class Failure(val wallet: Wallet, val errorMessage: String? = null) : WalletAction()
}
@ -35,19 +40,30 @@ sealed class WalletAction : Action {
sealed class MultiWallet : WalletAction() {
data class SetIsMultiwalletAllowed(val isMultiwalletAllowed: Boolean) : MultiWallet()
data class AddWalletManagers(val walletManagers: List<WalletManager>) : MultiWallet() {
constructor(walletManager: WalletManager) : this(listOf(walletManager))
}
data class AddBlockchain(val blockchain: Blockchain) : MultiWallet()
data class AddBlockchains(val blockchains: List<Blockchain>) : MultiWallet()
data class AddTokens(val tokens: List<Token>) : MultiWallet()
data class AddToken(val token: Token) : MultiWallet()
data class SaveCurrencies(val cardCurrencies: CardCurrencies) : MultiWallet()
data class AddBlockchain(
val blockchain: BlockchainNetwork,
val walletManager: WalletManager?
) : MultiWallet()
data class AddBlockchains(
val blockchains: List<BlockchainNetwork>, val walletManagers: List<WalletManager>
) : MultiWallet()
data class AddTokens(val tokens: List<Token>, val blockchain: BlockchainNetwork) :
MultiWallet()
data class AddToken(val token: Token, val blockchain: BlockchainNetwork) : MultiWallet()
data class SaveCurrencies(val blockchainNetworks: List<BlockchainNetwork>) : MultiWallet()
object FindTokensInUse : MultiWallet()
object FindBlockchainsInUse : MultiWallet()
data class TokenLoaded(val amount: Amount, val token: Token) : MultiWallet()
data class TokenLoaded(
val amount: Amount,
val token: Token,
val blockchain: BlockchainNetwork
) : MultiWallet()
data class SelectWallet(val walletData: WalletData?) : MultiWallet()
data class RemoveWallet(val walletData: WalletData) : MultiWallet()
data class SetPrimaryBlockchain(val blockchain: Blockchain) : MultiWallet()
@ -135,5 +151,9 @@ sealed class WalletAction : Action {
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
data class SetWalletRent(val blockchain: Blockchain, val minRent: String, val rentExempt: String) : WalletAction()
data class SetWalletRent(
val blockchain: BlockchainNetwork,
val minRent: String,
val rentExempt: String
) : WalletAction()
}

View file

@ -10,10 +10,12 @@ import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.toggleWidget.WidgetState
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.extensions.buyIsAllowed
import com.tangem.tap.domain.extensions.sellIsAllowed
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.toPendingTransactions
@ -33,8 +35,7 @@ data class WalletState(
val hashesCountVerified: Boolean? = null,
val walletDialog: StateDialog? = null,
val mainWarningsList: List<WarningMessage> = mutableListOf(),
val walletsData: List<WalletData> = emptyList(),
val walletManagers: List<WalletManager> = emptyList(),
val wallets: List<WalletStore> = listOf(),
val isMultiwalletAllowed: Boolean = false,
val cardCurrency: CryptoCurrencyName? = null,
val selectedCurrency: Currency? = null,
@ -52,45 +53,96 @@ data class WalletState(
val isTangemTwins: Boolean
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
val primaryWallet = if (walletsData.isNotEmpty()) walletsData[0] else null
val primaryWallet: WalletData? = if (wallets.isNotEmpty()) wallets[0].walletsData[0] else null
val primaryWalletManager: WalletManager? =
if (wallets.isNotEmpty()) wallets[0].walletManager else null
val shouldShowDetails: Boolean =
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
val blockchains: List<Blockchain>
get() = walletManagers.map { it.wallet.blockchain }
get() = wallets.mapNotNull { it.walletManager?.wallet?.blockchain }
val currencies: List<Currency>
get() = walletsData.mapNotNull { it.currency }
get() = wallets.flatMap { it.walletsData }.map { it.currency }
val walletsData: List<WalletData>
get() = wallets.flatMap { it.walletsData }
val walletManagers: List<WalletManager>
get() = wallets.mapNotNull { it.walletManager }
fun getWalletManager(token: Token?): WalletManager? {
if (token == null) return null
return walletManagers.find { it.wallet.blockchain == token.blockchain }
return wallets
.mapNotNull { it.walletManager }
.find { walletManager ->
walletManager.cardTokens.any { it.contractAddress == token.contractAddress }
}
}
fun getWalletManager(currency: Currency?): WalletManager? {
if (currency?.blockchain == null) return null
return walletManagers.find { it.wallet.blockchain == currency.blockchain }
return wallets.map { it.walletManager }
.find { it?.wallet?.blockchain == currency.blockchain }
}
fun getWalletManager(blockchain: Blockchain): WalletManager? {
return walletManagers.find { it.wallet.blockchain == blockchain }
fun getWalletManager(blockchain: BlockchainNetwork): WalletManager? {
return wallets.find { it.blockchainNetwork == blockchain }?.walletManager
}
fun getWalletData(blockchain: BlockchainNetwork?): WalletData? {
if (blockchain == null) return null
return walletsData.find {
it.currency is Currency.Blockchain &&
it.currency.blockchain == blockchain.blockchain &&
it.currency.derivationPath == blockchain.derivationPath
}
}
fun getWalletStore(currency: Currency?): WalletStore? {
if (currency == null) return null
return wallets.firstOrNull {
it.blockchainNetwork.derivationPath == currency.derivationPath &&
(it.blockchainNetwork.blockchain == currency.blockchain)
}
}
fun getWalletStore(wallet: Wallet?): WalletStore? {
if (wallet == null) return null
val currency =
Currency.Blockchain(wallet.blockchain, wallet.publicKey.derivationPath?.rawPath)
return getWalletStore(currency)
}
fun getWalletStore(blockchainNetwork: BlockchainNetwork?): WalletStore? {
if (blockchainNetwork == null) return null
return wallets.firstOrNull {
it.blockchainNetwork.derivationPath == blockchainNetwork.derivationPath &&
(it.blockchainNetwork.blockchain == blockchainNetwork.blockchain)
}
}
fun getWalletData(currency: Currency?): WalletData? {
if (currency == null) return null
return walletsData.find { it.currency == currency }
}
fun getWalletData(blockchain: Blockchain?): WalletData? {
if (blockchain == null) return null
return walletsData.find { (it.currency as? Currency.Blockchain)?.blockchain == blockchain }
return getWalletStore(currency)?.walletsData?.firstOrNull { it.currency == currency }
}
fun getWalletData(token: Token?): WalletData? {
if (token == null) return null
return walletsData.find { (it.currency as? Currency.Token)?.token == token }
return walletsData.find {
(it.currency as? Currency.Token)?.token == token &&
!it.currency.isCustomCurrency(store.state.globalState.scanResponse!!.card.derivationStyle)
}
}
fun getWalletData(blockchain: Blockchain?): WalletData? {
if (blockchain == null) return null
return walletsData.find {
(it.currency as? Currency.Blockchain)?.blockchain == blockchain &&
!it.currency.isCustomCurrency(store.state.globalState.scanResponse!!.card.derivationStyle)
}
}
fun getSelectedWalletData(): WalletData? {
@ -114,36 +166,90 @@ data class WalletState(
if (walletData.currency is Currency.Blockchain) {
return wallet.recentTransactions.toPendingTransactions(wallet.address).isEmpty() &&
wallet.amounts.toSendableAmounts().isEmpty()
wallet.amounts.toSendableAmounts().isEmpty()
} else if (walletData.currency is Currency.Token) (
return wallet.recentTransactions.toPendingTransactionsForToken(
walletData.currency.token, wallet.address).isEmpty()
&& wallet.amounts[AmountType.Token(token = walletData.currency.token)]
?.isAboveZero() != true
)
return wallet.recentTransactions.toPendingTransactionsForToken(
walletData.currency.token, wallet.address
).isEmpty()
&& wallet.amounts[AmountType.Token(token = walletData.currency.token)]
?.isAboveZero() != true
)
}
return false
}
private fun isPrimaryCurrency(walletData: WalletData): Boolean {
return (walletData.currency is Currency.Blockchain &&
walletData.currency.blockchain == store.state.walletState.primaryBlockchain)
|| (walletData.currency is Currency.Token &&
walletData.currency.token == store.state.walletState.primaryToken)
walletData.currency.blockchain == store.state.walletState.primaryBlockchain)
|| (walletData.currency is Currency.Token &&
walletData.currency.token == store.state.walletState.primaryToken)
}
fun replaceWalletInWallets(walletData: WalletData?): List<WalletData> {
if (walletData == null) return walletsData
fun replaceWalletInWallets(wallet: WalletStore?): List<WalletStore> {
if (wallet == null) return wallets
var changed = false
val updatedWallets = walletsData.map {
if (it.currency == walletData.currency) {
val updatedWallets = wallets.map {
if (it.blockchainNetwork == wallet.blockchainNetwork) {
changed = true
walletData
wallet
} else {
it
}
}
return if (changed) updatedWallets else walletsData + walletData
return if (changed) updatedWallets else wallets + wallet
}
fun updateWalletData(walletData: WalletData?): WalletState {
if (walletData == null) return this
return updateWalletsData(listOf(walletData))
}
fun updateWalletsData(
walletsData: List<WalletData>
): WalletState {
val walletStores = walletsData
.map { BlockchainNetwork(it.currency.blockchain, it.currency.derivationPath, emptyList()) }
.distinct().map { getWalletStore(it) }.mapNotNull { it?.updateWallets(walletsData) }
return updateWalletStores(walletStores)
}
fun updateWalletStore(walletStore: WalletStore?): WalletState {
return copy(wallets = replaceWalletInWallets(walletStore))
}
fun updateWalletStores(walletStores: List<WalletStore>): WalletState {
val walletStores = walletStores.toMutableList()
val updatedWallets = wallets.map { oldWalletStore ->
val walletStore = walletStores.find { it.blockchainNetwork == oldWalletStore.blockchainNetwork }
if (walletStore != null) {
walletStores.remove(walletStore)
walletStore
} else {
oldWalletStore
}
}
return copy(wallets = updatedWallets + walletStores)
}
fun removeWallet(walletData: WalletData?): WalletState {
if (walletData == null) return this
return if (walletData.currency is Currency.Blockchain) {
val walletStores = wallets.filterNot {
it.blockchainNetwork.blockchain == walletData.currency.blockchain
&& it.blockchainNetwork.derivationPath == walletData.currency.derivationPath
}
copy(wallets = walletStores)
} else {
val walletStore = getWalletStore(walletData.currency)
val walletDataList = walletStore?.walletsData
?.filterNot { it.currency == walletData.currency }
?: emptyList()
val updatedWalletStore = walletStore?.copy(walletsData = walletDataList)
updateWalletStore(updatedWalletStore)
}
}
fun replaceSomeWallets(newWallets: List<WalletData>): List<WalletData> {
@ -165,20 +271,26 @@ data class WalletState(
exchangeManager: CurrencyExchangeManager?,
walletData: WalletData
): WalletData {
return walletData.copy(tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData))
return walletData.copy(
tradeCryptoState = TradeCryptoState.from(
exchangeManager,
walletData
)
)
}
fun updateTradeCryptoState(
exchangeManager: CurrencyExchangeManager?,
walletDataList: List<WalletData>
): List<WalletData> {
return walletDataList.map { it.copy(tradeCryptoState = TradeCryptoState.from(exchangeManager, it)) }
}
fun addWalletManagers(newWalletManagers: List<WalletManager>): WalletState {
val updatedWalletManagers = this.walletManagers +
newWalletManagers.filterNot { this.blockchains.contains(it.wallet.blockchain) }
return copy(walletManagers = updatedWalletManagers)
return walletDataList.map {
it.copy(
tradeCryptoState = TradeCryptoState.from(
exchangeManager,
it
)
)
}
}
}
@ -224,7 +336,8 @@ data class Artwork(
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
const val TEMP_CARDANO = "https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659"
const val TEMP_CARDANO =
"https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659"
}
}
@ -233,7 +346,10 @@ data class TradeCryptoState(
val buyingAllowed: Boolean = false,
) {
companion object {
fun from(exchangeManager: CurrencyExchangeManager?, walletData: WalletData): TradeCryptoState {
fun from(
exchangeManager: CurrencyExchangeManager?,
walletData: WalletData
): TradeCryptoState {
val status = exchangeManager ?: return walletData.tradeCryptoState
val currency = walletData.currency
@ -267,7 +383,9 @@ data class WalletData(
fun shouldEnableTokenSendButton(): Boolean = !blockchainAmountIsEmpty() || !tokenAmountIsEmpty()
private fun blockchainAmountIsEmpty(): Boolean = currencyData.blockchainAmount?.isZero() ?: false
private fun blockchainAmountIsEmpty(): Boolean =
currencyData.blockchainAmount?.isZero() ?: false
private fun tokenAmountIsEmpty(): Boolean = currencyData.amount?.isZero() == true
}
@ -280,17 +398,83 @@ sealed interface Currency {
val blockchain: com.tangem.blockchain.common.Blockchain
val currencySymbol: CryptoCurrencyName
val derivationPath: String?
data class Token(
val token: com.tangem.blockchain.common.Token
val token: com.tangem.blockchain.common.Token,
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
) : Currency {
override val blockchain = token.blockchain
override val currencySymbol: CryptoCurrencyName = token.symbol
override val currencySymbol = token.symbol
}
data class Blockchain(
override val blockchain: com.tangem.blockchain.common.Blockchain
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
) : Currency {
override val currencySymbol: CryptoCurrencyName = blockchain.currency
}
fun isCustomCurrency(derivationStyle: DerivationStyle?): Boolean {
if (derivationPath == null || derivationStyle == null) return false
return derivationPath != blockchain.derivationPath(derivationStyle)?.rawPath
}
companion object {
fun fromBlockchainNetwork(
blockchainNetwork: BlockchainNetwork,
token: com.tangem.blockchain.common.Token? = null
): Currency {
return if (token != null) {
Currency.Token(
token = token,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
)
} else {
Currency.Blockchain(
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
)
}
}
}
}
data class WalletStore(
val walletManager: WalletManager?,
val blockchainNetwork: BlockchainNetwork,
val walletsData: List<WalletData>
) {
fun updateWallets(walletDataList: List<WalletData>): WalletStore {
val relevantWalletDataList = walletDataList.filter {
it.currency.blockchain == blockchainNetwork.blockchain &&
it.currency.derivationPath == blockchainNetwork.derivationPath
}.toMutableList()
val updatedWalletDataList = walletsData.map { walletData ->
val matchingWalletData = relevantWalletDataList.find { it.currency == walletData.currency }
if (matchingWalletData != null) relevantWalletDataList.remove(matchingWalletData)
matchingWalletData ?: walletData
}
return copy(walletsData = updatedWalletDataList + relevantWalletDataList)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as WalletStore
if (walletManager != other.walletManager) return false
if (blockchainNetwork != other.blockchainNetwork) return false
return true
}
override fun hashCode(): Int {
var result = walletManager?.hashCode() ?: 0
result = 31 * result + blockchainNetwork.hashCode()
return result
}
}

View file

@ -10,6 +10,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.redux.Currency
@ -31,11 +32,8 @@ class MultiWalletMiddleware {
val tapWalletManager = globalState.tapWalletManager
when (action) {
is WalletAction.MultiWallet.AddWalletManagers -> {
globalState.feedbackManager?.infoHolder?.setWalletsInfo(action.walletManagers)
if (globalState.scanResponse?.isDemoCard() == true) {
addDummyBalances(action.walletManagers)
}
is WalletAction.MultiWallet.AddBlockchains -> {
handleAddingWalletManagers(globalState, action.walletManagers)
}
is WalletAction.MultiWallet.SelectWallet -> {
if (action.walletData != null) {
@ -43,33 +41,39 @@ class MultiWalletMiddleware {
}
}
is WalletAction.MultiWallet.AddToken -> {
globalState.scanResponse?.card?.cardId?.let {
currenciesRepository.saveAddedToken(it, action.token)
}
addTokens(listOf(action.token), walletState, globalState)
addTokens(listOf(action.token), action.blockchain, walletState, globalState)
}
is WalletAction.MultiWallet.AddTokens -> {
addTokens(action.tokens, walletState, globalState)
addTokens(action.tokens, action.blockchain, walletState, globalState)
}
is WalletAction.MultiWallet.AddBlockchain -> {
globalState.scanResponse?.let {
currenciesRepository.saveAddedBlockchain(it.card.cardId, action.blockchain)
if (walletState?.blockchains?.contains(action.blockchain) != true) {
tapWalletManager.walletManagerFactory
.makeWalletManagerForApp(it, action.blockchain)?.let { walletManager ->
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
}
}
action.walletManager?.let {
handleAddingWalletManagers(globalState, listOf(action.walletManager))
}
store.dispatch(WalletAction.LoadFiatRate(
currencyList = listOf(Currency.Blockchain(action.blockchain)))
globalState.scanResponse?.let {
currenciesRepository.saveUpdatedCurrency(
cardId = it.card.cardId,
blockchainNetwork = action.blockchain
)
}
store.dispatch(
WalletAction.LoadFiatRate(
currencyList = listOf(
Currency.Blockchain(
action.blockchain.blockchain,
action.blockchain.derivationPath
)
)
)
)
store.dispatch(WalletAction.LoadWallet(action.blockchain)
store.dispatch(
WalletAction.LoadWallet(action.blockchain)
)
}
is WalletAction.MultiWallet.SaveCurrencies -> {
globalState.scanResponse?.card?.cardId?.let {
currenciesRepository.saveCardCurrencies(it, action.cardCurrencies)
currenciesRepository.saveCurrencies(it, action.blockchainNetworks)
}
}
is WalletAction.MultiWallet.RemoveWallet -> {
@ -77,13 +81,27 @@ class MultiWalletMiddleware {
when (val currency = action.walletData.currency) {
is Currency.Blockchain -> {
cardId?.let {
currenciesRepository.removeBlockchain(it, currency.blockchain)
currenciesRepository.removeBlockchain(
cardId = it,
blockchainNetwork = BlockchainNetwork(
currency.blockchain, currency.derivationPath,
emptyList()
)
)
}
}
is Currency.Token -> {
walletState?.getWalletManager(currency.token)
?.removeToken(currency.token)
cardId?.let { currenciesRepository.removeToken(it, currency.token) }
val walletManager = walletState?.getWalletManager(currency.token)
if (walletManager != null) {
walletManager.removeToken(currency.token)
cardId?.let {
currenciesRepository.removeToken(
cardId = it,
token = currency.token,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
)
}
}
}
}
}
@ -94,8 +112,12 @@ class MultiWalletMiddleware {
val cardFirmware = scanResponse.card.firmwareVersion
val blockchains = currenciesRepository.getBlockchains(cardFirmware)
.filterNot { walletState?.blockchains?.contains(it) == true }
.map { BlockchainNetwork(it, null, emptyList()) }
val walletManagers =
tapWalletManager.walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains)
tapWalletManager.walletManagerFactory.makeWalletManagersForApp(
scanResponse,
blockchains
)
scope.launch {
walletManagers.map { walletManager ->
@ -105,18 +127,23 @@ class MultiWalletMiddleware {
val coinAmount = wallet.amounts[AmountType.Coin]?.value
if (coinAmount != null && !coinAmount.isZero()) {
scope.launch(Dispatchers.Main) {
if (walletState?.getWalletData(wallet.blockchain) == null) {
store.dispatch(
WalletAction.MultiWallet.AddWalletManagers(
listOfNotNull(walletManager)
)
)
val blockchainNetwork = BlockchainNetwork(
wallet.blockchain,
null,
walletManager.cardTokens.toList()
)
if (walletState?.getWalletData(blockchainNetwork) == null) {
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
wallet.blockchain
blockchainNetwork, walletManager
)
)
store.dispatch(
WalletAction.LoadWallet.Success(
wallet = wallet,
blockchain = blockchainNetwork
)
)
store.dispatch(WalletAction.LoadWallet.Success(wallet))
}
}
}
@ -130,30 +157,42 @@ class MultiWalletMiddleware {
val walletFactory = tapWalletManager.walletManagerFactory
val card = scanResponse.card
val walletManager = walletState?.getWalletManager(Blockchain.Ethereum)
?: walletFactory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum)
val walletManager = walletState?.getWalletManager(
Currency.Blockchain(Blockchain.Ethereum, null)
)
?: walletFactory.makeWalletManagerForApp(
scanResponse,
Currency.Blockchain(Blockchain.Ethereum, null)
)
val tokenFinder = walletManager as? TokenFinder ?: return
scope.launch {
val result = tokenFinder.findTokens()
withContext(Dispatchers.Main) {
when (result) {
is Result.Success -> {
if (result.data.isNotEmpty()) {
currenciesRepository.saveAddedTokens(card.cardId, result.data)
val blockchainNetwork = BlockchainNetwork(
walletManager.wallet.blockchain,
walletManager.wallet.publicKey.derivationPath?.rawPath,
walletManager.cardTokens.toList()
)
currenciesRepository.saveUpdatedCurrency(
card.cardId,
blockchainNetwork
)
store.dispatch(
WalletAction.MultiWallet.AddWalletManagers(
WalletAction.MultiWallet.AddBlockchain(
blockchainNetwork,
walletManager
)
)
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
walletManager.wallet.blockchain
)
)
store.dispatch(
WalletAction.MultiWallet.AddTokens(
walletManager.cardTokens.toList()
walletManager.cardTokens.toList(),
blockchainNetwork
)
)
}
@ -173,38 +212,60 @@ class MultiWalletMiddleware {
}
}
private fun addTokens(tokens: List<Token>, walletState: WalletState?, globalState: GlobalState?) {
private fun handleAddingWalletManagers(
globalState: GlobalState,
walletManagers: List<WalletManager>
) {
globalState.feedbackManager?.infoHolder?.setWalletsInfo(walletManagers)
if (globalState.scanResponse?.isDemoCard() == true) {
addDummyBalances(walletManagers)
}
}
private fun addTokens(
tokens: List<Token>, blockchainNetwork: BlockchainNetwork,
walletState: WalletState?, globalState: GlobalState?
) {
val scanResponse = globalState?.scanResponse ?: return
val wmFactory = globalState.tapWalletManager.walletManagerFactory
val groupedTokens = tokens.groupBy { it.blockchain }
val walletManagers = groupedTokens.mapNotNull { entry ->
val blockchain = entry.key
val tokensList = entry.value
val walletManager = walletState?.getWalletManager(blockchain)
?: wmFactory.makeWalletManagerForApp(scanResponse, blockchain)?.also {
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(it))
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchain))
}
store.dispatch(WalletAction.LoadFiatRate(currencyList = tokensList.map { Currency.Token(it) }))
walletManager?.apply { addTokens(tokensList) }
}
val walletManager = walletState?.getWalletManager(blockchainNetwork)
?: wmFactory.makeWalletManagerForApp(scanResponse, blockchainNetwork)?.also {
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchainNetwork, it))
} ?: return
store.dispatch(WalletAction.LoadFiatRate(currencyList = tokens.map { token ->
Currency.Token(
token, blockchainNetwork.blockchain, blockchainNetwork.derivationPath
)
}))
walletManager.addTokens(tokens)
currenciesRepository.saveUpdatedCurrency(
cardId = scanResponse.card.cardId,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
)
scope.launch {
walletManagers.forEach { walletManager ->
when (val result = walletManager.safeUpdate()) {
is com.tangem.common.services.Result.Success -> {
val wallet = result.data
wallet.getTokens()
.filter { tokens.contains(it) }
.mapNotNull { token -> wallet.getTokenAmount(token)?.let { Pair(token, it) } }
.forEach {
withContext(Dispatchers.Main) {
store.dispatch(WalletAction.MultiWallet.TokenLoaded(it.second, it.first))
}
when (val result = walletManager.safeUpdate()) {
is com.tangem.common.services.Result.Success -> {
val wallet = result.data
wallet.getTokens()
.filter { tokens.contains(it) }
.mapNotNull { token ->
wallet.getTokenAmount(token)?.let { Pair(token, it) }
}
.forEach {
withContext(Dispatchers.Main) {
store.dispatch(
WalletAction.MultiWallet.TokenLoaded(
it.second,
it.first,
blockchainNetwork
)
)
}
}
is com.tangem.common.services.Result.Failure -> {}
}
}
is com.tangem.common.services.Result.Failure -> {}
}
}
}

View file

@ -5,8 +5,6 @@ import android.net.Uri
import androidx.core.content.ContextCompat
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.isZero
@ -24,10 +22,7 @@ import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.network.NetworkStateChanged
import com.tangem.tap.scope
import com.tangem.tap.store
@ -62,7 +57,11 @@ class WalletMiddleware {
when (action) {
is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(state, action)
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState)
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(
action,
walletState,
globalState
)
is WalletAction.LoadWallet -> {
scope.launch {
if (action.blockchain == null) {
@ -78,9 +77,19 @@ class WalletMiddleware {
is WalletAction.LoadWallet.Success -> {
val coinAmount = action.wallet.amounts[AmountType.Coin]?.value
if (coinAmount != null && !coinAmount.isZero()) {
if (walletState.getWalletData(action.wallet.blockchain) == null) {
store.dispatch(WalletAction.MultiWallet.AddBlockchain(action.wallet.blockchain))
store.dispatch(WalletAction.LoadWallet.Success(action.wallet))
if (walletState.getWalletData(action.blockchain) == null) {
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
action.blockchain,
null
)
)
store.dispatch(
WalletAction.LoadWallet.Success(
action.wallet,
action.blockchain
)
)
}
}
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
@ -120,7 +129,8 @@ class WalletMiddleware {
)
when (result) {
is CompletionResult.Success -> {
val scanNoteResponse = globalState.scanResponse?.copy(card = result.data)
val scanNoteResponse =
globalState.scanResponse?.copy(card = result.data)
scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) }
}
is CompletionResult.Failure -> {
@ -196,8 +206,10 @@ class WalletMiddleware {
}
}
is WalletAction.ShowDialog.QrCode -> {
val selectedWalletData = walletState.getWalletData(walletState.selectedCurrency) ?: return
val selectedAddressData = selectedWalletData.walletAddresses?.selectedAddress ?: return
val selectedWalletData =
walletState.getWalletData(walletState.selectedCurrency) ?: return
val selectedAddressData =
selectedWalletData.walletAddresses?.selectedAddress ?: return
val currency = selectedWalletData.currency
store.dispatchDialogShow(AppDialog.AddressInfoDialog(currency, selectedAddressData))
@ -208,28 +220,38 @@ class WalletMiddleware {
private fun prepareSendAction(amount: Amount?, state: WalletState?): Action {
val selectedWalletData = state?.getSelectedWalletData()
val currency = selectedWalletData?.currency
val walletManager = state?.getWalletManager(currency)
val wallet = walletManager?.wallet
val walletStore = state?.getWalletStore(currency)
return if (amount != null) {
if (amount.type is AmountType.Token) {
prepareSendActionForToken(amount, state, selectedWalletData, wallet, walletManager)
prepareSendActionForToken(amount, state, selectedWalletData, walletStore)
} else {
PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletManager)
PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletStore?.walletManager)
}
} else {
val amounts = wallet?.amounts?.toSendableAmounts()
val amounts = walletStore?.walletManager?.wallet?.amounts?.toSendableAmounts()
if (currency != null && state.isMultiwalletAllowed) {
when (currency) {
is Currency.Blockchain -> {
val amountToSend = amounts?.find { it.currencySymbol == currency.blockchain.currency }
?: return WalletAction.Send.ChooseCurrency(amounts)
PrepareSendScreen(amountToSend, selectedWalletData.fiatRate, walletManager)
val amountToSend =
amounts?.find { it.currencySymbol == currency.blockchain.currency }
?: return WalletAction.Send.ChooseCurrency(amounts)
PrepareSendScreen(
coinAmount = amountToSend,
coinRate = selectedWalletData.fiatRate,
walletManager = walletStore.walletManager
)
}
is Currency.Token -> {
val amountToSend = amounts?.find { it.currencySymbol == currency.token.symbol }
?: return WalletAction.Send.ChooseCurrency(amounts)
prepareSendActionForToken(amountToSend, state, selectedWalletData, wallet, walletManager)
val amountToSend =
amounts?.find { it.currencySymbol == currency.token.symbol }
?: return WalletAction.Send.ChooseCurrency(amounts)
prepareSendActionForToken(
amount = amountToSend,
state = state,
selectedWalletData = selectedWalletData,
walletStore = walletStore
)
}
}
} else {
@ -237,24 +259,36 @@ class WalletMiddleware {
WalletAction.Send.ChooseCurrency(amounts)
} else {
val amountToSend = amounts?.first()
PrepareSendScreen(amountToSend, selectedWalletData?.fiatRate, walletManager)
PrepareSendScreen(
coinAmount = amountToSend,
coinRate = selectedWalletData?.fiatRate,
walletManager = walletStore?.walletManager
)
}
}
}
}
private fun prepareSendActionForToken(
amount: Amount, state: WalletState?, selectedWalletData: WalletData?, wallet: Wallet?,
walletManager: WalletManager?
amount: Amount,
state: WalletState?,
selectedWalletData: WalletData?,
walletStore: WalletStore?
): PrepareSendScreen {
val coinRate = state?.getWalletData(wallet?.blockchain)?.fiatRate
val coinRate = state?.getWalletData(walletStore?.blockchainNetwork)?.fiatRate
val tokenRate = if (state?.isMultiwalletAllowed == true) {
selectedWalletData?.fiatRate
} else {
selectedWalletData?.currencyData?.token?.fiatRate
}
val coinAmount = walletStore?.walletManager?.wallet?.amounts?.get(AmountType.Coin)
return PrepareSendScreen(
wallet?.amounts?.get(AmountType.Coin), coinRate, walletManager,
amount, tokenRate)
coinAmount = coinAmount,
coinRate = coinRate,
walletManager = walletStore?.walletManager,
tokenAmount = amount,
tokenRate = tokenRate
)
}
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.extensions.isZero
import com.tangem.tap.common.extensions.toFiatString
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.redux.*
@ -17,66 +18,85 @@ import com.tangem.tap.store
class MultiWalletReducer {
fun reduce(action: WalletAction.MultiWallet, state: WalletState): WalletState {
return when (action) {
is WalletAction.MultiWallet.AddWalletManagers -> {
state.addWalletManagers(action.walletManagers)
}
is WalletAction.MultiWallet.AddBlockchains -> {
val walletsData = action.blockchains.map { blockchain ->
val wallet = state.getWalletManager(blockchain)?.wallet
val wallets: List<WalletStore> = action.blockchains.map { blockchain ->
val walletManager = action.walletManagers.first {
it.wallet.blockchain == blockchain.blockchain &&
(it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath)
}
val wallet = walletManager.wallet
val cardToken = if (!state.isMultiwalletAllowed) {
wallet?.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
wallet.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
} else {
null
}
WalletData(
val walletData = WalletData(
currencyData = BalanceWidgetData(
BalanceStatus.Loading,
blockchain.fullName,
currencySymbol = blockchain.currency,
blockchain.blockchain.fullName,
currencySymbol = blockchain.blockchain.currency,
token = cardToken
),
walletAddresses = createAddressList(wallet),
mainButton = WalletMainButton.SendButton(false),
currency = Currency.Blockchain(blockchain),
currency = Currency.Blockchain(
blockchain.blockchain,
blockchain.derivationPath
),
)
WalletStore(
walletManager = walletManager,
blockchainNetwork = blockchain,
walletsData = listOf(walletData)
)
}
val selectedCurrency = if (!state.isMultiwalletAllowed) {
walletsData[0].currency
wallets[0].walletsData[0].currency
} else {
state.selectedCurrency
}
state.copy(
walletsData = walletsData,
wallets = wallets,
selectedCurrency = selectedCurrency
)
}
is WalletAction.MultiWallet.AddBlockchain -> {
val wallet = state.getWalletManager(action.blockchain)?.wallet
val walletManager = action.walletManager ?: state.getWalletManager(action.blockchain)
val wallet = walletManager?.wallet
val walletData = WalletData(
currencyData = BalanceWidgetData(
BalanceStatus.Loading,
action.blockchain.fullName,
currencySymbol = action.blockchain.currency,
action.blockchain.blockchain.fullName,
currencySymbol = action.blockchain.blockchain.currency,
),
walletAddresses = createAddressList(wallet),
mainButton = WalletMainButton.SendButton(false),
currency = Currency.Blockchain(action.blockchain),
currency = Currency.Blockchain(
action.blockchain.blockchain,
action.blockchain.derivationPath
),
)
val newState = state.copy(walletsData = state.replaceWalletInWallets(walletData))
val walletStore = WalletStore(
walletManager = walletManager,
blockchainNetwork = action.blockchain,
walletsData = listOf(walletData)
)
val newState = state.updateWalletStore(walletStore)
if (wallet != null && wallet.amounts[AmountType.Coin]?.value != null) {
OnWalletLoadedReducer().reduce(wallet, newState)
OnWalletLoadedReducer().reduce(wallet, action.blockchain, newState)
} else {
newState
}
}
is WalletAction.MultiWallet.AddTokens -> {
val wallets = action.tokens.mapNotNull { token -> token.toWallet(state) }
state.copy(walletsData = state.replaceSomeWallets(wallets))
addTokens(action.tokens, action.blockchain, state)
}
is WalletAction.MultiWallet.AddToken -> {
val walletData = action.token.toWallet(state) ?: return state
state.copy(walletsData = state.replaceWalletInWallets(walletData))
addTokens(listOf(action.token), action.blockchain, state)
}
is WalletAction.MultiWallet.TokenLoaded -> {
val pendingTransactions = state.getWalletManager(action.token)
@ -84,7 +104,8 @@ class MultiWalletReducer {
wallet.recentTransactions.toPendingTransactions(wallet.address)
} ?: emptyList()
val sendButtonEnabled = action.amount.value?.isZero() == false && pendingTransactions.isEmpty()
val sendButtonEnabled =
action.amount.value?.isZero() == false && pendingTransactions.isEmpty()
val tokenPendingTransactions = pendingTransactions
.filter { it.currency == action.amount.currencySymbol }
val tokenBalanceStatus = when {
@ -107,10 +128,13 @@ class MultiWalletReducer {
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
currency = Currency.Token(action.token)
currency = Currency.Token(
token = action.token,
blockchain = action.blockchain.blockchain,
derivationPath = action.blockchain.derivationPath
)
)
val wallets = state.replaceWalletInWallets(newTokenWalletData)
state.copy(walletsData = wallets)
state.updateWalletData(newTokenWalletData)
}
is WalletAction.MultiWallet.SetIsMultiwalletAllowed ->
state.copy(isMultiwalletAllowed = action.isMultiwalletAllowed)
@ -119,21 +143,7 @@ class MultiWalletReducer {
state.copy(selectedCurrency = action.walletData?.currency)
is WalletAction.MultiWallet.RemoveWallet -> {
val wallets = state.walletsData.filterNot {
it.currency == action.walletData.currency
}
if (action.walletData.currency is Currency.Blockchain) {
state.copy(
walletsData = wallets,
walletManagers = state.walletManagers.filterNot {
it.wallet.blockchain == action.walletData.currency.blockchain
}
)
} else {
state.copy(walletsData = wallets)
}
state.removeWallet(action.walletData)
}
is WalletAction.MultiWallet.SetPrimaryBlockchain ->
state.copy(primaryBlockchain = action.blockchain)
@ -147,7 +157,14 @@ class MultiWalletReducer {
}
}
fun Token.toWallet(state: WalletState): WalletData? {
private fun addTokens(
tokens: List<Token>, blockchain: BlockchainNetwork, state: WalletState
): WalletState {
val wallets = tokens.mapNotNull { token -> token.toWallet(state, blockchain) }
return state.updateWalletsData(wallets)
}
fun Token.toWallet(state: WalletState, blockchain: BlockchainNetwork): WalletData? {
if (!state.isMultiwalletAllowed) return null
if (state.currencies.any { it is Currency.Token && it.token == this }) {
return null
@ -164,6 +181,6 @@ fun Token.toWallet(state: WalletState): WalletData? {
),
walletAddresses = walletAddresses,
mainButton = WalletMainButton.SendButton(false),
currency = Currency.Token(this),
currency = Currency.fromBlockchainNetwork(blockchain, this)
)
}

View file

@ -8,6 +8,7 @@ import com.tangem.tap.common.extensions.toFiatValue
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.redux.*
@ -19,25 +20,30 @@ import java.math.RoundingMode
class OnWalletLoadedReducer {
fun reduce(wallet: Wallet, walletState: WalletState): WalletState {
fun reduce(wallet: Wallet, blockchainNetwork: BlockchainNetwork, walletState: WalletState): WalletState {
return if (!walletState.isMultiwalletAllowed) {
onSingleWalletLoaded(wallet, walletState)
} else {
onMultiWalletLoaded(wallet, walletState)
onMultiWalletLoaded(wallet, blockchainNetwork, walletState)
}
}
private fun onMultiWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState {
private fun onMultiWalletLoaded(
wallet: Wallet,
blockchainNetwork: BlockchainNetwork,
walletState: WalletState
): WalletState {
val fiatCurrencySymbol = store.state.globalState.appCurrency
val exchangeManager = store.state.globalState.currencyExchangeManager
val coinAmountValue = wallet.amounts[AmountType.Coin]?.value
if (walletState.getWalletData(wallet.blockchain) == null) {
if (walletState.getWalletData(blockchainNetwork) == null) {
return walletState
}
val formattedAmount = coinAmountValue?.toFormattedCurrencyString(
wallet.blockchain.decimals(),
wallet.blockchain.currency)
wallet.blockchain.currency
)
val pendingTransactions = wallet.recentTransactions
.toPendingTransactions(wallet.address)
@ -48,7 +54,7 @@ class OnWalletLoadedReducer {
} else {
BalanceStatus.VerifiedOnline
}
val walletData = walletState.getWalletData(wallet.blockchain)
val walletData = walletState.getWalletData(blockchainNetwork)
val fiatAmount = walletData?.fiatRate?.let { coinAmountValue?.toFiatValue(it) }
val newWalletData = walletData?.copy(
@ -63,29 +69,34 @@ class OnWalletLoadedReducer {
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(coinSendButton),
currency = Currency.Blockchain(wallet.blockchain),
currency = Currency.fromBlockchainNetwork(blockchainNetwork),
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData),
)
val tokens = wallet.getTokens().mapNotNull { token ->
val tokenWalletData = walletState.getWalletData(token)
val tokenPendingTransactions = pendingTransactions.filter { it.currency == token.symbol }
val tokenPendingTransactions =
pendingTransactions.filter { it.currency == token.symbol }
val tokenBalanceStatus = when {
tokenPendingTransactions.isNotEmpty() -> BalanceStatus.TransactionInProgress
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
else -> BalanceStatus.VerifiedOnline
}
val tokenAmountValue = wallet.getTokenAmount(token)?.value
val tokenFiatAmount = tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
val tokenFiatAmount =
tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
val tokenSendButton = newWalletData?.shouldEnableTokenSendButton() == true
&& tokenPendingTransactions.isEmpty()
&& tokenPendingTransactions.isEmpty()
tokenWalletData?.copy(
currencyData = tokenWalletData.currencyData.copy(
status = tokenBalanceStatus,
blockchainAmount = coinAmountValue,
amount = tokenAmountValue,
amountFormatted = tokenAmountValue?.toFormattedCurrencyString(token.decimals, token.symbol),
amountFormatted = tokenAmountValue?.toFormattedCurrencyString(
token.decimals,
token.symbol
),
fiatAmount = tokenFiatAmount,
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
),
@ -102,8 +113,9 @@ class OnWalletLoadedReducer {
} else {
ProgressState.Done
}
return walletState.copy(
state = state, walletsData = wallets, error = null
val newState = walletState.updateWalletsData(wallets)
return newState.copy(
state = state, error = null
)
}
@ -118,7 +130,8 @@ class OnWalletLoadedReducer {
val tokenAmount = wallet.getTokenAmount(token)
if (tokenAmount != null) {
val tokenFiatRate = walletState.primaryWallet?.currencyData?.token?.fiatRate
val tokenFiatAmount = tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencySymbol) }
val tokenFiatAmount =
tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencySymbol) }
TokenData(
tokenAmount.value?.toFormattedCurrencyString(
token.decimals, token.symbol
@ -134,7 +147,8 @@ class OnWalletLoadedReducer {
val amount = wallet.amounts[AmountType.Coin]?.value
val formattedAmount = amount?.toFormattedCurrencyString(
wallet.blockchain.decimals(),
wallet.blockchain.currency)
wallet.blockchain.currency
)
val fiatRate = walletState.primaryWallet?.fiatRate
val fiatAmountRaw = fiatRate?.multiply(amount)?.setScale(2, RoundingMode.DOWN)
val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencySymbol) }
@ -161,9 +175,11 @@ class OnWalletLoadedReducer {
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletState.primaryWallet),
)
val wallets = walletData?.let { listOf(walletData) } ?: emptyList()
return walletState.copy(
state = ProgressState.Done, walletsData = wallets, error = null
val wallets = listOfNotNull(walletData)
val updatedStore = walletState.getWalletStore(walletData?.currency)?.updateWallets(wallets)
return walletState.updateWalletStore(updatedStore).copy(
state = ProgressState.Done, error = null
)
}
}

View file

@ -12,6 +12,7 @@ import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.getArtworkUrl
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.ui.BalanceStatus
@ -46,11 +47,21 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
is WalletAction.EmptyWallet -> {
newState = newState.copy(
state = ProgressState.Done,
walletsData = listOf(
WalletData(
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
mainButton = WalletMainButton.CreateWalletButton(true),
currency = Currency.Blockchain(Blockchain.Unknown)
wallets = listOf(
WalletStore(
walletManager = null,
blockchainNetwork = BlockchainNetwork(
Blockchain.Unknown,
null,
emptyList()
),
walletsData = listOf(
WalletData(
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
mainButton = WalletMainButton.CreateWalletButton(true),
currency = Currency.Blockchain(Blockchain.Unknown, null)
)
)
)
)
)
@ -58,27 +69,43 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
is WalletAction.LoadData.Failure -> {
when (action.error) {
is TapError.NoInternetConnection -> {
val wallets = newState.walletsData
val wallets = newState.wallets
.map {
it.copy(
currencyData = it.currencyData.copy(
status = BalanceStatus.Unreachable
)
walletsData = it.walletsData.map {
it.copy(
currencyData = it.currencyData.copy(
status = BalanceStatus.Unreachable
)
)
}
)
}
newState = newState.copy(
state = ProgressState.Error,
error = ErrorType.NoInternetConnection,
walletsData = wallets
wallets = wallets
)
}
is TapError.UnknownBlockchain -> {
newState = newState.copy(
state = ProgressState.Done,
walletsData = listOf(
WalletData(
currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
currency = Currency.Blockchain(Blockchain.Unknown)
wallets = listOf(
WalletStore(
walletManager = null,
blockchainNetwork = BlockchainNetwork(
Blockchain.Unknown,
null,
emptyList()
),
walletsData = listOf(
WalletData(
currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
currency = Currency.Blockchain(Blockchain.Unknown, null)
)
)
)
)
)
@ -94,26 +121,37 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
}
is WalletAction.LoadWallet -> {
if (action.blockchain == null) {
val wallets = newState.walletsData.map { wallet ->
wallet.copy(
currencyData = wallet.currencyData.copy(
status = BalanceStatus.Loading,
currency = wallet.currencyData.currency,
currencySymbol = wallet.currencyData.currencySymbol,
),
mainButton = WalletMainButton.SendButton(false),
tradeCryptoState = TradeCryptoState.from(exchangeManager, wallet)
val wallets = newState.wallets.map {
it.copy(
walletsData = it.walletsData.map { walletData ->
walletData.copy(
currencyData = walletData.currencyData.copy(
status = BalanceStatus.Loading,
currency = walletData.currencyData.currency,
currencySymbol = walletData.currencyData.currencySymbol,
),
mainButton = WalletMainButton.SendButton(false),
tradeCryptoState = TradeCryptoState.from(
exchangeManager,
walletData
)
)
}
)
}
newState = newState.copy(
state = ProgressState.Loading,
walletsData = wallets,
wallets = wallets,
)
} else {
val walletManager = newState.getWalletManager(action.blockchain) ?: return newState
val blockchain = walletManager.wallet.blockchain
val currencies = listOf(Currency.Blockchain(blockchain)) +
walletManager.cardTokens.map { Currency.Token(it) }
val currencies = listOf(Currency.fromBlockchainNetwork(action.blockchain)) +
walletManager.cardTokens.map {
Currency.fromBlockchainNetwork(
action.blockchain,
it
)
}
val newWallets = newState.walletsData.filter { currencies.contains(it.currency) }
.map { wallet ->
wallet.copy(
@ -126,38 +164,52 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
tradeCryptoState = TradeCryptoState.from(exchangeManager, wallet)
)
}
val wallets = newState.replaceSomeWallets(newWallets)
newState = newState.copy(walletsData = newState.updateTradeCryptoState(exchangeManager, wallets))
val wallets = newState.updateTradeCryptoState(exchangeManager, newState.replaceSomeWallets(newWallets))
val walletStore = newState.getWalletStore(action.blockchain)?.updateWallets(wallets)
newState = newState.updateWalletStore(walletStore)
}
}
is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(action.wallet, newState)
is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(
wallet = action.wallet,
blockchainNetwork = action.blockchain,
walletState = newState
)
is WalletAction.LoadWallet.NoAccount -> {
val walletData = newState.getWalletData(action.wallet.blockchain)?.copy(
val walletData = newState.getWalletData(action.blockchain)?.copy(
currencyData = BalanceWidgetData(
BalanceStatus.NoAccount, action.wallet.blockchain.fullName,
currencySymbol = action.wallet.blockchain.currency,
amountToCreateAccount = action.amountToCreateAccount
)
)
val wallets = newState.replaceWalletInWallets(walletData)
var updatedWalletStore = newState.getWalletStore(action.blockchain)
?.updateWallets(listOfNotNull(walletData))
val progressState =
if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
if (updatedWalletStore?.walletsData?.any { it.currencyData.status == BalanceStatus.Loading } == true) {
ProgressState.Loading
} else {
ProgressState.Done
}
newState = newState.copy(
state = progressState,
walletsData = newState.updateTradeCryptoState(exchangeManager, wallets)
)
updatedWalletStore =
updatedWalletStore?.updateWallets(
newState.updateTradeCryptoState(exchangeManager, updatedWalletStore.walletsData)
)
newState = newState.updateWalletStore(updatedWalletStore)
.copy(
state = progressState
)
}
is WalletAction.LoadWallet.Failure -> {
val message = if (newState.error == ErrorType.NoInternetConnection) {
null
} else {
action.errorMessage
}
val walletData = newState.getWalletData(action.wallet.blockchain)
val walletStore = newState.getWalletStore(action.wallet)
val walletData = walletStore?.walletsData?.first { it.currency is Currency.Blockchain }
val newWalletData = walletData?.copy(
currencyData = walletData.currencyData.copy(
status = BalanceStatus.Unreachable,
@ -173,17 +225,23 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
)
)
}
val wallets = newState.replaceSomeWallets(listOfNotNull(newWalletData) + tokenWallets)
val updatedWallets =
newState.updateTradeCryptoState(
exchangeManager,
walletStore!!.updateWallets(listOfNotNull(newWalletData) + tokenWallets).walletsData
)
newState = newState.updateWalletsData(updatedWallets)
val progressState =
if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
if (newState.walletsData.any { it.currencyData.status == BalanceStatus.Loading }) {
ProgressState.Loading
} else {
ProgressState.Done
}
newState = newState.copy(
state = progressState,
walletsData = newState.updateTradeCryptoState(exchangeManager, wallets)
)
}
is WalletAction.SetArtworkId -> {
@ -230,25 +288,21 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
?: return newState
val address = walletAddresses.list.firstOrNull { it.type == action.type }
?: return newState
val wallets = newState.replaceWalletInWallets(
selectedWalletData?.copy(
walletAddresses = WalletAddresses(
address,
walletAddresses.list
)
newState = newState.updateWalletData(selectedWalletData?.copy(
walletAddresses = WalletAddresses(
address,
walletAddresses.list
)
)
newState = newState.copy(walletsData = wallets)
))
}
is WalletAction.SetWalletRent -> {
var walletData = newState.getWalletData(action.blockchain)
if (walletData == null) {
newState
} else {
walletData = walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt))
newState = newState.copy(
walletsData = newState.replaceSomeWallets(listOf(walletData))
)
walletData =
walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt))
newState = newState.updateWalletsData(listOf(walletData))
}
}
}
@ -322,8 +376,11 @@ private fun setMultiWalletFiatRate(
rate: BigDecimal, rateFormatted: String, currency: Currency,
appCurrency: FiatCurrencyName, state: WalletState
): WalletState {
val walletStore = state.getWalletStore(currency) ?: return state
val wallet = walletStore.walletManager?.wallet
val walletData = state.getWalletData(currency) ?: return state
val wallet = state.getWalletManager(currency)?.wallet
val fiatAmount = when (currency) {
is Currency.Blockchain ->
wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatValue(rate)
@ -338,14 +395,14 @@ private fun setMultiWalletFiatRate(
),
fiatRate = rate, fiatRateString = rateFormatted
)
return state.copy(walletsData = state.replaceWalletInWallets(newWalletData))
return state.updateWalletData(newWalletData)
}
private fun setSingeWalletFiatRate(
rate: BigDecimal, rateFormatted: String, currency: Currency,
appCurrency: FiatCurrencyName, state: WalletState
): WalletState {
val wallet = state.walletManagers[0].wallet
val wallet = state.primaryWalletManager?.wallet ?: return state
val token = wallet.getFirstToken()
if (currency == state.primaryWallet?.currency) {
@ -356,7 +413,7 @@ private fun setSingeWalletFiatRate(
fiatRate = rate,
fiatRateString = rateFormatted
)
return state.copy(walletsData = listOf(walletData))
return state.updateWalletData(walletData)
} else if (currency is Currency.Token && currency.token == token) {
val tokenFiatAmount = wallet.getTokenAmount(token)?.value?.toFiatString(rate, appCurrency)
val tokenData = state.primaryWallet?.currencyData?.token?.copy(
@ -371,7 +428,7 @@ private fun setSingeWalletFiatRate(
)
)
val wallets = walletData?.let { listOf(walletData) } ?: emptyList()
return state.copy(walletsData = wallets)
return state.updateWalletsData(wallets)
}
return state
}

View file

@ -18,6 +18,7 @@ import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.onboarding.getQRReceiveMessage
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.redux.*
@ -114,7 +115,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
store.dispatch(
WalletAction.LoadWallet(
blockchain = selectedWallet.currency.blockchain
blockchain = BlockchainNetwork(selectedWallet.currency.blockchain, selectedWallet.currency.derivationPath, emptyList())
)
)
}
@ -188,7 +189,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
private fun showPendingTransactionsIfPresent(pendingTransactions: List<PendingTransaction>) {
pendingTransactionAdapter.submitList(pendingTransactions)
binding.rvPendingTransaction?.show(pendingTransactions.isNotEmpty())
binding.rvPendingTransaction.show(pendingTransactions.isNotEmpty())
}
private fun setupAddressCard(state: WalletData) = with(binding.lWalletDetails) {
@ -212,7 +213,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
chipGroupAddressType.hide()
}
tvAddress.text = state.walletAddresses.selectedAddress.address
tvExplore?.setOnClickListener {
tvExplore.setOnClickListener {
store.dispatch(
WalletAction.ExploreAddress(
state.walletAddresses.selectedAddress.exploreUrl,

View file

@ -8,6 +8,9 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletDialog
@ -97,11 +100,19 @@ class MultiWalletView : WalletView {
walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken)
binding.btnAddToken.setOnClickListener {
store.dispatch(TokensAction.LoadCurrencies)
val card = store.state.globalState.scanResponse!!.card
store.dispatch(TokensAction.LoadCurrencies(
supportedBlockchains = currenciesRepository.getBlockchains(
card.firmwareVersion,
card.isTestCard
)))
store.dispatch(TokensAction.AllowToAddTokens(true))
store.dispatch(TokensAction.SetAddedCurrencies(state.walletsData))
store.dispatch(TokensAction.SetAddedCurrencies(
wallets = state.walletsData,
derivationStyle = card.derivationStyle
))
store.dispatch(TokensAction.SetNonRemovableCurrencies(
state.wallets.filterNot { state.canBeRemoved(it) })
state.walletsData.filterNot { state.canBeRemoved(it) })
)
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
}