Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-02 18:54:22 +04:00
parent b73b6c3fe7
commit d5299f8094
15 changed files with 119 additions and 10 deletions

View file

@ -54,6 +54,7 @@ import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.api.TangemApiServiceLogging
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler
@ -276,6 +277,10 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
createNetworkLoggingInterceptor(),
ChuckerInterceptor(this),
)
TangemApiServiceLogging.addInterceptors(
createNetworkLoggingInterceptor(),
ChuckerInterceptor(this),
)
}
derivationsFinder = DerivationsFinder(

View file

@ -15,6 +15,12 @@ import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.crypto.bip39.Wordlist
import com.tangem.data.card.sdk.CardSdkOwner
import com.tangem.data.card.sdk.CardSdkProvider
import com.tangem.datasource.api.common.config.ApiConfig
import com.tangem.datasource.api.common.config.ApiEnvironment
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectMap
import com.tangem.sdk.DefaultSessionViewDelegate
import com.tangem.sdk.api.featuretoggles.CardSdkFeatureToggles
import com.tangem.sdk.extensions.*
@ -23,6 +29,11 @@ import com.tangem.sdk.nfc.NfcManager
import com.tangem.sdk.storage.create
import com.tangem.tap.foregroundActivityObserver
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
import javax.inject.Singleton
@ -37,6 +48,8 @@ internal class DefaultCardSdkProvider @Inject constructor(
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
private val dispatchers: CoroutineDispatcherProvider,
private val cardSdkFeatureToggles: CardSdkFeatureToggles,
private val apiConfigsManager: ApiConfigsManager,
appPreferencesStore: AppPreferencesStore,
) : CardSdkProvider, CardSdkOwner {
private val observer = Observer()
@ -46,6 +59,17 @@ internal class DefaultCardSdkProvider @Inject constructor(
override val sdk: TangemSdk
get() = holder?.sdk ?: tryToRegisterWithForegroundActivity()
init {
appPreferencesStore.getObjectMap<ApiEnvironment>(PreferencesKeys.apiConfigsEnvironmentKey)
.map { it[ApiConfig.ID.TangemCardSdk.name] == ApiEnvironment.PROD }
.onEach { isProd ->
holder?.let {
it.sdk.config.isTangemAttestationProdEnv = isProd
}
}
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.main))
}
override fun register(activity: FragmentActivity) = runBlocking(dispatchers.mainImmediate) {
if (activity.isDestroyed || activity.isFinishing || activity.isChangingConfigurations) {
val message = "Tangem SDK owner registration skipped: activity is destroyed or finishing"
@ -133,6 +157,9 @@ internal class DefaultCardSdkProvider @Inject constructor(
wordlist = Wordlist.getWordlist(activity),
config = config.apply {
isNewOnlineAttestationEnabled = cardSdkFeatureToggles.isNewAttestationEnabled
val apiConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemCardSdk)
isTangemAttestationProdEnv = apiConfig.environment == ApiEnvironment.PROD
},
)
@ -186,7 +213,6 @@ internal class DefaultCardSdkProvider @Inject constructor(
val config = Config(
linkedTerminal = true,
allowUntrustedCards = true,
filter = CardFilter(
allowedCardTypes = FirmwareVersion.FirmwareType.entries.toList(),
maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33),

View file

@ -32,6 +32,11 @@ object Wallet2MockContent : MockContent {
name = "TANGEM 2.0",
publicKey = byteArrayOf(2, -120, 89, -52, -60, 36, -73, -17, 103, 107, -110, -36, 3, 110, -122, 72, 43, -38, 8, 30, -50, 25, -23, -17, 38, 94, 5, -112, -20, 9, 54, -24, -32),
),
manufacturer = Card.Manufacturer(
name = "TANGEM SDK",
manufactureDate = Date(1743759687),
signature = byteArrayOf(),
),
walletCurves = listOf(
EllipticCurve.Secp256k1,
EllipticCurve.Ed25519,

View file

@ -32,6 +32,11 @@ object WalletMockContent : MockContent {
name = "TANGEM AG",
publicKey = byteArrayOf(3, 86, -25, -61, 55, 99, 41, -33, -82, 115, -120, -33, 22, -107, 103, 3, -122, 16, 60, -110, 72, 106, -121, 100, 79, -87, -27, 18, -55, -49, 78, -110, -2),
),
manufacturer = Card.Manufacturer(
name = "TANGEM",
manufactureDate = Date(1743759687),
signature = byteArrayOf(),
),
walletCurves = listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519, EllipticCurve.Bls12381G2Aug),
firmwareVersion = FirmwareVersion(
major = 4,

View file

@ -26,6 +26,7 @@ sealed class ApiConfig {
StakeKit,
TangemVisaAuth,
TangemVisa,
TangemCardSdk,
}
private fun initializeId(): ID {
@ -35,6 +36,7 @@ sealed class ApiConfig {
is StakeKit -> ID.StakeKit
is TangemVisaAuth -> ID.TangemVisaAuth
is TangemVisa -> ID.TangemVisa
is TangemCardSdk -> ID.TangemCardSdk
}
}

View file

@ -0,0 +1,26 @@
package com.tangem.datasource.api.common.config
/**
* Tangem API [ApiConfig] that used in CardSDK
*
[REDACTED_AUTHOR]
*/
internal class TangemCardSdk : ApiConfig() {
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.PROD
override val environmentConfigs: List<ApiEnvironmentConfig> = listOf(
createDevEnvironment(),
createProdEnvironment(),
)
private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
environment = ApiEnvironment.DEV,
baseUrl = "[REDACTED_ENV_URL]",
)
private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
environment = ApiEnvironment.PROD,
baseUrl = "https://api.tangem-tech.com/",
)
}

View file

@ -11,7 +11,7 @@ import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
interface ApiConfigsManager {
/** Initialize resources */
suspend fun initialize() {}
fun initialize() {}
/** Get environment config by [id] */
fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig

View file

@ -9,6 +9,7 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectMap
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.*
/**
@ -28,7 +29,7 @@ internal class DevApiConfigsManager(
private val _apiConfigs = MutableStateFlow(value = apiConfigs.associateWith { it.defaultEnvironment })
override suspend fun initialize() {
override fun initialize() {
// We can't use appPreferencesStore.getObjectMap as base flow,
// because we should keep possibility to work with configs synchronous.
// See [getBaseUrl]
@ -42,7 +43,7 @@ internal class DevApiConfigsManager(
}
}
}
.launchIn(CoroutineScope(dispatchers.main))
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.main))
}
override fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig {

View file

@ -2,9 +2,6 @@ package com.tangem.datasource.di
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.common.config.*
import com.tangem.datasource.api.common.config.Express
import com.tangem.datasource.api.common.config.StakeKit
import com.tangem.datasource.api.common.config.TangemTech
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.lib.auth.ExpressAuthProvider
import com.tangem.lib.auth.StakeKitAuthProvider
@ -48,4 +45,8 @@ internal object ApiConfigsModule {
@Provides
@IntoSet
fun provideTangemVisaConfig(appVersionProvider: AppVersionProvider): ApiConfig = TangemVisa(appVersionProvider)
@Provides
@IntoSet
fun provideTangemCardSdkConfig(): ApiConfig = TangemCardSdk()
}

View file

@ -89,6 +89,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
is StakeKit -> createStakeKitModel()
is TangemVisaAuth -> createVisaAuthModel()
is TangemVisa -> createVisaModel()
is TangemCardSdk -> createTangemCardSdkModel()
}
}
@ -204,6 +205,16 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
)
}
private fun createTangemCardSdkModel(): Model {
return Model(
id = ApiConfig.ID.TangemCardSdk,
expected = ApiEnvironmentConfig(
environment = ApiEnvironment.PROD,
baseUrl = "https://api.tangem-tech.com/",
),
)
}
private fun String.checkHeaderValueOrEmpty(): String {
for (i in this.indices) {
val c = this[i]

View file

@ -6,8 +6,8 @@ import com.tangem.common.UserCodeType
import com.tangem.common.core.CardIdDisplayFormat
import com.tangem.common.core.UserCodeRequestPolicy
import com.tangem.data.card.sdk.CardSdkProvider
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.card.models.TwinKey
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.scan.ProductType
/**
@ -70,4 +70,8 @@ internal class DefaultCardSdkConfigRepository(
override fun setLinkedTerminal(isLinked: Boolean?) {
sdk.config.linkedTerminal = isLinked
}
override fun setTangemApiProdEnvFlag(flag: Boolean) {
sdk.config.isTangemAttestationProdEnv = flag
}
}

View file

@ -36,4 +36,7 @@ interface CardSdkConfigRepository {
/** Set linked terminal by [isLinked] */
fun setLinkedTerminal(isLinked: Boolean?)
/** Set [flag] that determines whether to use Prod environment for Tangem API */
fun setTangemApiProdEnvFlag(flag: Boolean)
}

View file

@ -32,6 +32,7 @@ dependencies {
/** Domain modules */
implementation(projects.domain.appTheme)
implementation(projects.domain.appTheme.models)
implementation(projects.domain.card)
implementation(projects.domain.feedback)
/** Other libraries */

View file

@ -6,6 +6,7 @@ import com.tangem.datasource.api.common.config.ApiConfig
import com.tangem.datasource.api.common.config.ApiEnvironment
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.api.common.config.managers.MutableApiConfigsManager
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.feature.tester.impl.BuildConfig
import com.tangem.feature.tester.impl.R
import com.tangem.feature.tester.presentation.environments.state.EnvironmentTogglesScreenUM
@ -28,6 +29,7 @@ import javax.inject.Inject
@HiltViewModel
internal class EnvironmentsTogglesViewModel @Inject constructor(
apiConfigsManager: ApiConfigsManager,
private val cardSdkConfigRepository: CardSdkConfigRepository,
) : ViewModel() {
/** Current ui state */
@ -92,7 +94,24 @@ internal class EnvironmentsTogglesViewModel @Inject constructor(
private fun onToggleValueChange(id: String, name: String) {
viewModelScope.launch {
mutableApiConfigsManager.changeEnvironment(id = id, environment = ApiEnvironment.valueOf(name))
val environment = ApiEnvironment.valueOf(name)
handleIfTangemCardSdkConfig(id = id, environment = environment)
mutableApiConfigsManager.changeEnvironment(id = id, environment = environment)
}
}
/** Special logic for [ApiConfig.ID.TangemCardSdk] */
private fun handleIfTangemCardSdkConfig(id: String, environment: ApiEnvironment) {
if (id == ApiConfig.ID.TangemCardSdk.name) {
cardSdkConfigRepository.setTangemApiProdEnvFlag(
flag = when (environment) {
ApiEnvironment.PROD -> true
ApiEnvironment.DEV -> false
else -> false
},
)
}
}
}

View file

@ -1,7 +1,7 @@
[versions]
tangemBlockchainSdk = "develop-1000"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-445"
tangemCardSdk = "develop-449"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
tangemVico = "2.0.0-alpha.25-tangem-developments8"
#tangemVico = "0.0.1" # Keep it! - used for local builds ^