Updated on 2026-08-14

This commit is contained in:
Tangem 2022-04-22 18:13:28 +03:00
commit e93963d14d
19 changed files with 153 additions and 166 deletions

File diff suppressed because one or more lines are too long

View file

@ -250,10 +250,10 @@ class TapWalletManager {
WalletAction.MultiWallet.AddBlockchains(blockchainNetworks, walletManagers),
)
}
dispatchOnMain(
WalletAction.MultiWallet.FindBlockchainsInUse,
WalletAction.MultiWallet.FindTokensInUse,
)
// dispatchOnMain(
// WalletAction.MultiWallet.FindBlockchainsInUse,
// WalletAction.MultiWallet.FindTokensInUse,
// )
} else {
val walletManagers = if (
primaryTokens.isNotEmpty() &&

View file

@ -195,17 +195,24 @@ class WarningMessagesManager(
priority = WarningMessage.Priority.Warning,
listOf(WarningMessage.Location.MainScreen),
blockchains = null,
titleResId = R.string.alert_funds_restoration_message,
titleResId = R.string.alert_title,
messageResId = R.string.alert_funds_restoration_message,
origin = WarningMessage.Origin.Local,
buttonTextId = R.string.warning_button_learn_more
)
const val REMAINING_SIGNATURES_WARNING = 10
private const val RESTORE_FUNDS_GUIDE_URL_RU =
"https://tangem.com/ru/kak-vosstanovit-tokeny-otpravlennye-ne-na-tot-adres-v-tangem-wallet"
private const val RESTORE_FUNDS_GUIDE_URL_EN =
"https://tangem.com/en/how-to-recover-crypto-sent-to-the-wrong-address-in-tangem-wallet"
fun getRestoreFundsGuideUrl(locale: String): String {
val code = if (locale == Locale("ru").language) "ru" else "en"
return "https://tangem.com/$code/notion"
return if (locale == Locale("ru").language) {
RESTORE_FUNDS_GUIDE_URL_RU
} else {
RESTORE_FUNDS_GUIDE_URL_EN
}
}
}
}

View file

@ -179,7 +179,7 @@ class CurrenciesRepository(val context: Application) {
fun getSupportedTokens(isTestNet: Boolean = false): List<Currency> {
val fileName = if (isTestNet) "testnet_tokens" else "tokens"
val json = context.assets.readJsonFileToString(fileName)
return currenciesAdapter.fromJson(json)!!.tokens
return currenciesAdapter.fromJson(json)!!.coins
.map { Currency.fromJsonObject(it) }
}

View file

@ -9,20 +9,20 @@ data class CurrencyFromJson(
val id: String,
val name: String,
val symbol: String,
val contracts: List<ContractFromJson>? = null
val networks: List<ContractFromJson>? = null
)
@JsonClass(generateAdapter = true)
data class ContractFromJson(
val networkId: String,
val address: String,
val decimalCount: Int
val contractAddress: String?,
val decimalCount: Int?
)
@JsonClass(generateAdapter = true)
data class CurrenciesFromJson(
val imageHost: String?,
val tokens: List<CurrencyFromJson>
val coins: List<CurrencyFromJson>
)
@ -45,26 +45,17 @@ data class Currency(
name = currency.name,
symbol = currency.symbol,
iconUrl = getIconUrl(currency.id),
contracts = prepareListOfContracts(currency.contracts, currency.id)
contracts = currency.networks?.toContracts() ?: emptyList()
)
}
private fun prepareListOfContracts(
contractsFromJson: List<ContractFromJson>?,
currencyId: String,
): List<Contract> {
val mainNetwork = Contract.fromCurrencyId(currencyId)
val contracts = contractsFromJson?.toContracts() ?: emptyList()
return (listOfNotNull(mainNetwork) + contracts).distinct()
}
}
}
data class Contract(
val networkId: String,
val blockchain: Blockchain,
val address: String,
val decimalCount: Int,
val address: String?,
val decimalCount: Int?,
val iconUrl: String,
) {
@ -74,23 +65,11 @@ data class Contract(
return Contract(
networkId = contract.networkId,
blockchain = blockchain,
address = contract.address,
address = contract.contractAddress,
decimalCount = contract.decimalCount,
iconUrl = getIconUrl(contract.networkId)
)
}
fun fromCurrencyId(currencyId: String): Contract? {
val blockchain = Blockchain.fromNetworkId(currencyId) ?: return null
return Contract(
networkId = currencyId,
blockchain = blockchain,
address = blockchain.currency,
decimalCount = blockchain.decimals(),
iconUrl = getIconUrl(currencyId)
)
}
}
}

