Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-14 18:18:27 +03:00
commit 6451e40024
10 changed files with 85 additions and 89 deletions

View file

@ -129,6 +129,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
super.onCreate(savedInstanceState)
cardSdkLifecycleObserver.onCreate(context = this)
bootstrapMainStateUpdates()
splashScreen.setKeepOnScreenCondition { isInitializing }
@ -138,8 +140,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
store.dispatch(NavigationAction.ActivityCreated(WeakReference(this)))
cardSdkLifecycleObserver.onCreate(context = this)
tangemSdkManager = injectedTangemSdkManager
appStateHolder.tangemSdkManager = tangemSdkManager
backupService = BackupService.init(cardSdkConfigRepository.sdk, this)

View file

@ -71,9 +71,9 @@ class DefaultCustomTokenInteractor(
onSuccess: suspend (ScanResponse) -> Unit,
) {
val config = CardConfig.createConfig(scanResponse.card)
val derivationDataList = currencyList.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencyList) }
val derivationDataList = currencyList.mapNotNull { currency ->
val curve = config.primaryCurve(currency.blockchain)
curve?.let { getDerivations(curve, scanResponse, currency) }
}
val derivations = derivationDataList.associate(TokensMiddleware.DerivationData::derivations)
@ -110,24 +110,25 @@ class DefaultCustomTokenInteractor(
private fun getDerivations(
curve: EllipticCurve,
scanResponse: ScanResponse,
currencyList: List<Currency>,
currency: Currency,
): TokensMiddleware.DerivationData? {
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter {
it.getSupportedCurves().contains(curve)
}.mapNotNull {
it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
val supportedCurves = currency.blockchain.getSupportedCurves()
val path = currency.derivationPath
?.let {
currency.blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
.takeIf { supportedCurves.contains(curve) }
val customTokensCandidates = currencyList.filter {
it.blockchain.getSupportedCurves().contains(curve)
}.mapNotNull { it.derivationPath }.map { DerivationPath(it) }
val customPath = currency.derivationPath?.let {
DerivationPath(it)
}.takeIf { supportedCurves.contains(curve) }
val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct().toMutableList()
val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList()
if (bothCandidates.isEmpty()) return null
currencyList.find { it is Currency.Blockchain && it.blockchain == Blockchain.Cardano }?.let { currency ->
if (currency is Currency.Blockchain && currency.blockchain == Blockchain.Cardano) {
currency.derivationPath?.let {
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
}

View file

@ -136,9 +136,9 @@ internal class DefaultTokensListInteractor(
private suspend fun deriveMissingBlockchains(scanResponse: ScanResponse, currencies: List<Currency>) {
val config = CardConfig.createConfig(scanResponse.card)
val derivations = currencies.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencies) }
val derivations = currencies.mapNotNull { currency ->
val curve = config.primaryCurve(currency.blockchain)
curve?.let { getDerivations(curve, scanResponse, currency) }
}.associate(transform = TokensMiddleware.DerivationData::derivations)
if (derivations.isEmpty()) {
@ -176,35 +176,36 @@ internal class DefaultTokensListInteractor(
private fun getDerivations(
curve: EllipticCurve,
scanResponse: ScanResponse,
currencyList: List<Currency>,
currency: Currency,
): TokensMiddleware.DerivationData? {
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val manageTokensCandidates = currencyList
.map(Currency::blockchain)
.distinct()
.filter { it.getSupportedCurves().contains(curve) }
.mapNotNull { it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) }
val supportedCurves = currency.blockchain.getSupportedCurves()
val path = currency.derivationPath
?.let {
currency.blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
.takeIf { supportedCurves.contains(curve) }
val customTokensCandidates = currencyList
.filter { it.blockchain.getSupportedCurves().contains(curve) }
.mapNotNull(Currency::derivationPath)
.map(::DerivationPath)
val customPath = currency.derivationPath?.let {
DerivationPath(it)
}.takeIf { supportedCurves.contains(curve) }
val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct().toMutableList()
val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList()
if (bothCandidates.isEmpty()) return null
currencyList.find { it is Currency.Blockchain && it.blockchain == Blockchain.Cardano }?.let { currency ->
if (currency is Currency.Blockchain && currency.blockchain == Blockchain.Cardano) {
currency.derivationPath?.let {
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
}
}
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
val alreadyDerivedKeys = scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
val alreadyDerivedKeys: ExtendedPublicKeysMap =
scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList()
val toDerive = bothCandidates.filterNot(alreadyDerivedPaths::contains)
val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) }
if (toDerive.isEmpty()) return null
return TokensMiddleware.DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive)

View file

@ -121,9 +121,9 @@ object TokensMiddleware {
onSuccess: (ScanResponse) -> Unit,
) {
val config = CardConfig.createConfig(scanResponse.card)
val derivationDataList = currencyList.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencyList) }
val derivationDataList = currencyList.mapNotNull { currency ->
val curve = config.primaryCurve(currency.blockchain)
curve?.let { getDerivations(curve, scanResponse, currency) }
}
val derivations = derivationDataList.associate { it.derivations }
if (derivations.isEmpty()) {
@ -163,27 +163,24 @@ object TokensMiddleware {
}
}
private fun getDerivations(
curve: EllipticCurve,
scanResponse: ScanResponse,
currencyList: List<Currency>,
): DerivationData? {
private fun getDerivations(curve: EllipticCurve, scanResponse: ScanResponse, currency: Currency): DerivationData? {
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter {
it.getSupportedCurves().contains(curve)
}.mapNotNull {
it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
val supportedCurves = currency.blockchain.getSupportedCurves()
val path = currency.derivationPath
?.let {
currency.blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
.takeIf { supportedCurves.contains(curve) }
val customTokensCandidates = currencyList.filter {
it.blockchain.getSupportedCurves().contains(curve)
}.mapNotNull { it.derivationPath }.map { DerivationPath(it) }
val customPath = currency.derivationPath?.let {
DerivationPath(it)
}.takeIf { supportedCurves.contains(curve) }
val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct().toMutableList()
val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList()
if (bothCandidates.isEmpty()) return null
currencyList.find { it is Currency.Blockchain && it.blockchain == Blockchain.Cardano }?.let { currency ->
if (currency is Currency.Blockchain && currency.blockchain == Blockchain.Cardano) {
currency.derivationPath?.let {
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
}

View file

@ -82,9 +82,6 @@ data class WalletState(
get() = primaryWalletStore?.walletsData
?.firstOrNull { it.currency !is Currency.Blockchain }
val shouldShowDetails: Boolean =
primaryWalletData?.status !is WalletDataModel.Unreachable
fun getWalletManager(currency: Currency?): WalletManager? {
if (currency?.blockchain == null) return null
return getWalletStore(currency)?.walletManager

View file

@ -190,9 +190,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber<W
walletView.onNewState(state)
if (!state.shouldShowDetails) {
binding.toolbar.menu.removeItem(R.id.details_menu)
} else if (binding.toolbar.menu.findItem(R.id.details_menu) == null) {
if (binding.toolbar.menu.findItem(R.id.details_menu) == null) {
binding.toolbar.inflateMenu(R.menu.menu_wallet)
}
@ -336,6 +334,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber<W
),
)
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
if (store.state.walletState.shouldShowDetails) inflater.inflate(R.menu.menu_wallet, menu)
inflater.inflate(R.menu.menu_wallet, menu)
}
}

View file

@ -26,6 +26,7 @@ import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware
import com.tangem.tap.scope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -97,9 +98,9 @@ class DerivationManagerImpl(
onFailure: (Exception) -> Unit,
) {
val config = CardConfig.createConfig(scanResponse.card)
val derivationDataList = currencyList.mapNotNull {
val curve = config.primaryCurve(it.blockchain)
curve?.let { getDerivations(curve, scanResponse, currencyList) }
val derivationDataList = currencyList.mapNotNull { currency ->
val curve = config.primaryCurve(currency.blockchain)
curve?.let { getDerivations(curve, scanResponse, currency) }
}
val derivations = derivationDataList.associate { it.derivations }
if (derivations.isEmpty()) {
@ -159,23 +160,32 @@ class DerivationManagerImpl(
private fun getDerivations(
curve: EllipticCurve,
scanResponse: ScanResponse,
currencyList: List<WalletModelCurrency>,
): DerivationData? {
currency: com.tangem.tap.features.wallet.models.Currency,
): TokensMiddleware.DerivationData? {
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter {
it.getSupportedCurves().contains(curve)
}.mapNotNull {
it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
val supportedCurves = currency.blockchain.getSupportedCurves()
val path = currency.derivationPath
?.let {
currency.blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
}
.takeIf { supportedCurves.contains(curve) }
val customTokensCandidates = currencyList.filter {
it.blockchain.getSupportedCurves().contains(curve)
}.mapNotNull { it.derivationPath }.map { DerivationPath(it) }
val customPath = currency.derivationPath?.let {
DerivationPath(it)
}.takeIf { supportedCurves.contains(curve) }
val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct().toMutableList()
val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList()
if (bothCandidates.isEmpty()) return null
if (currency is WalletModelCurrency.Blockchain &&
currency.blockchain == Blockchain.Cardano
) {
currency.derivationPath?.let {
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
}
}
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
val alreadyDerivedKeys: ExtendedPublicKeysMap =
scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
@ -184,16 +194,7 @@ class DerivationManagerImpl(
val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) }
if (toDerive.isEmpty()) return null
currencyList.find { it is WalletModelCurrency.Blockchain && it.blockchain == Blockchain.Cardano }
?.let { currency ->
currency.derivationPath?.let {
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
}
}
return DerivationData(
derivations = mapKeyOfWalletPublicKey to toDerive,
)
return TokensMiddleware.DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive)
}
/**

View file

@ -490,7 +490,7 @@
<string name="user_wallet_list_unlock_all">Разблокировать все с %s</string>
<string name="wallet_address_button_explore">История транзакций</string>
<string name="wallet_balance_blockchain_unreachable">Сеть недоступна</string>
<string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступен. Попробуй позже.</string>
<string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступен. Попробуйте позже.</string>
<string name="wallet_balance_loading">Баланс загружается…</string>
<string name="wallet_balance_missing_derivation">Отсканируйте карту</string>
<string name="wallet_balance_tx_in_progress">Транзакция подтверждается…</string>

View file

@ -7,7 +7,6 @@ import com.tangem.common.CardFilter
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.core.Config
import com.tangem.sdk.extensions.initWithBiometrics
import java.lang.ref.WeakReference
import javax.inject.Inject
import javax.inject.Singleton
@ -20,17 +19,18 @@ import javax.inject.Singleton
internal class DefaultCardSdkProvider @Inject constructor() : CardSdkProvider, CardSdkLifecycleObserver {
override val sdk: TangemSdk
get() = requireNotNull(value = _sdk?.get()) { "Impossible to get the TangemSdk when activity is destroyed" }
get() = requireNotNull(value = _sdk) { "Impossible to get the TangemSdk when activity is destroyed" }
private var _sdk: WeakReference<TangemSdk?>? = null
private var _sdk: TangemSdk? = null
override fun onCreate(context: Context) {
_sdk = WeakReference(TangemSdk.initWithBiometrics(activity = context as FragmentActivity, config = config))
_sdk = TangemSdk.initWithBiometrics(activity = context as FragmentActivity, config = config)
}
override fun onDestroy(context: Context) {
// Commented out to prevent crash on getting sdk when it's null.
// FIXME: We still should find the real cause and fix it properly.
// idea: pass everywhere DefaultCardSdkProvider instead sdk to reach lazy access to sdk property
// _sdk = null
}

View file

@ -32,6 +32,7 @@ compose-navigation = "2.5.3"
compose-accompanist = "0.30.1"
compose-paging = "3.2.0"
compose-reorderable = "0.9.6"
compoese-lifecycle-runtime = "2.6.1"
# endregion Compose
# region Other libraries
@ -80,7 +81,7 @@ okHttp-prettyLogging = "3.1.0"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "release-app_4.11-348"
tangemBlockchainSdk = "release-app_4.11-349"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_4.11-295"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds
@ -132,7 +133,7 @@ androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.r
lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidxLifecycle" }
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
lifecycle-viewModel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" }
lifecycle-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" }
lifecycle-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "compoese-lifecycle-runtime" }
# region AndroidX
# region Compose