View file

@ -69,9 +69,9 @@ fun CollapsedCurrencyItem(
Row {
if (!currency.contracts.isNullOrEmpty()) {
currency.contracts.map { contract ->
if (contract.address == currency.symbol) {
if (contract.address == null) {
BlockchainNetworkItem(
blockchain = Blockchain.fromNetworkId(contract.networkId),
blockchain = contract.blockchain,
isMainNetwork = true,
addedBlockchains = addedBlockchains
)

View file

@ -132,12 +132,13 @@ fun ExpandedCurrencyItem(
) {
blockchains.map { blockchain ->
val contract = currency.contracts.firstOrNull { it.blockchain == blockchain }
val added = if (contract != null && contract.address != currency.symbol) {
?: return@map
val added = if (contract.address != null) {
addedTokens.map { it.token.contractAddress }.contains(contract.address)
} else {
addedBlockchains.contains(blockchain)
}
val canBeRemoved = if (contract != null && contract.address != currency.symbol) {
val canBeRemoved = if (contract.address != null) {
!nonRemovableTokens.contains(contract.address)
} else {
!nonRemovableBlockchains.contains(blockchain)

View file

@ -32,31 +32,30 @@ import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun NetworkItem(
currency: Currency, contract: Contract?,
currency: Currency, contract: Contract,
blockchain: Blockchain, allowToAdd: Boolean,
added: Boolean, canBeRemoved: Boolean,
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit,
onNetworkItemClicked: (ContractAddress) -> Unit
) {
val isBlockchain = contract == null || contract.address == currency.symbol
Row(
modifier = Modifier
.fillMaxWidth()
.combinedClickable(
enabled = allowToAdd,
onLongClick = {
if (!isBlockchain) onNetworkItemClicked(contract!!.address)
contract.address?.let { onNetworkItemClicked(it) }
},
onClick = {},
indication = null,
interactionSource = remember { MutableInteractionSource() }
)
) {
Box(modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 8.dp, top = 16.dp, bottom = 16.dp, end = 6.dp)
Box(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 8.dp, top = 16.dp, bottom = 16.dp, end = 6.dp)
) {
SubcomposeAsyncImage(
model = if (added) blockchain.getRoundIconRes() else blockchain.getGreyedOutIconRes(),
@ -66,7 +65,7 @@ fun NetworkItem(
modifier = Modifier
.size(20.dp)
)
if (isBlockchain) {
if (contract.address == null) {
Box(
modifier = Modifier
.align(Alignment.TopEnd)
@ -98,29 +97,29 @@ fun NetworkItem(
)
Spacer(modifier = Modifier.size(3.dp))
Text(
text = if (isBlockchain) "MAIN" else blockchain.getNetworkName().uppercase(),
text = if (contract.address == null) "MAIN" else blockchain.getNetworkName().uppercase(),
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
color = if (!isBlockchain) Color(0xFF8E8E93) else Color(0xFF1ACE80),
color = if (contract.address != null) Color(0xFF8E8E93) else Color(0xFF1ACE80),
)
}
if (allowToAdd) {
val token = if (!isBlockchain) {
val token = if (contract.address != null) {
Token(
id = currency.id,
name = currency.name,
symbol = currency.symbol,
contractAddress = contract!!.address,
decimals = contract.decimalCount,
contractAddress = contract.address,
decimals = contract.decimalCount!!,
)
} else {
null
}
val tokenWithBlockchain =
token?.let { TokenWithBlockchain(token, contract!!.blockchain) }
token?.let { TokenWithBlockchain(token, contract.blockchain) }
val currencyToSave = if (isBlockchain && contract != null) {
val currencyToSave = if (contract.address == null) {
currency.copy(id = contract.networkId)
} else {
currency

View file

@ -58,8 +58,8 @@ sealed class WalletAction : Action {
data class AddToken(val token: Token, val blockchain: BlockchainNetwork) : MultiWallet()
data class SaveCurrencies(val blockchainNetworks: List<BlockchainNetwork>) : MultiWallet()
object FindTokensInUse : MultiWallet()
object FindBlockchainsInUse : MultiWallet()
// object FindTokensInUse : MultiWallet()
// object FindBlockchainsInUse : MultiWallet()
data class TokenLoaded(
val amount: Amount,

View file

@ -397,7 +397,10 @@ sealed interface Currency {
}
fun isCustomCurrency(derivationStyle: DerivationStyle?): Boolean {
if (this is Token && this.token.id == null) return true
if (derivationPath == null || derivationStyle == null) return false
return derivationPath != blockchain.derivationPath(derivationStyle)?.rawPath
}

View file

@ -1,15 +1,14 @@
package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.blockchain.common.*
import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.isZero
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.redux.global.GlobalState
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.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
@ -19,7 +18,6 @@ import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.math.BigDecimal
@ -29,7 +27,7 @@ class MultiWalletMiddleware {
action: WalletAction.MultiWallet, walletState: WalletState?, globalState: GlobalState?,
) {
val globalState = globalState ?: return
val tapWalletManager = globalState.tapWalletManager
// val tapWalletManager = globalState.tapWalletManager
when (action) {
is WalletAction.MultiWallet.AddBlockchains -> {
@ -105,94 +103,94 @@ class MultiWalletMiddleware {
}
}
}
is WalletAction.MultiWallet.FindBlockchainsInUse -> {
val scanResponse = globalState.scanResponse ?: return
if (scanResponse.supportsHdWallet()) return
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
)
scope.launch {
walletManagers.map { walletManager ->
async(Dispatchers.IO) {
walletManager.safeUpdate()
val wallet = walletManager.wallet
val coinAmount = wallet.amounts[AmountType.Coin]?.value
if (coinAmount != null && !coinAmount.isZero()) {
scope.launch(Dispatchers.Main) {
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
if (walletState?.getWalletData(blockchainNetwork) == null) {
store.dispatch(WalletAction.MultiWallet.AddBlockchain(
blockchainNetwork, walletManager
))
store.dispatch(WalletAction.LoadWallet.Success(
wallet = wallet,
blockchain = blockchainNetwork
))
}
}
}
}
}
}
}
is WalletAction.MultiWallet.FindTokensInUse -> {
val scanResponse = globalState.scanResponse ?: return
if (scanResponse.supportsHdWallet()) return
val walletFactory = tapWalletManager.walletManagerFactory
val card = scanResponse.card
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()) {
val blockchainNetwork = BlockchainNetwork(
walletManager.wallet.blockchain,
walletManager.wallet.publicKey.derivationPath?.rawPath,
walletManager.cardTokens.toList()
)
currenciesRepository.saveUpdatedCurrency(
card.cardId,
blockchainNetwork
)
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
blockchainNetwork,
walletManager
)
)
store.dispatch(
WalletAction.MultiWallet.AddTokens(
walletManager.cardTokens.toList(),
blockchainNetwork
)
)
}
}
}
}
}
}
// is WalletAction.MultiWallet.FindBlockchainsInUse -> {
// val scanResponse = globalState.scanResponse ?: return
// if (scanResponse.supportsHdWallet()) return
//
// 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
// )
//
// scope.launch {
// walletManagers.map { walletManager ->
// async(Dispatchers.IO) {
// walletManager.safeUpdate()
// val wallet = walletManager.wallet
// val coinAmount = wallet.amounts[AmountType.Coin]?.value
// if (coinAmount != null && !coinAmount.isZero()) {
// scope.launch(Dispatchers.Main) {
// val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
// if (walletState?.getWalletData(blockchainNetwork) == null) {
// store.dispatch(WalletAction.MultiWallet.AddBlockchain(
// blockchainNetwork, walletManager
// ))
// store.dispatch(WalletAction.LoadWallet.Success(
// wallet = wallet,
// blockchain = blockchainNetwork
// ))
// }
// }
// }
// }
// }
// }
// }
// is WalletAction.MultiWallet.FindTokensInUse -> {
// val scanResponse = globalState.scanResponse ?: return
// if (scanResponse.supportsHdWallet()) return
//
// val walletFactory = tapWalletManager.walletManagerFactory
// val card = scanResponse.card
//
// 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()) {
// val blockchainNetwork = BlockchainNetwork(
// walletManager.wallet.blockchain,
// walletManager.wallet.publicKey.derivationPath?.rawPath,
// walletManager.cardTokens.toList()
// )
// currenciesRepository.saveUpdatedCurrency(
// card.cardId,
// blockchainNetwork
// )
// store.dispatch(
// WalletAction.MultiWallet.AddBlockchain(
// blockchainNetwork,
// walletManager
// )
// )
// store.dispatch(
// WalletAction.MultiWallet.AddTokens(
// walletManager.cardTokens.toList(),
// blockchainNetwork
// )
// )
// }
// }
// }
// }
// }
// }
}
}

View file

@ -151,8 +151,8 @@ class MultiWalletReducer {
is WalletAction.MultiWallet.SetPrimaryToken ->
state.copy(primaryToken = action.token)
is WalletAction.MultiWallet.FindTokensInUse -> state
is WalletAction.MultiWallet.FindBlockchainsInUse -> state
// is WalletAction.MultiWallet.FindTokensInUse -> state
// is WalletAction.MultiWallet.FindBlockchainsInUse -> state
is WalletAction.MultiWallet.SaveCurrencies -> state
}
}

View file

@ -86,13 +86,13 @@ class WarningMessageVH(val binding: LayoutWarningBinding) : RecyclerView.ViewHol
binding.btnClose.hide()
val buttonAction =
when (warning.titleResId) {
R.string.warning_important_security_info -> {
when {
warning.titleResId == R.string.warning_important_security_info -> {
View.OnClickListener {
store.dispatch(WalletAction.ShowDialog.SignedHashesMultiWalletDialog)
}
}
R.string.alert_funds_restoration_message -> {
warning.messageResId == R.string.alert_funds_restoration_message -> {
binding.btnClose.show()
binding.btnClose.setOnClickListener {
store.dispatch(GlobalAction.HideWarningMessage(warning))

View file

@ -40,7 +40,7 @@
<string name="currency_subtitle_expanded">Available networks</string>
<string name="alert_manage_tokens_addresses_message">Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds.</string>
<string name="contract_address_copied_message">Contract address copied!</string>
<string name="alert_funds_restoration_message">Funds restoration instuction</string>
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>

View file

@ -40,7 +40,7 @@
<string name="currency_subtitle_expanded">Available networks</string>
<string name="alert_manage_tokens_addresses_message">Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds.</string>
<string name="contract_address_copied_message">Contract address copied!</string>
<string name="alert_funds_restoration_message">Funds restoration instuction</string>
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>

View file

@ -40,7 +40,7 @@
<string name="currency_subtitle_expanded">Available networks</string>
<string name="alert_manage_tokens_addresses_message">Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds.</string>
<string name="contract_address_copied_message">Contract address copied!</string>
<string name="alert_funds_restoration_message">Funds restoration instuction</string>
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>

View file

@ -40,7 +40,7 @@
<string name="currency_subtitle_expanded">Доступные сети</string>
<string name="alert_manage_tokens_addresses_message">Внимание! Валюты на разных сетях имеют разные адреса. Убедитесь, что адрес соответствует сети, в которой вы отправляете средства.</string>
<string name="contract_address_copied_message">Адрес контракта скопирован!</string>
<string name="alert_funds_restoration_message">Инструкция по восстановлению средств</string>
<string name="alert_funds_restoration_message">Если вы совершили ошибку с выбором сети при переводе средств с биржи, эта инструкция поможет вам восстановить средства</string>
<string name="common_custom">Пользовательский</string>

View file

@ -40,7 +40,7 @@
<string name="currency_subtitle_expanded">Available networks</string>
<string name="alert_manage_tokens_addresses_message">Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds.</string>
<string name="contract_address_copied_message">Contract address copied!</string>
<string name="alert_funds_restoration_message">Funds restoration instuction</string>
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>

View file

@ -26,11 +26,11 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
"dogecoin" -> Blockchain.Dogecoin
"ducatus" -> Blockchain.Ducatus
"litecoin" -> Blockchain.Litecoin
"rsk" -> Blockchain.RSK
"rootstock" -> Blockchain.RSK
"stellar" -> Blockchain.Stellar
"stellar/test" -> Blockchain.StellarTestnet
"tezos" -> Blockchain.Tezos
"ripple" -> Blockchain.XRP
"xrp" -> Blockchain.XRP
else -> null
}
}
@ -65,7 +65,7 @@ fun Blockchain.toNetworkId(): String {
Blockchain.Solana -> "solana"
Blockchain.SolanaTestnet -> "solana/test"
Blockchain.Tezos -> "tezos"
Blockchain.XRP -> "ripple"
Blockchain.XRP -> "xrp"
}
}