diff --git a/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt
index 62b73d6c4b..863e387a71 100644
--- a/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt
+++ b/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt
@@ -44,7 +44,7 @@ class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
}
val contactSupportButton: KNode = child {
hasTestTag(DetailsScreenTestTags.SCREEN_ITEM)
- hasText(getResourceString(R.string.details_row_title_contact_to_support))
+ hasText(getResourceString(R.string.common_contact_support))
}
val toSButton: KNode = child {
hasTestTag(DetailsScreenTestTags.SCREEN_ITEM)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 60e77f7ace..ef63e16db7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -279,6 +279,17 @@
android:host="onboard-visa"
android:scheme="tangem" />
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/String.kt b/app/src/main/java/com/tangem/tap/common/extensions/String.kt
deleted file mode 100644
index 0396cf84fc..0000000000
--- a/app/src/main/java/com/tangem/tap/common/extensions/String.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.tangem.tap.common.extensions
-
-fun String.removePrefixOrNull(prefix: String): String? = when {
- startsWith(prefix) -> substring(prefix.length)
- else -> null
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt b/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt
index 9ea7cbeb84..4f7f4a0166 100644
--- a/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt
+++ b/app/src/main/java/com/tangem/tap/data/DefaultTangemPayStorage.kt
@@ -18,8 +18,7 @@ import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
-private const val DEFAULT_KEY = "tangem_pay_default_key"
-private const val ORDER_ID_KEY = "tangem_pay_order_id_key"
+private const val AUTH_TOKENS_DEFAULT_KEY = "tangem_pay_default_key"
private const val WITHDRAW_ORDER_ID_KEY = "tangem_pay_withdraw_order_id_key"
@Singleton
@@ -42,13 +41,15 @@ internal class DefaultTangemPayStorage @Inject constructor(
override suspend fun storeCustomerWalletAddress(userWalletId: UserWalletId, customerWalletAddress: String) {
withContext(dispatcherProvider.io) {
- secureStorage.store(key = createCustomerAddressKey(userWalletId), value = customerWalletAddress)
+ appPreferencesStore
+ .store(PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId), customerWalletAddress)
}
}
override suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String? {
return withContext(dispatcherProvider.io) {
- secureStorage.getAsString(createCustomerAddressKey(userWalletId))
+ appPreferencesStore.getSyncOrNull(key = PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId))
+ .takeIf { !it.isNullOrEmpty() }
}
}
@@ -58,28 +59,28 @@ internal class DefaultTangemPayStorage @Inject constructor(
secureStorage.store(
json.encodeToByteArray(throwOnInvalidSequence = true),
- createKey(customerWalletAddress),
+ createAuthTokensKey(customerWalletAddress),
)
}
override suspend fun getAuthTokens(customerWalletAddress: String): TangemPayAuthTokens? =
withContext(dispatcherProvider.io) {
- secureStorage.get(createKey(customerWalletAddress))
+ secureStorage.get(createAuthTokensKey(customerWalletAddress))
?.decodeToString(throwOnInvalidSequence = true)
?.let(tokensAdapter::fromJson)
}
override suspend fun storeOrderId(customerWalletAddress: String, orderId: String) {
withContext(dispatcherProvider.io) {
- secureStorage.store(
- orderId.encodeToByteArray(throwOnInvalidSequence = true),
- createOrderIdKey(customerWalletAddress),
- )
+ appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), orderId)
}
}
- override suspend fun getOrderId(customerWalletAddress: String): String? = withContext(dispatcherProvider.io) {
- secureStorage.get(createOrderIdKey(customerWalletAddress))?.decodeToString(throwOnInvalidSequence = true)
+ override suspend fun getOrderId(customerWalletAddress: String): String? {
+ return withContext(dispatcherProvider.io) {
+ appPreferencesStore.getSyncOrNull(key = PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress))
+ .takeIf { !it.isNullOrEmpty() }
+ }
}
override suspend fun getAddToWalletDone(customerWalletAddress: String): Boolean {
@@ -97,7 +98,7 @@ internal class DefaultTangemPayStorage @Inject constructor(
}
override suspend fun clearOrderId(customerWalletAddress: String) = withContext(dispatcherProvider.io) {
- secureStorage.delete(createOrderIdKey(customerWalletAddress))
+ appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "")
}
override suspend fun storeCheckCustomerWalletResult(userWalletId: UserWalletId, isPaeraCustomer: Boolean) {
@@ -110,9 +111,9 @@ internal class DefaultTangemPayStorage @Inject constructor(
override suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) =
withContext(dispatcherProvider.io) {
- secureStorage.delete(createCustomerAddressKey(userWalletId))
- secureStorage.delete(createKey(customerWalletAddress))
- secureStorage.delete(createOrderIdKey(customerWalletAddress))
+ secureStorage.delete(createAuthTokensKey(customerWalletAddress))
+ appPreferencesStore.store(PreferencesKeys.getTangemPayCustomerWalletAddressKey(userWalletId), "")
+ appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "")
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), false)
}
@@ -143,11 +144,7 @@ internal class DefaultTangemPayStorage @Inject constructor(
}
}
- private fun createCustomerAddressKey(userWalletId: UserWalletId): String = userWalletId.stringValue
-
- private fun createKey(address: String): String = "${DEFAULT_KEY}_$address"
-
- private fun createOrderIdKey(address: String): String = "${ORDER_ID_KEY}_$address"
+ private fun createAuthTokensKey(address: String): String = "${AUTH_TOKENS_DEFAULT_KEY}_$address"
private fun createWithdrawOrderIdKey(userWalletId: UserWalletId): String = "${WITHDRAW_ORDER_ID_KEY}_$userWalletId"
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/TangemPaySignWithdrawalHashTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/TangemPaySignWithdrawalHashTask.kt
index faf32983a0..d5bc3b64c1 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/TangemPaySignWithdrawalHashTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/TangemPaySignWithdrawalHashTask.kt
@@ -15,7 +15,6 @@ import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.visa.error.VisaActivationError
-import com.tangem.operations.ScanTask
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
import com.tangem.operations.sign.SignHashCommand
@@ -92,25 +91,7 @@ class TangemPaySignWithdrawalHashTask(
?: targetWalletPublicKey.toDecompressedPublicKey(),
).asRSVLegacyEVM().toHexString().lowercase()
- scanCard(
- session = session,
- callback = callback,
- signedData = rsvSignature,
- )
- }
- is CompletionResult.Failure -> {
- callback(CompletionResult.Failure(result.error))
- }
- }
- }
- }
-
- private fun scanCard(signedData: String, session: CardSession, callback: CompletionCallback) {
- val scanTask = ScanTask()
- scanTask.run(session) { result ->
- when (result) {
- is CompletionResult.Success -> {
- callback(CompletionResult.Success(signedData))
+ callback(CompletionResult.Success(rsvSignature))
}
is CompletionResult.Failure -> {
callback(CompletionResult.Failure(result.error))
diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt
index e0b08af719..e9bc518930 100644
--- a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt
+++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt
@@ -112,6 +112,7 @@ internal object UserWalletsListManagerModule {
publicInformationRepository = publicInformationRepository,
sensitiveInformationRepository = sensitiveInformationRepository,
selectedUserWalletRepository = selectedUserWalletRepository,
+ dispatcherProvider = dispatchers,
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt
index 85b958f1d1..8183415675 100644
--- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt
@@ -16,19 +16,29 @@ import com.tangem.tap.domain.userWalletList.utils.encryptionKey
import com.tangem.tap.domain.userWalletList.utils.lockAll
import com.tangem.tap.domain.userWalletList.utils.toUserWallets
import com.tangem.tap.domain.userWalletList.utils.updateWith
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
import timber.log.Timber
+@Suppress("LargeClass")
@OptIn(ExperimentalCoroutinesApi::class)
internal class BiometricUserWalletsListManager(
private val keysRepository: UserWalletsKeysRepository,
private val publicInformationRepository: UserWalletsPublicInformationRepository,
private val sensitiveInformationRepository: UserWalletsSensitiveInformationRepository,
private val selectedUserWalletRepository: SelectedUserWalletRepository,
+ private val dispatcherProvider: CoroutineDispatcherProvider,
) : UserWalletsListManager.Lockable {
private val state = MutableStateFlow(State())
+ private var hasSavedWallets: Boolean? = null
+ private val savedWalletMutex = Mutex()
+
override val isLockable: Boolean = true
override val userWallets: Flow>
@@ -61,7 +71,23 @@ internal class BiometricUserWalletsListManager(
get() = state.value.isLocked
override val hasUserWallets: Boolean
- get() = keysRepository.hasSavedEncryptionKeys()
+ get() {
+ return runBlocking {
+ // workaround to avoid calling hasSavedEncryptionKeys many times because of performance
+ savedWalletMutex.withLock {
+ Timber.i("Checking if user has saved wallets")
+ val hasSavedWalletsLocal = hasSavedWallets
+ if (hasSavedWalletsLocal == null || !hasSavedWalletsLocal) {
+ val hasKeys = keysRepository.hasSavedEncryptionKeys()
+ hasSavedWallets = hasKeys
+ hasKeys
+ } else {
+ Timber.i("User has saved wallets (from cache)")
+ true
+ }
+ }
+ }
+ }
override val walletsCount: Int
get() = state.value.userWallets.size
@@ -72,23 +98,25 @@ internal class BiometricUserWalletsListManager(
.distinctUntilChanged()
override suspend fun unlock(type: UnlockType): CompletionResult {
- return unlockAndSetSelectedUserWallet(type)
- .mapFailure { error ->
- Timber.e(error, "Unable to unlock user wallets")
- if (error is UserWalletsListError) {
- error
- } else {
- UserWalletsListError.UnableToUnlockUserWallets(error)
+ return withContext(dispatcherProvider.io) {
+ unlockAndSetSelectedUserWallet(type)
+ .mapFailure { error ->
+ Timber.e(error, "Unable to unlock user wallets")
+ if (error is UserWalletsListError) {
+ error
+ } else {
+ UserWalletsListError.UnableToUnlockUserWallets(error)
+ }
}
- }
- .map { selectedUserWallet ->
- if (selectedUserWallet == null || selectedUserWallet.isLocked) {
- Timber.e("Unable to find selected user wallet")
- throw UserWalletsListError.NoUserWalletSelected
- } else {
- selectedUserWallet
+ .map { selectedUserWallet ->
+ if (selectedUserWallet == null || selectedUserWallet.isLocked) {
+ Timber.e("Unable to find selected user wallet")
+ throw UserWalletsListError.NoUserWalletSelected
+ } else {
+ selectedUserWallet
+ }
}
- }
+ }
}
override fun lock() {
@@ -120,18 +148,20 @@ internal class BiometricUserWalletsListManager(
}
override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult {
- return if (canOverride) {
- saveInternal(userWallet, changeSelectedUserWallet = true, canOverridePublicInfo = false)
- } else {
- val isWalletSaved = state.value.userWallets
- .any {
- it.walletId == userWallet.walletId
- }
-
- if (isWalletSaved) {
- CompletionResult.Failure(UserWalletsListError.WalletAlreadySaved)
- } else {
+ return withContext(dispatcherProvider.io) {
+ if (canOverride) {
saveInternal(userWallet, changeSelectedUserWallet = true, canOverridePublicInfo = false)
+ } else {
+ val isWalletSaved = state.value.userWallets
+ .any {
+ it.walletId == userWallet.walletId
+ }
+
+ if (isWalletSaved) {
+ CompletionResult.Failure(UserWalletsListError.WalletAlreadySaved)
+ } else {
+ saveInternal(userWallet, changeSelectedUserWallet = true, canOverridePublicInfo = false)
+ }
}
}
}
@@ -140,16 +170,18 @@ internal class BiometricUserWalletsListManager(
userWalletId: UserWalletId,
update: suspend (UserWallet) -> UserWallet,
): CompletionResult {
- return get(userWalletId)
- .map { storedUserWallet ->
- update(storedUserWallet)
- }
- .flatMap { updatedUserWallet ->
- saveInternal(updatedUserWallet, changeSelectedUserWallet = false, canOverridePublicInfo = true)
- }
- .flatMap {
- get(userWalletId)
- }
+ return withContext(dispatcherProvider.io) {
+ get(userWalletId)
+ .map { storedUserWallet ->
+ update(storedUserWallet)
+ }
+ .flatMap { updatedUserWallet ->
+ saveInternal(updatedUserWallet, changeSelectedUserWallet = false, canOverridePublicInfo = true)
+ }
+ .flatMap {
+ get(userWalletId)
+ }
+ }
}
override suspend fun delete(userWalletIds: List): CompletionResult {
@@ -166,41 +198,48 @@ internal class BiometricUserWalletsListManager(
return clear()
}
- return sensitiveInformationRepository.delete(idsToRemove)
- .flatMap { publicInformationRepository.delete(idsToRemove) }
- .map { keysRepository.delete(idsToRemove) }
- .map {
- state.update { prevState ->
- val remainingWallets = prevState.userWallets.filter { it.walletId !in idsToRemove }
+ return withContext(dispatcherProvider.io) {
+ sensitiveInformationRepository.delete(idsToRemove)
+ .flatMap { publicInformationRepository.delete(idsToRemove) }
+ .map { keysRepository.delete(idsToRemove) }
+ .map {
+ state.update { prevState ->
+ val remainingWallets = prevState.userWallets.filter { it.walletId !in idsToRemove }
- val isSelectedWalletDeleted = prevState.selectedUserWalletId in idsToRemove
- val newSelectedUserWallet = findOrSetSelectedWallet(
- prevSelectedWalletId = prevState.selectedUserWalletId,
- prevSelectedWalletIndex = prevState.userWallets.indexOfFirst {
- it.walletId == prevState.selectedUserWalletId
- },
- userWallets = remainingWallets,
- ignorePrevSelectedWallet = isSelectedWalletDeleted,
- )
+ val isSelectedWalletDeleted = prevState.selectedUserWalletId in idsToRemove
+ val newSelectedUserWallet = findOrSetSelectedWallet(
+ prevSelectedWalletId = prevState.selectedUserWalletId,
+ prevSelectedWalletIndex = prevState.userWallets.indexOfFirst {
+ it.walletId == prevState.selectedUserWalletId
+ },
+ userWallets = remainingWallets,
+ ignorePrevSelectedWallet = isSelectedWalletDeleted,
+ )
- prevState.copy(
- encryptionKeys = prevState.encryptionKeys.filter { it.walletId !in idsToRemove },
- userWallets = remainingWallets,
- isLocked = remainingWallets.any { it.isLocked },
- selectedUserWalletId = newSelectedUserWallet?.walletId,
- )
+ prevState.copy(
+ encryptionKeys = prevState.encryptionKeys.filter { it.walletId !in idsToRemove },
+ userWallets = remainingWallets,
+ isLocked = remainingWallets.any { it.isLocked },
+ selectedUserWalletId = newSelectedUserWallet?.walletId,
+ )
+ }
}
- }
+ }
}
override suspend fun clear(): CompletionResult {
- return sensitiveInformationRepository.clear()
- .flatMap { publicInformationRepository.clear() }
- .map {
- keysRepository.clear()
- selectedUserWalletRepository.set(null)
- state.value = State()
- }
+ savedWalletMutex.withLock {
+ hasSavedWallets = null
+ }
+ return withContext(dispatcherProvider.io) {
+ sensitiveInformationRepository.clear()
+ .flatMap { publicInformationRepository.clear() }
+ .map {
+ keysRepository.clear()
+ selectedUserWalletRepository.set(null)
+ state.value = State()
+ }
+ }
}
override suspend fun get(userWalletId: UserWalletId): CompletionResult {
diff --git a/app/src/main/java/com/tangem/tap/routing/RootContent.kt b/app/src/main/java/com/tangem/tap/routing/RootContent.kt
index b95777a48b..add9354694 100644
--- a/app/src/main/java/com/tangem/tap/routing/RootContent.kt
+++ b/app/src/main/java/com/tangem/tap/routing/RootContent.kt
@@ -74,6 +74,7 @@ internal fun RootContent(
startActivity(context, instance.intent, Bundle.EMPTY)
}
}
+ RoutingComponent.Child.DummyComponent -> Unit
}
}
diff --git a/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt
index 22bb2b0b4a..05486d63cc 100644
--- a/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt
+++ b/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt
@@ -20,6 +20,8 @@ internal interface RoutingComponent : ComposableContentComponent {
data class ComposableComponent(
val component: ComposableContentComponent,
) : Child()
+
+ data object DummyComponent : Child()
}
interface Factory {
diff --git a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt
index 7025664f0b..e51b49f725 100644
--- a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt
+++ b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt
@@ -4,12 +4,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.childStack
+import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.value.Value
import com.arkivanov.decompose.value.subscribe
import com.arkivanov.essenty.lifecycle.subscribe
import com.google.android.material.snackbar.Snackbar
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.entity.InitScreenLaunchMode
+import com.tangem.core.analytics.api.AnalyticsExceptionHandler
+import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.context.childByContext
@@ -42,6 +45,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
+import timber.log.Timber
@Suppress("LongParameterList")
internal class DefaultRoutingComponent @AssistedInject constructor(
@@ -59,6 +63,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
private val userWalletsListRepository: UserWalletsListRepository,
private val cardRepository: CardRepository,
private val onboardingRepository: OnboardingRepository,
+ private val analyticsExceptionHandler: AnalyticsExceptionHandler,
) : RoutingComponent,
AppComponentContext by context,
SnackbarHandler {
@@ -73,13 +78,23 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
.create(child("hotAccessCodeRequestComponent"), Unit)
}
+ private val navigation = navigationProvider.getOrCreateTyped()
+
private val stack: Value> = childStack(
- source = navigationProvider.getOrCreateTyped(),
+ source = navigation,
initialStack = { getInitialStackOrInit() },
serializer = null, // AppRoute.serializer(), // Disabled until Nav refactoring completes
handleBackButton = true,
childFactory = { route, childContext ->
- childFactory.createChild(route, childByContext(childContext))
+ try {
+ childFactory.createChild(route, childByContext(childContext))
+ } catch (e: Exception) {
+ Timber.e(e, "App Router Failed")
+ analyticsExceptionHandler.sendException(
+ ExceptionAnalyticsEvent(exception = e, params = mapOf("Category" to "App Routing")),
+ )
+ Child.DummyComponent
+ }
},
)
@@ -89,6 +104,12 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
appRouterConfig.snackbarHandler = this
stack.subscribe(lifecycle) { stack ->
+ // Handle DummyComponent by popping it immediately
+ if (stack.active.instance is Child.DummyComponent) {
+ navigation.pop()
+ return@subscribe
+ }
+
val stackItems = stack.items.map { it.configuration }
wcRoutingComponent.onAppRouteChange(stack.active.configuration)
diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
index 57dc3d376e..a3a9cf4047 100644
--- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
+++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
@@ -660,8 +660,13 @@ internal class ChildFactory @Inject constructor(
createComponentChild(
context = context,
params = when (val mode = route.mode) {
- is AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding -> ContinueOnboarding
- is AppRoute.TangemPayOnboarding.Mode.Deeplink -> Deeplink(deeplink = mode.deeplink)
+ is AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding -> ContinueOnboarding(
+ userWalletId = mode.userWalletId,
+ )
+ is AppRoute.TangemPayOnboarding.Mode.Deeplink -> Deeplink(
+ deeplink = mode.deeplink,
+ userWalletId = mode.userWalletId,
+ )
},
componentFactory = tangemPayOnboardingComponentFactory,
)
diff --git a/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt
index 45a9e27528..097b57b518 100644
--- a/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt
+++ b/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt
@@ -102,6 +102,7 @@ internal class DeepLinkFactory @Inject constructor(
private fun launchDeepLink(deeplinkUri: Uri, coroutineScope: CoroutineScope, isFromOnNewIntent: Boolean) {
when (deeplinkUri.scheme) {
+ DeepLinkScheme.Https.scheme -> handleHttpDeepLinks(deeplinkUri)
DeepLinkScheme.Tangem.scheme -> handleTangemDeepLinks(deeplinkUri, coroutineScope, isFromOnNewIntent)
DeepLinkScheme.WalletConnect.scheme -> walletConnectDeepLink.create(deeplinkUri)
else -> {
@@ -115,6 +116,13 @@ internal class DeepLinkFactory @Inject constructor(
}
}
+ private fun handleHttpDeepLinks(deeplinkUri: Uri) {
+ if (deeplinkUri.host == DeepLinkRoute.PayApp.host && deeplinkUri.path?.startsWith("/pay-app") == true) {
+ onboardVisaDeepLink.create(deeplinkUri)
+ return
+ }
+ }
+
@Suppress("CyclomaticComplexMethod")
private fun handleTangemDeepLinks(deeplinkUri: Uri, coroutineScope: CoroutineScope, isFromOnNewIntent: Boolean) {
val queryParams = getQueryParams(deeplinkUri)
diff --git a/app/src/test/kotlin/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManagerTest.kt b/app/src/test/kotlin/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManagerTest.kt
index 3d7fa216d8..7e54ffc6bd 100644
--- a/app/src/test/kotlin/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManagerTest.kt
+++ b/app/src/test/kotlin/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManagerTest.kt
@@ -23,6 +23,7 @@ internal class BiometricUserWalletsListManagerTest(private val model: Model) {
publicInformationRepository = mockk(),
sensitiveInformationRepository = mockk(),
selectedUserWalletRepository = mockk(),
+ dispatcherProvider = mockk(),
)
@Test
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
index f1c07144f6..4e22012973 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
@@ -431,10 +431,13 @@ sealed class AppRoute(val path: String) : Route {
@Serializable
data class Deeplink(
val deeplink: String,
+ val userWalletId: UserWalletId?,
) : Mode()
@Serializable
- object ContinueOnboarding : Mode()
+ data class ContinueOnboarding(
+ val userWalletId: UserWalletId?,
+ ) : Mode()
}
}
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt
index 9b5ba24bfa..d57eb5e7f0 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt
@@ -63,9 +63,14 @@ sealed class DeepLinkRoute {
data object OnboardVisa : DeepLinkRoute() {
override val host: String = "onboard-visa"
}
+
+ data object PayApp : DeepLinkRoute() {
+ override val host: String = "tangem.com"
+ }
}
enum class DeepLinkScheme(val scheme: String) {
Tangem(scheme = "tangem"),
WalletConnect(scheme = "wc"),
+ Https(scheme = "https"),
}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt
index 47233d7291..1941c34ff9 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt
@@ -35,8 +35,8 @@ import com.tangem.core.ui.extensions.isNullOrEmpty
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
-import com.tangem.core.ui.utils.singleEvent
import com.tangem.core.ui.test.SendScreenTestTags
+import com.tangem.core.ui.utils.singleEvent
@Composable
fun NavigationButtonsBlock(
@@ -78,7 +78,9 @@ fun NavigationButtonsBlockV2(
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
PreviousButton(navigationUM?.prevButton)
- NavigationPrimaryButton(navigationUM?.primaryButton, modifier = Modifier.weight(1f))
+ key(navigationUM?.source) {
+ NavigationPrimaryButton(navigationUM?.primaryButton, modifier = Modifier.weight(1f))
+ }
}
}
}
diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationUM.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationUM.kt
index dcde9ac935..dc1953b1d9 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationUM.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationUM.kt
@@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.TextReference
@Immutable
sealed class NavigationUM {
data class Content(
+ val source: String,
val title: TextReference,
val subtitle: TextReference?,
@DrawableRes val backIconRes: Int,
diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt
index 516a15539c..ce46ff400f 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt
@@ -49,7 +49,7 @@ class TokenItemStateConverter(
private val iconStateProvider: (CryptoCurrencyStatus) -> CurrencyIconState = {
CryptoCurrencyToIconStateConverter().convert(it)
},
- private val onApyLabelClick: ((CryptoCurrencyStatus, String) -> Unit)? = null,
+ private val onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)? = null,
private val onYieldPromoCloseClick: (() -> Unit)? = null,
private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = { currencyStatus ->
createTitleState(
@@ -178,7 +178,7 @@ class TokenItemStateConverter(
currencyStatus: CryptoCurrencyStatus,
yieldModuleApyMap: Map,
stakingApyMap: Map>,
- onApyLabelClick: ((CryptoCurrencyStatus, String) -> Unit)?,
+ onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)?,
): TokenItemState.TitleState {
return when (val value = currencyStatus.value) {
is CryptoCurrencyStatus.Loading,
@@ -204,7 +204,7 @@ class TokenItemStateConverter(
earnApy = apyInfo?.text,
earnApyIsActive = apyInfo?.isActive == true,
onApyLabelClick = if (apyInfo?.apy != null && onApyLabelClick != null) {
- { onApyLabelClick.invoke(currencyStatus, apyInfo.apy) }
+ { onApyLabelClick.invoke(currencyStatus, apyInfo.source, apyInfo.apy) }
} else {
null
},
@@ -236,6 +236,7 @@ class TokenItemStateConverter(
),
isActive = isActive,
apy = yieldSupplyApy.toString(),
+ source = ApySource.YIELD_SUPPLY,
)
}
}
@@ -261,6 +262,7 @@ class TokenItemStateConverter(
),
isActive = stakingInfo.isActive,
apy = apyString,
+ source = ApySource.STAKING,
)
}
}
@@ -395,7 +397,7 @@ class TokenItemStateConverter(
status: CryptoCurrencyStatus,
yieldModuleApyMap: Map,
yieldSupplyPromoBannerKey: String?,
- onApyLabelClick: ((CryptoCurrencyStatus, String) -> Unit)?,
+ onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)?,
onYieldPromoCloseClick: (() -> Unit)?,
): TokenItemState.PromoBannerState {
val token = status.currency as? CryptoCurrency.Token ?: return TokenItemState.PromoBannerState.Empty
@@ -413,7 +415,7 @@ class TokenItemStateConverter(
wrappedList(yieldSupplyApy),
),
onPromoBannerClick = {
- onApyLabelClick?.invoke(status, yieldSupplyApy.toString())
+ onApyLabelClick?.invoke(status, ApySource.YIELD_SUPPLY, yieldSupplyApy.toString())
},
onCloseClick = {
onYieldPromoCloseClick?.invoke()
@@ -472,5 +474,11 @@ class TokenItemStateConverter(
val text: TextReference?,
val isActive: Boolean,
val apy: String?,
+ val source: ApySource,
)
+
+ enum class ApySource {
+ STAKING,
+ YIELD_SUPPLY,
+ }
}
\ No newline at end of file
diff --git a/core/config-toggles/detekt-baseline-debug.xml b/core/config-toggles/detekt-baseline-debug.xml
index b0e03ef827..274a75d8d4 100644
--- a/core/config-toggles/detekt-baseline-debug.xml
+++ b/core/config-toggles/detekt-baseline-debug.xml
@@ -2,8 +2,6 @@
- DoubleMutabilityForCollection:DevExcludedBlockchainsManager.kt$DevExcludedBlockchainsManager$private var blockchainTogglesMap: MutableMap<String, Boolean> by Delegates.notNull()
- DoubleMutabilityForCollection:DevFeatureTogglesManager.kt$DevFeatureTogglesManager$private var featureTogglesMap: MutableMap<String, Boolean> by Delegates.notNull()
Indentation:ExcludedBlockchainToggles.kt$ExcludedBlockchainToggles$
Indentation:FeatureToggles.kt$FeatureToggles$
diff --git a/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json b/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json
index b3c1dd8f8d..59809c4e40 100644
--- a/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json
+++ b/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json
@@ -29,7 +29,7 @@
},
{
"name": "zklink",
- "version": "5.31"
+ "version": "undefined"
},
{
"name": "plasma",
diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
index 1d456f1591..03e0a8cfd3 100644
--- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
+++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
@@ -49,7 +49,7 @@
},
{
"name": "NEW_ONRAMP_MAIN_ENABLED",
- "version": "undefined"
+ "version": "5.31.0"
},
{
"name": "ACCOUNTS_FEATURE_ENABLED",
diff --git a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/blockchain/impl/DevExcludedBlockchainsManager.kt b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/blockchain/impl/DevExcludedBlockchainsManager.kt
index 6f6645c865..98311b8d7a 100644
--- a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/blockchain/impl/DevExcludedBlockchainsManager.kt
+++ b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/blockchain/impl/DevExcludedBlockchainsManager.kt
@@ -21,6 +21,8 @@ internal class DevExcludedBlockchainsManager(
) : MutableExcludedBlockchainsManager {
private val fileBlockchainToggles: Map = getFileBlockchainToggles()
+
+ @Suppress("DoubleMutabilityForCollection")
private var blockchainTogglesMap: MutableMap by Delegates.notNull()
override val excludedBlockchainsIds: Set
diff --git a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt
index 79d78151b7..d59bac9d71 100644
--- a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt
+++ b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt
@@ -22,6 +22,8 @@ internal class DevFeatureTogglesManager(
) : MutableFeatureTogglesManager {
private val fileFeatureTogglesMap: Map = getFileFeatureToggles()
+
+ @Suppress("DoubleMutabilityForCollection")
private var featureTogglesMap: MutableMap by Delegates.notNull()
init {
diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
index f3f7fbf07d..037f71a2a6 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
@@ -183,6 +183,12 @@ object PreferencesKeys {
fun getTangemPayAddToWalletKey(customerWalletAddress: String) =
booleanPreferencesKey("tangem_pay_add_to_wallet_done_key_$customerWalletAddress")
+ fun getTangemPayOrderIdKey(customerWalletAddress: String) =
+ stringPreferencesKey("tangem_pay_order_id_key_$customerWalletAddress")
+
+ fun getTangemPayCustomerWalletAddressKey(userWalletId: UserWalletId) =
+ stringPreferencesKey("tangem_pay_customer_wallet_address_key_${userWalletId.stringValue}")
+
fun getTangemPayCheckCustomerByWalletId(userWalletId: UserWalletId) =
booleanPreferencesKey("tangem_pay_check_customer_by_wallet_id_$userWalletId")
diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/NetworkLogsSaveInterceptor.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/NetworkLogsSaveInterceptor.kt
index 58b21f35dd..9e3d88dcb4 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/utils/NetworkLogsSaveInterceptor.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/utils/NetworkLogsSaveInterceptor.kt
@@ -44,7 +44,12 @@ class NetworkLogsSaveInterceptor(
throw e
}
- if (restrictedForLogURLs.contains(request.url.host + request.url.encodedPath)) {
+ val host = request.url.host
+ val path = request.url.encodedPath
+ val isRestrictedUrl = restrictedForLogURLs.contains(host + path)
+ val isRestrictedHost = restrictedForLogHosts.any { host.contains(it) }
+
+ if (isRestrictedUrl || isRestrictedHost) {
logResponseWithEmptyMessage(response, startNs)
} else {
logResponseMessage(response, startNs)
@@ -222,5 +227,8 @@ class NetworkLogsSaveInterceptor(
val restrictedForLogURLs = listOf(
"api.stakek.it/v1/yields/enabled",
)
+ val restrictedForLogHosts = listOf(
+ "us.paera.com",
+ )
}
}
\ No newline at end of file
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 58da791e4e..a009fa485f 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -3,10 +3,14 @@
Ohne Zugangscode ist Deine Wallet nicht geschützt.
Trotzdem überspringen
Zugangscode nicht festgelegt
+ Code ändern
+ Dein Zugangscode dient zum Entsperren Deiner Wallet und zum Schutz des Zugriffs auf Deine Vermögenswerte.
+ Trotzdem verwenden
+ Dieser Zugangscode kann leicht erraten werden
Zugangscode eingeben
Falscher Zugangscode. Deine mobile Wallet wird nach %s weiteren Fehlversuchen gelöscht.
Falscher Zugangscode. Die App wird nach %s weiteren Fehlversuchen gesperrt.
- Falscher Zugangscode.\nBitte warte %s Sekunden und versuche es erneut.
+ Falscher Zugangscode.\n Bitte warte %s Sekunden und versuche es erneut.
Bestätige Deinen Zugangscode, um fortzufahren.
Zugangscode erneut eingeben
Erstelle einen %s-stelliger Zugangscode, um Deine Wallet zu entsperren.
@@ -32,6 +36,7 @@
Du archivierst dieses Konto, kannst es aber jederzeit entarchivieren.
Archivierung...
Konto
+ Ich kann das Konto nicht bearbeiten.
Konto gespeichert
Prämien berücksichtigen
Kontonummer %s – wird zur Adressableitung verwendet.
@@ -139,10 +144,17 @@
Guthaben sind ausgeblendet
Laut den Blockchain-Entwicklern befinden sich der Kaspa-Token derzeit in der Betaphase. Bleibe dran für Updates!
Beta-Phase
+ Die biometrischen Daten sind auf Deinem Gerät deaktiviert, sodass Du sie nicht zum Entsperren Deine Wallet verwenden kannst. Aktiviere die Biometrie in den Einstellungen Deines Geräts, um diese Methode wieder zu verwenden.
+ Biometrische Authentifizierung deaktiviert
Bitte Karte oder Ring scannen
+ Du hast das Limit an biometrischen Entsperrversuchen erreicht. Bitte entsperren Deine Wallet durch Antippen Deines Geräts oder gib den Zugangscode ein.
+ Biometrische Authentifizierung gesperrt
Bitte versuche es in 30 Sekunden erneut oder scanne die Karte oder Ring
+ Die biometrische Anmeldung ist vorübergehend gesperrt. Bitte versuche es in 30 Sekunden erneut oder entsperre Deine Wallet durch Antippen Deines Geräts oder mit einem Zugangscode.
Zu viele Versuche
Du hast die biometrische Authentifizierung auf deinem Telefon deaktiviert und kannst keine Wallets in der App speichern. Um Wallets zu speichern, aktiviere bitte die biometrische Authentifizierung in deinen Telefoneinstellungen.
+ Die biometrischen Daten Deines Geräts wurden aktualisiert. Bitte wähle Deine Wallet aus und gib den zugehörigen Zugangscode ein, um die biometrische Anmeldung wieder zu aktivieren.
+ Aufmerksamkeit erforderlich
Bei der Bearbeitung Deines Aktionscodes ist ein Fehler aufgetreten. Bitte versuche es später noch einmal.
Fehler bei der Aktivierung
Dein Gutscheincode wurde erfolgreich aktiviert. Die Prämie wird deinem Bitcoin-Konto innerhalb von 14 Tagen gutgeschrieben.
@@ -221,7 +233,7 @@
Transaktion fehlgeschlagen
Kaufen
Gehe zu %1$s
- D hast keinen Zugang zur Kamera erteilt, bitte passe deine Datenschutzeinstellungen an
+ Du hast keinen Zugang zur Kamera erteilt, bitte passe Deine Datenschutzeinstellungen an
Abbrechen
Ändern
Konto auswählen
@@ -235,6 +247,7 @@
Demnächst verfügbar
Bestätigen
Verbinden
+ Kontakt zum Support
Kontakt zum Tangem-Support
Kontakt zum Visa-Support
Weiter
@@ -284,7 +297,7 @@
Zum Token
Verstanden
Ausblenden
- stunde
+ Stunde
Importieren
In Arbeit
Später
@@ -294,7 +307,7 @@
Gesperrt
Gesperrte Wallet
Hauptnetz
- monat
+ Monat
Netzgebühr
Der überwiesene Betrag wird um %1$s (%2$s) gekürzt, um die gewählte Gebührenhöhe zu decken.
@@ -376,7 +389,7 @@
Staking beenden
Aufgrund der Beschränkungen von %1$s können nur %2$d UTXOs in eine einzige Transaktion passen. Das bedeutet, dass du nur %3$s oder weniger senden kannst. Du musst den Betrag reduzieren.
Wert kopiert
- woche
+ Woche
mit
Ja
Vertragsadresse kopiert!
@@ -423,7 +436,6 @@
Flippe den Bildschirm deines Geräts nach unten, um Salden schnell ein- und auszublenden
%s Hasch
Geräte-ID
- Kontakt zum Support
Support-Chat öffnen
Weitere Karten oder Ringe verknüpfen
App Währung
@@ -557,7 +569,7 @@
Neues Wallet erstellen
Karte oder Ring bestellen
Karte oder Ring scannen
- Möchtest Du „Tangem“ die biometrische Authentifizierung erlauben? Um Deine Identität zu bestätigen und die App zu öffnen
+ Möchtest Du „Tangem“ die Verwendung biometrischer Authentifizierung erlauben? Um Deine Identität zu bestätigen und die App zu öffnen, klicke bitte hier.
An %s
Im %s Netzwerk
Willst Du den Vorgang zur Erstellung des Zugangscodes wirklich beenden?
@@ -678,11 +690,13 @@
APY %s
Mein Portfolio
Markt
+ Verdiene Geld mit Tangem
Um Adressen für ausgewählte Netzwerke zu generieren, musst du eine Tangem-Karte oder Ring scannen
Um Token hinzuzufügen, rufe dies auf oder tippe auf die Suchleiste
Die Daten dieses Abschnitts stammen aus den folgenden Netzwerken: %s
Die Daten konnten nicht geladen werden...
Keine Daten
+ Marktimpuls
Schnelle Aktionen
Markt durchsuchen
Ergebnis
@@ -773,6 +787,10 @@
Volumen
Rufe dies auf oder tippe auf die Suchleiste, um Token direkt vom Markt hinzuzufügen
Token hinzufügen
+ Steiger die Leistung Deiner Assets und ermögliche Dir gleichzeitig den sofortigen Zugriff. %s
+ Yield-Modus aktivieren
+ Du musst auf die folgende Version aktualisieren: %1$s um eine mobile Wallet zu erstellen
+ Mobile Wallet erfordert %1$s oder später
Alle Neuigkeiten
Auf dem Laufenden bleiben
NFC ist auf deinem Gerät nicht verfügbar
@@ -1367,31 +1385,45 @@
Du erhältst
Token auswählen
Nicht verfügbar
+ Wir freuen uns über Ihr Feedback
+ Tangem Pay jetzt in der Beta
+ Karte eingefroren
+ Kartenzahlung
Einzahlung
Streitfall
Transaktion erkunden
Servicegebühren
Gebühr
- Schütze Dein Geld, falls Deine Karte verloren geht oder gestohlen wird. Du kannst die Sperre jederzeit aufheben.
+ Schütze Dein Geld. Du kannst die Sperre jederzeit aufheben.
Karte sperren lassen?
+ Karte konnte nicht eingefroren werden. Versuchen Sie es später erneut.
Einfrieren
+ Ihre Karte ist eingefroren.
Hilfe erhalten
+ Andere
Abgeschlossen
Abgelehnt
Ausstehend
+ Bedingungen, Gebühren & Limits
Bedingungen und Einschränkungen
Die Bank hat diese Transaktionsanfrage abgelehnt.
- Diese Gebühr dient zur Deckung der Kosten für die Abwicklung Deiner Überweisung.
+ Diese Gebühr dient zur Deckung der Kosten für die Abwicklung Deiner Überweisung.Questa commissione copre il costo della gestione del tuo trasferimento.
Nutze Dein Geld weiterhin. Du kannst es jederzeit einfrieren.
Karte entsperren?
+ Entsperren der Karte fehlgeschlagen. Versuchen Sie es später erneut.
+ Ihre Karte ist entsperrt.
Abhebung
Guthaben hinzufügen
Aufladeoptionen
+ Kartennummer
PIN ändern
Die Karte ist vollständig für Zahlungen bereit.
PIN-Code erstellt
+ CVC
Daten konnten nicht geladen werden. Versuche es später noch einmal.
+ Ablaufdatum
Karte einfrieren
+ Details ausblenden
Verstecken
Google Wallet öffnen
Richte Tangem Pay mit wenigen Klicks ein und bezahlen mit Google Pay.
@@ -1399,8 +1431,10 @@
Karte zu Google Pay hinzufügen
Karte zu Apple Pay hinzufügen
Google Wallet öffnen
+ Tippen Sie auf die Schaltfläche \"+\" oben rechts
Apple Wallet öffnen
Tippe auf „Karte hinzufügen“.
+ Tippen Sie auf „Debit- oder Kreditkarte“
Gebe die Kartendaten manuell ein
Verifiziere die Karte mit dem Einmalpasswort (OPT), das an Dein Gerät gesendet wird.
Alles erledigt! Deine Karte ist einsatzbereit.
@@ -1410,22 +1444,53 @@
Es wurden technische Probleme festgestellt. Bitte versuche es später erneut oder kontaktiere den Support.
Empfangen ist jetzt nicht verfügbar
Aufdecken
+ Details anzeigen
Tausche beliebige Vermögenswerte in Deinem Portfolio gegen eine Karte.
Kartendetails
Karte entsperren
+ Auszahlung
+ Auszahlung derzeit nicht möglich
+ Sie können keinen Tausch oder eine neue Auszahlung starten, bis die aktuelle abgeschlossen ist.
+ Auszahlung läuft
+ PIN-Code ändern
+ Kehren Sie zur App zurück, falls Sie ihn vergessen.
+ Kartenausstellung fehlgeschlagen
+ Ein technischer Fehler ist aufgetreten, bitte versuchen Sie es erneut, indem Sie auf die Schaltfläche unten klicken
+ Ein technischer Fehler ist aufgetreten, bitte kontaktieren Sie den Support
+ Nutzen Sie Ihre Kryptowährungen für Einkäufe im Alltag. \nEine Zahlungskarte, die ihresgleichen sucht.
+ Tangem Pay erhalten
+ Zum Support
Es dauert in der Regel bis zu 15 Minuten.
+ Einrichtung Ihrer Tangem-Karte
+ Ausstellung Deiner Karte
+ Wir bereiten Ihre Karte vor. Dies kann etwas dauern.
Tangem Pay
+ Wir konnten Ihr Profil nicht verifizieren. Bei Fragen wenden Sie sich bitte an den Support.
+ Leider konnten wir Ihre Identität nicht verifizieren
+ KYC in Bearbeitung
Status anzeigen
KYC für Tangem Pay in Arbeit
+ Nutzen Sie Ihre Kryptowährungen für Einkäufe im echten Leben. \nEs ist eine Zahlungskarte, die ihresgleichen sucht.
+ Tangem Visa Card
Karte erhalten
- Füge es Deiner Wallet hinzu und bezahle überall mit Deinem Smartphone.
- Apple Pay & Google Pay
- Verwende Dein USDC-Guthaben, um alltägliche Einkäufe problemlos zu bezahlen.
- Einkäufe im Alltag
- Deine Kartendaten sind geschützt – volle Kontrolle in der App.
- Integrierte Sicherheit
- Hol Dir Deine kostenlose Crypto Card \n in wenigen Minuten
+ Mit digitaler Karte, die mit Apple Pay und Google Pay funktioniert
+ Geben Sie Ihre Vermögenswerte überall aus
+ Es fallen keine zusätzlichen Gebühren für Käufe an
+ Zahlen Sie genau das, was Sie sehen
+ Ein separates Zahlungskonto wird erstellt, ohne Ihre Adressen und Vermögenswerte offenzulegen
+ Unerreichte Privatsphäre
+ Erhalten Sie Ihre kostenlose Tangem Pay Card in wenigen Minuten
+ Payment account
+ Synchronisierung des Zahlungskontos erforderlich
+ Wir beheben ein technisches Problem. Bitte versuchen Sie es später erneut.
+ Service vorübergehend nicht verfügbar
+ Der Dienst ist derzeit nicht erreichbar. Bitte versuchen Sie es später erneut.
+ Synchronisation erforderlich
+ Tangem Visa Card
+ Tangem Pay ist vorübergehend nicht verfügbar
Tangem Pay
+ Verwenden Sie Ihre Karte oder Ihren Ring, um den Zugriff auf Ihr Zahlungskonto wiederherzustellen
+ Ihr PIN-Code
Das ist meine Wallet
Guthaben versteckt
Angezeigte Salden
@@ -1469,6 +1534,7 @@
Mehrere Adressen
Die Transaktionshistorie wird für diese Blockchain derzeit nicht verfügbar. Aber keine Sorge, wir arbeiten daran! In der Zwischenzeit kannst du es im Explorer überprüfen.
Operation
+ für: %s
von: %s
zu: %s
Validierer: %s
@@ -1920,7 +1986,9 @@
Gebührenpolitik
Tangem erhebt außerdem eine Servicegebühr von 15% auf den erzielten Ertrag.
Deine Gelder werden automatisch an Aave überwiesen, sobald die Netzwerkgebühren niedriger sind oder Dein Guthaben den erforderlichen Mindestbetrag erreicht.
+ Hohe Netzwerkgebühren
Historische Renditen
+ Aktiviere %1$s%% Jahreszins auf Dein Guthaben
Die Genehmigung für Deine Token im Yield-Modus wurde widerrufen. Öffne den Token, um die Berechtigung erneut zu erteilen.
Token-Genehmigung erforderlich
Prüfe Deine Netzwerkverbindung
@@ -1935,7 +2003,9 @@
Dezentral und selbstverwahrend
Durch die Nutzung dieses Dienstes erklärst Du Dich mit provider\n%1$s und %2$s einverstanden
Mit Aave verbinden
+ Aktiviere %1$s%% APY\nauf Deinem Kontostand
Aave %1$s%% • Variabler Zinssatz
+ Variabler Zinssatz
Aave
Durchschnitt %s
Renditen des letzten Jahres
@@ -1966,8 +2036,11 @@
%1$s geliefert an Aave
Ertragsmodus deaktiviert
%1$s aus Aave zurücküberwiesen
+ Yield-Modus initialisieren
+ Yield-Modus reaktivieren
Lieferung an Aave
%1$s geliefert an Aave
+ Abheben von Aave
Automatisch
Einzahlung von %1$s %2$s zur Deckung der Netzwerkgebühr für Transaktionen
Die Gebühr %s kann nicht gedeckt werden
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index 039fe17b62..b0aabad52d 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -1,5 +1,21 @@
+ Sin un código de acceso, su billetera no es segura.
+ Saltar de todos modos
+ Código de acceso no establecido
+ Cambiar código
+ Su código de acceso se utilizará para desbloquear su billetera y proteger el acceso a sus activos.
+ Usar de todos modos
+ Este código de acceso se puede adivinar fácilmente.
+ Introduzca el código de acceso
+ Código de acceso incorrecto. Su billetera móvil se bloqueará tras %s intentos incorrectos más.
+ Código de acceso incorrecto. La aplicación se bloqueará tras %s intentos incorrectos más.
+ Código de acceso incorrecto.\nPor favor, espere %s segundos e inténtelo de nuevo.
+ Confirme su código de acceso para continuar
+ Vuelva a introducir el código de acceso
+ Establezca un código de acceso de %sdígitos para desbloquear su billetera.
+ Crear código de acceso
+ Código de acceso
Archivar cuenta
Archivar
Estás archivando esta cuenta, pero siempre puede recuperarla.
@@ -38,16 +54,19 @@
Esta tarjeta no admite tokens en la red %1$s debido a una limitación del firmware.
¿Tiene dificultades para escanear su tarjeta/anillo?
Esta tarjeta no está diseñada para funcionar con Tangem
+ Establezca primero un código de acceso para activar la biometría
Utilice %1$s para desbloquear de forma rápida y segura su billetera y autorizar todas las acciones importantes, como la firma de transacciones. Al ser una billetera de hardware, seguirá necesitando una tarjeta para firmar.
Tarifa por defecto
Habilite las Tarifas predeterminadas para establecer automáticamente las tarifas de transacción y omitir la página de Tarifas al enviar fondos. Siempre puede volver a esta página si es necesario.
Vaya a ajustes para habilitar la autenticación biométrica en la Tangem App
Habilitar autenticación biométrica
Para deshabilitar %1$s deberá ingresar su código de acceso para desbloquear la aplicación e interactuar con su billetera.
+ Más tarde se le solicitará el código de acceso a su billetera para que podamos almacenarlo de forma segura para usarlo posteriormente
Esto eliminará todos los códigos de acceso guardados de la billetera. Cualquier operación posterior con la billetera requerirá introducir el código de acceso.
Eliminar la tarjeta guardada borra todos las billeteras guardadas y sus códigos de acceso de la app.
+ Esto borrará todos los códigos de acceso a la billetera guardados. Cualquier otra interacción con la billetera requerirá el envío del código de acceso.
Requerir código de acceso
- Esta opción desactiva la autenticación biométrica para acciones importantes. Se le pedirá que introduzca su código de acceso cada vez que, por ejemplo, deba firmar una transacción.
+ Esta opción desactiva el uso de datos biométricos para operaciones críticas. Tendrá que introducir su código de acceso cada vez que firme una transacción.
Guardar código de acceso
Se solicitará la autenticación biométrica en lugar del código de acceso para las interacciones con su tarjeta o anillo.
Mantener la billetera en la app
@@ -57,6 +76,9 @@
Predeterminado del sistema
Tema
Ajustes de la app
+ Agregar billetera
+ Seleccione una billetera para iniciar sesión
+ ¡Bienvenido de nuevo!
Realizó una copia de seguridad de su billetera correctamente.
Copia de seguridad completada
Su frase secreta de recuperación es un conjunto fijo de %s palabras aleatorias que se utilizan para acceder a su billetera y recuperarla.
@@ -74,10 +96,17 @@
Los saldos están ocultos
Según los desarrolladores de la blockchain, los tokens de Kaspa se encuentran actualmente en fase beta. ¡Estén atentos a las actualizaciones!
Modo Beta
+ La biometría está desactivada en su dispositivo, por lo que no puede utilizarla para desbloquear sus billeteras. Active la biometría en los ajustes de su dispositivo para volver a utilizar este método.
+ Autenticación biométrica deshabilitada
Por favor, escanee la tarjeta/anillo
+ Ha alcanzado el límite de intentos biométricos. Desbloquee su billetera con un toque en su dispositivo o introduzca su código de acceso.
+ Autenticación biométrica bloqueada
Por favor, inténtelo de nuevo en 30 segundos o escanee la tarjeta/anillo
+ El inicio de sesión biométrico está bloqueado temporalmente. Inténtalo de nuevo en 30 segundos o desbloquee su billetera con un toque del dispositivo o un código de acceso.
Demasiados intentos
Ha desactivado la autenticación biométrica en su teléfono y no podrá guardar billeteras en la aplicación. Para guardar billeteras, active la función de autenticación biométrica en los ajustes de su teléfono.
+ Se han actualizado los datos biométricos de su dispositivo. Seleccione su billetera e introduzca su código de acceso para habilitar nuevamente el inicio de sesión biométrico.
+ Se requiere atención
Se produjo un error al procesar tu código promocional. Inténtalo de nuevo más tarde.
Error de activación
Tu código promocional se activó correctamente. Una recompensa se acreditará en tu cuenta dentro de 14 días.
@@ -98,6 +127,13 @@
- %s token
- %s tokens
+ Por favor restablezca el siguiente dispositivo para continuar.
+ Restablecer billetera
+ Se han restablecido todos sus dispositivos Tangem. Ya puede seguir actualizando su billetera.
+ Actualizar de nuevo
+ Restablecimiento completo
+ Recomendamos completar el proceso de restablecimiento para todos los dispositivos Tangem en esta billetera.
+ No ha restablecido todos sus dispositivos Tangem
Desactive esta opción si no quiere que esta tarjeta se use para reiniciar códigos de acceso en otras tarjetas de esta billetera. Tenga en cuenta que esto también evitará que reinicie el código de acceso en esta tarjeta.
Permite usar esta tarjeta para reiniciar el código de acceso en otras tarjetas de esta billetera
Recuperación de código de acceso
@@ -123,19 +159,25 @@
ADA insuficiente
Aceptar
Acceso denegado
+ Cuenta
+ Cuentas
+ Activar
Agregar
Añadir al portafolio
Agregar token
+ Agregado
Dirección
Todos
Autorizar
Cantidad
Analítica
+ y
Aplicar
Aprobación
Aprobar
Atención
Redes disponibles
+ Copia de seguridad
Saldo: %s
Saldo
autenticación biométrica
@@ -146,6 +188,7 @@
No ha otorgado acceso a su cámara, cambie su configuración de privacidad
Cancelar
Cambie
+ Elegir cuenta
Elige una acción
Elija red
Elija token
@@ -156,6 +199,7 @@
Próximamente
Confirme
Conectando
+ Contactar con el equipo de soporte
Contacte con el soporte de Tangem
Contacte con el soporte de Visa
Continuar
@@ -194,9 +238,13 @@
Lento
Velocidad y tarifa
Finalizar
+ Olvidar
+ Gratis
+ De
De %s
Sincronizar direcciones
Comenzar
+ Obtener token
Ir al proveedor
Ir al token
Entendido
@@ -207,11 +255,19 @@
Más tarde
Más información
%1$s quedan
+ Legacy Bitcoin
Bloqueado
+ Billeteras bloqueadas
Red principal
mes
Tarifa de la red
La cantidad enviada se reducirá en %1$s (%2$s) para cubrir el nivel de tarifa seleccionado
+
+ - %d red
+ - %d redes
+
+ Nueva dirección
+ Noticias
Siguiente
NFT
No
@@ -230,10 +286,12 @@
%1$s — %2$s
Leer más
Recibir
+ Recomendado
Rechazar
Recargar
Renombrar
Requerido
+ Resetear
Guarde
Guardar cambios
Buscar
@@ -252,6 +310,8 @@
Mostrar más
Firme
Firme y envíe
+ Saltar
+ Algo salió mal
Stake
Staking
Empezar
@@ -260,9 +320,12 @@
Soporte
Redes soportadas
Intercambiar
+ Tangem
+ Tangem Wallet
términos y condiciones
Condiciones de uso
A
+ A %s
Hoy
- %d token
@@ -272,6 +335,7 @@
Estado de la transacción
Transacciones
Transferencia
+ No se pueden cargar los datos…
Entiendo
Hubo un error. Por favor inténtelo de nuevo.
Inaccesible
@@ -323,7 +387,6 @@
Gire la pantalla de su dispositivo hacia abajo para ocultar y mostrar rápidamente los saldos
%s hashes
ID del dispositivo
- Contactar con el equipo de soporte
Abrir chat de soporte
Vincular más tarjetas
Moneda de la aplicación
@@ -447,6 +510,7 @@
Crear Nueva Billetera
Pedir Tangem
Escanee
+ ¿Quiere permitir que \"Tangem\" utilice la autenticación biométrica? Para confirmar su identidad y abrir la aplicación
a %s
En la red %s
¿Está seguro de que desea salir del proceso de creación de código de acceso?
@@ -504,11 +568,13 @@
APY %s
Mi portafolio
Mercado
+ Gane con Tangem
Para generar direcciones para las redes seleccionadas, debe escanear su tarjeta Tangem
Para agregar tokens, abra esta página o pulse sobre la barra de búsqueda
Los datos de este apartado proceden de las siguientes redes: %s
No se pueden cargar los datos…
Sin datos
+ Análisis del Mercado
Acciones rápidas
Busque en el mercado
Resultado
@@ -598,6 +664,10 @@
Volumen
Tire hacia arriba o toque la barra de búsqueda para agregar tokens directamente desde el mercado
Agregar tokens
+ Potencie sus activos mientras los suministra con acceso inmediato. %s
+ Activar Modo Rendimiento
+ Debes actualizar a %1$s para crear una mobile wallet
+ Mobile Wallet requiere %1$s o posterior
NFC no está disponible en su dispositivo
Acerca de NFT
Activo NFT
@@ -654,6 +724,9 @@
La cuenta de destino no tiene una Trustline (línea de confianza) para el activo que se envía.
Obtén $10 en BTC con cada billetera \n ¡Date prisa!
Black Friday: hasta 30% DESCUENTO
+ Vamos
+ Crea el par perfecto de packs de Tangem. Por tiempo limitado.
+ 1+1: Compra una billetera y obtén un 50% dto. en la segunda
Únase ahora
Comparta su código y gane 5 USDT por venta. Su amigo obtiene un 10% de descuento.
¡Obtenga RECOMPENSAS por cada amigo!
@@ -1152,6 +1225,111 @@
Usted recibe
Elige token
no disponible
+ Nos encantaría recibir tus comentarios
+ Tangem Pay ya está en beta
+ Tarjeta congelada
+ Pago con tarjeta
+ Depósito
+ Disputar
+ Explorar transacción
+ Comisiones
+ Comisión
+ Mantén tu dinero seguro. Puedes desbloquear en cualquier momento.
+ ¿Congelar tu tarjeta?
+ No se pudo congelar la tarjeta. Inténtalo de nuevo más tarde.
+ Mantén tu dinero seguro. Puedes desbloquear en cualquier momento.
+ Tu tarjeta está congelada.
+ Obtener ayuda
+ Otro
+ Completado
+ Rechazado
+ Pendiente
+ Términos, tarifas y límites
+ Términos y límites
+ El banco rechazó esta solicitud de transacción.
+ Esta tarifa cubre el costo de procesar tu transferencia.
+ Sigue usando tu dinero. Puedes congelarlo en cualquier momento.
+ ¿Descongelar tu tarjeta?
+ No se pudo descongelar la tarjeta. Inténtalo de nuevo más tarde.
+ Tu tarjeta está descongelada.
+ Retirada
+ Agregar fondos
+ Opciones de recarga
+ Número de tarjeta
+ Modificar PIN
+ La tarjeta está completamente lista para pagos.
+ Código PIN creado
+ CVC
+ Error al cargar datos. Inténtalo de nuevo más tarde.
+ Caducidad
+ Congelar tarjeta
+ Ocultar detalles
+ Ocultar
+ Abrir Google Wallet
+ Configura Tangem Pay en unos pocos toques y empieza a pagar con Google Pay.
+ Configura Tangem Pay en unos pocos toques y empieza a pagar con Apple Pay
+ Añade tu tarjeta a Google Pay
+ Añade tu tarjeta a Apple Pay
+ Abrir Google Wallet
+ Toque el botón \"+\" en la parte superior derecha
+ Abrir Apple Wallet
+ Toca \"Añadir tarjeta\"
+ Toque “Tarjeta de débito o crédito”
+ Ingresa los detalles de la tarjeta manualmente
+ Verifica la tarjeta usando el OTP enviado a tu dispositivo.
+ ¡Todo listo! Tu tarjeta está lista para usar.
+ Añadir tarjeta a Google Pay
+ Añade tu tarjeta a Apple Pay
+ Comparte tu dirección o muestra el código QR
+ Recepción no disponible ahora
+ Mostrar
+ Mostrar detalles
+ Intercambia cualquier activo de tu portafolio por una tarjeta
+ Detalles de la tarjeta
+ Descongelar tarjeta
+ Retirar
+ Retiro no disponible ahora
+ No puedes iniciar un intercambio o un nuevo retiro hasta que el actual finalice.
+ Retiro en progreso
+ Cambiar código PIN
+ Vuelve a la app si lo olvidas.
+ Error al emitir la tarjeta
+ Ha ocurrido un error técnico, por favor inténtalo de nuevo haciendo clic en el botón de abajo
+ Ha ocurrido un error técnico, por favor contacta con el soporte
+ Usa tus criptomonedas para compras en el mundo real. \nEs una tarjeta de pago única en su tipo.
+ Obtener Tangem Pay
+ Ir a Soporte
+ Suele tardar hasta 15 minutos
+ Configurando tu tarjeta Tangem
+ Emisión de su tarjeta
+ Estamos preparando tu tarjeta. Esto puede llevar un poco de tiempo.
+ Tangem Pay
+ No pudimos verificar tu perfil. Si tienes alguna pregunta, contacta con el soporte.
+ Lamentablemente, no pudimos verificar tu identidad
+ KYC en curso
+ Ver estado
+ KYC en progreso para Tangem Pay
+ Usa tus criptomonedas para compras en el mundo real. \nEs una tarjeta de pago única en su tipo.
+ Tangem Visa Card
+ Obtener tarjeta
+ Con tarjeta digital que funciona con Apple Pay y Google Pay
+ Gasta tus activos en cualquier lugar
+ No hay comisiones adicionales por compras
+ Paga exactamente lo que ves
+ Se creará una cuenta de pago separada sin divulgar tus direcciones y activos
+ Privacidad inigualable
+ Obtén tu tarjeta Tangem Pay gratuita en minutos
+ Payment account
+ Sincronización de cuenta de pago necesaria
+ Estamos solucionando un problema técnico. Por favor, inténtelo de nuevo más tarde.
+ Servicio temporalmente no disponible
+ El servicio no está disponible actualmente. Inténtalo de nuevo más tarde.
+ Sincronización necesaria
+ Tangem Visa Card
+ Tangem Pay temporalmente no disponible
+ Tangem Pay
+ Usa tu tarjeta o anillo para restaurar el acceso a tu cuenta de pago
+ Tu código PIN
Esta es mi billetera
Saldos ocultos
Saldos mostrados
@@ -1239,6 +1417,7 @@
Renombrar la billetera
Desbloquear todo
Desbloquear todo con %s
+ Elija cómo agregar su billetera
La blockchain está Inaccesible. Inténtelo más tarde
Escanee la tarjeta o el anillo
Esta billetera ya ha sido activada anteriormente.\nSi no fue usted quien lo hizo, comuníquese con el servicio de asistencia.\nTangem nunca vende billeteras con un código de acceso pre generado.
@@ -1274,10 +1453,21 @@
Conectar a dApps
WalletConnect
La conexión puede tardar unos segundos.
+ Crear una billetera Tangem
+ ¿Quiere comprar Tangem Wallet?
+ Comprar ahora
+ Recuperar una billetera existente mediante copia de seguridad de Google Drive
+ Importar desde Google Drive
+ Agregar billetera existente
+ Dispositivos físicos que almacenan de forma segura su clave privada fuera de línea.
+ Escanee una billetera Tangem
+ Importa una billetera existente con su frase de recuperación.
Importar billetera
+ Introduzca la frase de recuperación
La copia de seguridad de su billetera se realizó con éxito.
Importar billetera
Importación completada
+ Importar billetera
%s Precio de mercado
últimas 24h
%s red
@@ -1286,8 +1476,11 @@
Consíguelo ahora con un 10 % de descuento
Acceda a más de 13.000 criptomonedas. Compre, venda, intercambie y realice staking con un solo toque.\nVincule hasta tres tarjetas para hacer copias de seguridad.
Descubra Tangem Wallet
+ Este código de acceso protege su billetera y se utiliza para iniciar sesión y firmar transacciones.
+ Establecer/Cambiar código de acceso
Cambiar código de acceso
Manténgase informado sobre las transacciones entrantes de la billetera y las actualizaciones de Tangem.
+ Es posible que las notificaciones push no funcionen actualmente en dispositivos Huawei. Estamos trabajando activamente en una solución y la publicaremos en una próxima actualización. ¡Gracias por su comprensión!
Notificaciones de transacciones
Establecer código de acceso
Ajustes de la wallet
@@ -1477,6 +1670,20 @@
URI ya utilizado
WalletConnect
Transacción sospechosa
+ ¿Ya tiene Tangem Wallet?
+ Miles de activos
+ La mejor billetera de hardware de su clase
+ Entrega rápida
+ Empiece con un solo toque
+ Fácil y seguro
+ Fácil de usar
+ Cree una billetera de hardware con Tangem. Delgada como una tarjeta bancaria, segura como una bóveda.
+ Crear o importar una billetera de software
+ Cree o importe una billetera de software en su teléfono.
+ Empezar con Mobile Wallet
+ Otro método
+ Utilice la billetera de hardware Tangem
+ Obtenga más información y compre
Ignorar
Tiene un backup interrumpido. ¿Quiere reanudarlo?
Sí, reanudar
@@ -1491,14 +1698,14 @@
No, enviar todo
Reducir en %s XTZ
Para evitar pagar una comisión mayor la próxima vez que recargue su billetera, reduzca el importe en %s XTZ
- Con el modo Rendimiento activo, todos los depósitos futuros a esta dirección irán a Aave. Puede seguir gestionando sus fondos libremente.
+ Con el Modo Rendimiento activo, todos los depósitos futuros a esta dirección irán a Aave. Puede seguir gestionando sus fondos libremente.
Su %s se suministra a Aave
El suministro de %1$s %2$s a Aave está pendiente
Aprobar
Se ha revocado la aprobación de su token. Concédalo de nuevo para reanudar el servicio.
Aprobación necesaria
- Se le descontará la comisión y se le volverán a prestar sus activos.
- Para seguir ganando, se requiere aprobación.
+ Se le descontará la comisión y se volverán a suministrar sus activos.
+ Para seguir generando rendimiento, se requiere aprobación.
Confirmar aprobación
Sus fondos se suministran actualmente al protocolo Aave, pero puede gestionarlos en cualquier momento.
Su %s está depositado en Aave
@@ -1527,8 +1734,11 @@
Política de tarifas
Tangem también cobra una comisión de servicio del 15% sobre el rendimiento obtenido.
Sus fondos se suministrarán automáticamente a Aave una vez que las comisiones de red sean más bajas o su saldo alcance el importe mínimo requerido.
+ Las comisiones son más altas de lo habitual debido a la alta actividad del mercado. Puede continuar ahora o volver a consultar más tarde cuando las comisiones sean más bajas.
+ Tarifas de red elevadas
Rendimientos históricos
- Se ha revocado la autorización para su token en el modo Rendimiento. Abra el token para volver a conceder el permiso.
+ Consiga un %1$s%% APY sobre su saldo
+ Se ha revocado la autorización para su token en el Modo Rendimiento. Abra el token para volver a conceder el permiso.
Se necesita la aprobación de Token
Compruebe su conexión de red
Información de tarifas de red inaccesible
@@ -1542,7 +1752,9 @@
Descentralizado y autocustodiado
Al utilizar este servicio, usted acepta que el proveedor\n%1$s y %2$s
Conectar Aave
+ Consiga un %1$s%% APY\nen su saldo
Aave %1$s%% - Tipo de interés variable
+ Tipo de interés variable
Aave
Promedio %s
Resultados del año pasado
@@ -1558,7 +1770,7 @@
Activo
En pausa
Desactivación del modo de rendimiento
- Al desactivar esto, retirará sus fondos de Aave a %s en su billetera y dejará de ganar recompensas.
+ Al desactivar esto, retirará sus fondos de Aave a %s en su billetera y dejará de generar rendimientos.
Se cobra una comisión de red por la blockchain al salir del modo Yield.
Desactivar el modo de rendimiento
Suministrar
@@ -1569,16 +1781,20 @@
Modo de rendimiento
Procesando su depósito
Modo Rendimiento
+ Implementación del contrato del Modo Rendimiento
Modo Rendimiento activado
%1$s suministrado a Aave
Modo Rendimiento desactivado
%1$s retirado de Aave
+ Iniciar Modo Rendimiento
+ Reactivar Modo Rendimiento
Suministrar a Aave
%1$s suministrado a Aave
+ Retirar de Aave
Automático
Deposite algo de %1$s %2$s para cubrir la tarifa de red para las transacciones
No se puede cubrir la tarifa %s
- El servicio de intereses no está disponible en este momento. Vuelva a intentarlo más tarde.
+ El Modo de Rendimiento no está disponible en este momento. Vuelva a intentarlo más tarde.
Modo de rendimiento no disponible
No se puede cargar el gráfico...
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index 34d5e51c71..ca122acb3d 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -1,5 +1,21 @@
+ Sans code d\'accès, votre portefeuille n\'est pas sécurisé.
+ Ignorer quand même
+ Code d\'accès non défini
+ Modifier le code
+ Votre code d\'accès sera utilisé pour déverrouiller votre portefeuille et protéger l\'accès à vos actifs.
+ Utiliser quand même
+ Ce code d\'accès peut être facilement deviné.
+ Entrez le code d\'accès
+ Code d\'accès incorrect. Votre portefeuille mobile sera supprimé après %s tentatives incorrectes supplémentaires.
+ Code d\'accès incorrect. L\'application sera verrouillée après %s tentatives infructueuses supplémentaires.
+ Code d\'accès incorrect.\nVeuillez patienter %s secondes et réessayer.
+ Confirmez votre code d\'accès pour continuer
+ Saisissez à nouveau le code d\'accès
+ Définissez un code d\'accès à %s chiffres pour déverrouiller votre portefeuille.
+ Créer un code d\'accès
+ Code d\'accès
Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à l\'achat
Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à la vente
Vendre
@@ -34,12 +50,16 @@
Les jetons du réseau %1$s ne sont pas pris en charge par cette carte en raison d\'une limitation du micrologiciel.
Avez-vous des difficultés à scanner votre carte ?
Cette carte n\'est pas conçue pour fonctionner avec Tangem
+ Définissez d\'abord un code d\'accès pour activer la biométrie.
Frais par défaut
Activez les frais par défaut pour définir automatiquement les frais de transaction et ignorer la page Frais lors de l\'envoi de fonds. Vous pouvez toujours revenir sur cette page si nécessaire.
Accédez aux paramètres pour activer l\'authentification biométrique dans l\'application Tangem
Activer l\'authentification biométrique
+ Le code d\'accès à votre portefeuille vous sera demandé ultérieurement afin que nous puissions le stocker en toute sécurité pour une utilisation future.
Cela supprimera tous les codes d\'accès des portefeuilles enregistrés. Toute opération ultérieure avec le portefeuille nécessitera la soumission du code d\'accès.
La suppression de la carte enregistrée supprime de l\'application tous les portefeuilles enregistrés et leurs codes d\'accès.
+ Cela supprimera tous les codes d\'accès enregistrés pour le portefeuille. Toute interaction ultérieure avec le portefeuille nécessitera la saisie du code d\'accès.
+ Cette option désactive la biométrie pour les actions sensibles. Vous devrez saisir votre code d\'accès à chaque fois que vous signerez une transaction.
Enregistrer le code d\'accès
L\'authentification biométrique sera demandée à la place du code d\'accès pour les interactions avec votre carte.
Conserver le portefeuille dans l\'application
@@ -49,6 +69,9 @@
Par défaut du système
Thème
Paramètres de l\'application
+ Ajouter un wallet
+ Sélectionnez un wallet pour vous connecter
+ Heureux de te revoir!
Ces mots ne peuvent pas être récupérés en cas de perte. Assurez-vous de les conserver en lieu sûr.
Pour masquer ou afficher vos soldes, il suffit de retourner l\'écran de votre appareil vers le bas ou de le désactiver dans les paramètres
Ne plus afficher
@@ -56,10 +79,17 @@
Les soldes sont masqués
Selon les développeurs de la blockchain, les jetons Kaspa sont actuellement en version bêta. Restez à l\'écoute des mises à jour !
Mode bêta
+ La biométrie est désactivée sur votre appareil, vous ne pouvez donc pas l\'utiliser pour déverrouiller vos portefeuilles. Activez la biométrie dans les paramètres de votre appareil pour pouvoir à nouveau utiliser cette méthode.
+ Authentification biométrique désactivée
Veuillez scanner la carte/bague
+ Vous avez atteint la limite de tentatives biométriques. Veuillez déverrouiller votre portefeuille en tapotant votre appareil ou en saisissant votre code d\'accès.
+ Authentification biométrique verrouillée
Veuillez réessayer dans 30 secondes ou scannez la carte/bague
+ La connexion biométrique est temporairement verrouillée. Veuillez réessayer dans 30 secondes ou déverrouiller votre portefeuille à l\'aide d\'un appareil ou d\'un code d\'accès.
Trop de tentatives
Vous avez désactivé l\'authentification biométrique sur votre téléphone et ne pourrez pas enregistrer de portefeuilles dans l\'application. Pour enregistrer des portefeuilles, veuillez activer la fonction d\'authentification biométrique dans les paramètres de votre téléphone.
+ Les données biométriques de votre appareil ont été mises à jour. Veuillez sélectionner votre portefeuille et saisir son code d\'accès pour réactiver la connexion biométrique.
+ Attention requise
Une erreur s\'est produite lors du traitement de votre code promo. Veuillez réessayer plus tard.
Erreur d\'activation
Votre code promotionnel a été activé avec succès. Une récompense sera créditée sur votre compte dans un délai de 14 jours.
@@ -80,6 +110,13 @@
- %s token
- %s tokens
+ Veuillez réinitialiser l\'appareil suivant pour continuer.
+ Réinitialisation du portefeuille
+ Tous les appareils Tangem ont été réinitialisés. Vous pouvez maintenant continuer à mettre à jour votre portefeuille.
+ Mettre de nouveau à jour
+ Réinitialisation terminée
+ Nous vous recommandons d\'effectuer la réinitialisation de tous les appareils Tangem contenus dans ce portefeuille.
+ Vous n\'avez pas réinitialisé tous vos appareils Tangem.
Désactivez cette option si vous ne voulez pas que cette carte soit utilisée pour réinitialiser les codes d\'accès sur d\'autres cartes de ce portefeuille. Veuillez noter que cela vous empêchera également de réinitialiser le code d\'accès sur cette carte.
Vous permet d\'utiliser cette carte pour réinitialiser le code d\'accès sur d\'autres cartes de ce portefeuille
Récupération du code d\'accès
@@ -105,19 +142,25 @@
ADA insuffisant
Accepter
Accès refusé
+ Compte
+ Comptes
+ Activer
Ajouter
Ajouter au portfolio
Ajouter un jeton
+ Ajouté
Adresse
Tous
Permettre
Montant
Analytique
+ et
Appliquer
Approbation
Approuver
Attention
Réseaux disponibles
+ Sauvegarde
Solde : %s
Solde
authentification biométrique
@@ -128,6 +171,7 @@
Vous n\'avez pas octroyé l\'accès à votre caméra, veuillez modifier vos paramètres de confidentialité
Annuler
Changez
+ Choisissez un compte
Choisissez une action
Choisissez le réseau
Choisir le jeton
@@ -135,8 +179,12 @@
Réclamer
Réclamez des récompenses
Fermer
+ À venir
Confirmez
Connexion
+ Contactez l\'équipe de support
+ Contacter l\'assistance Tangem
+ Contacter le service d\'assistance Visa
Continuer
Convertir
Copier
@@ -153,6 +201,7 @@
- jours
Supprimer
+ Désactiver
Désactivé
Se déconnecter
Exécuté
@@ -172,9 +221,13 @@
Lent
Vitesse et frais
Terminer
+ Oublier
+ Gratuit
+ Du
De %s
Synchroniser les adresses
Commencer
+ Obtenir un token
Aller au fournisseur
Aller au jeton
Compris
@@ -185,16 +238,25 @@
Plus tard
En savoir plus
Il reste %1$s
+ Legacy Bitcoin
Verrouillé
+ Portefeuilles verrouillés
Réseau principal
mois
Commissions du réseau
Le montant envoyé sera réduit de %1$s(%2$s) pour couvrir le niveau de frais sélectionné
+
+ - %d réseau
+ - %d réseaux
+
+ Nouvelle adresse
+ Actualités
Suivant
NFT
Non
Aucune adresse
Non ajouté
+ Pas maintenant
Maintenant
OK
Ouvrir dans le navigateur
@@ -207,10 +269,12 @@
%1$s — %2$s
En savoir plus
Recevoir
+ Recommandé
Rejeter
Recharger
Renommer
Obligatoire
+ Réinitialiser
Enregistrez
Sauvegarder les modifications
Rechercher
@@ -229,6 +293,8 @@
Afficher plus
Signez
Signez et envoyez
+ Passer
+ Une erreur s\'est produite.
Stake
Staking
Démarrer
@@ -237,18 +303,22 @@
Support
Réseaux pris en charge
Échanger
+ Tangem
+ Tangem Wallet
termes et conditions
Conditions d\'utilisation
À
+ À %s
Aujourd\'hui
- - %d jeton
- - %d jetons
+ - %d token
+ - %d tokens
La transaction a échoué
Statut de la transaction
Transactions
Fourniture
+ Impossible de charger les données…
Je comprends
Il y avait une erreur. Veuillez réessayer.
Inaccessible
@@ -300,7 +370,6 @@
Retournez l\'écran de votre appareil vers le bas pour masquer et afficher rapidement les soldes
%s hashes
ID de l\'appareil
- Contactez l\'équipe de support
Ouvrir le chat d\'assistance
Lier plus de cartes
Monnaie de l\'application
@@ -422,8 +491,10 @@
Créer un nouveau Portefeuille
Commandez
Scannez
+ Souhaitez-vous autoriser « Tangem » à utiliser l\'authentification biométrique ? Pour confirmer votre identité et ouvrir l\'application
à %s
Via %s
+ Êtes-vous sûr de vouloir annuler la configuration du code d\'accès ?
Ces informations ont été générées avec l\'IA.\nAppuyez ici si vous trouvez des erreurs.
Touchez, pour modifier le code d\'accès
Touchez, pour modifier le mot de passe
@@ -477,11 +548,13 @@
APY %s
Mon portfolio
Marché
+ Gagnez avec Tangem
Pour générer des adresses pour les réseaux sélectionnés, vous devez scanner votre carte Tangem
Pour ajouter des jetons, faites-le apparaître ou appuyez sur la barre de recherche
Les données de cette section proviennent des réseaux suivants : %s
Impossible de charger les données…
Aucune donnée
+ Rythme du marché
Actions rapides
Rechercher sur le marché
Résultat
@@ -571,6 +644,10 @@
Volume
Tirez vers le haut ou appuyez sur la barre de recherche pour ajouter des jetons directement depuis le marché
Ajouter des jetons
+ Optimisez vos actifs tout en leur fournissant un accès instantané. %s
+ Activer le mode rendement
+ Vous devez effectuer la mise à jour %1$s afin de créer un portefeuille mobile.
+ Le portefeuille mobile nécessite %1$s ou une version supérieure.
NFC n\'est pas disponible sur votre appareil
À propos des NFT
Actif NFT
@@ -627,6 +704,9 @@
Le compte destinataire n\'a pas de Trustline pour l\'actif envoyé qu\'il tente d\'envoyer.
Obtenez 10 $ en BTC avec chaque wallet\nDépêchez-vous !
Black Friday : jusqu\'à 30% de réduction
+ C\'est parti !
+ Offre à durée limitée !
+ 1 + 1 : achetez 1 wallet et bénéficiez de 50 % de réduction sur le 2ᵉ
Rejoignez maintenant
Partagez votre code et gagnez 5 USDT par vente. Votre ami bénéficie de 10 % de réduction.
Recevez des RÉCOMPENSES pour chaque ami !
@@ -1126,6 +1206,111 @@
Vous recevez
Choisir le jeton
non disponible
+ Nous serions ravis de recevoir vos retours
+ Tangem Pay en version bêta
+ Carte gelée
+ Paiement par carte
+ Dépôt
+ Litige
+ Explorer la transaction
+ Frais de service
+ Frais
+ Protégez votre argent. Vous pouvez le débloquer à tout moment.
+ Geler votre carte ?
+ Échec du gel de la carte. Réessayez plus tard.
+ Geler
+ Votre carte est gelée.
+ Obtenir de l\'aide
+ Autre
+ Terminé
+ Refusé
+ En attente
+ Conditions, frais et limites
+ Conditions et limites
+ La banque a rejeté cette demande de transaction.
+ Ces frais couvrent le coût du traitement de votre virement.
+ Continuez à utiliser votre argent. Vous pouvez le geler à tout moment.
+ Dégeler votre carte ?
+ Échec du dégel de la carte. Réessayez plus tard.
+ Votre carte est dégelée.
+ Retrait
+ Ajouter des fonds
+ Options de recharge
+ Numéro de carte
+ Modifier le code PIN
+ La carte est totalement prête pour les paiements.
+ Code PIN créé
+ CVC
+ Échec du chargement des données. Réessayez plus tard.
+ Expiration
+ Geler la carte
+ Masquer les détails
+ Masquer
+ Ouvrir Google Wallet
+ Configurez Tangem Pay en quelques clics et commencez à payer avec Google Pay.
+ Configurez Tangem Pay en quelques clics et commencez à payer avec Apple Pay
+ Ajoutez votre carte à Google Pay
+ Ajouter votre carte à Apple Pay
+ Ouvrir Google Wallet
+ Appuyez sur le bouton « + » en haut à droite
+ Ouvrir Apple Wallet
+ Appuyez sur « Ajouter une carte »
+ Appuyez sur « Carte de débit ou de crédit »
+ Saisissez manuellement les détails de la carte
+ Vérifiez la carte à l\'aide de l\'OTP envoyé à votre appareil.
+ Tout est prêt ! Votre carte est prête à l\'emploi.
+ Ajouter une carte à Google Pay
+ Ajouter la carte à Apple Pay
+ Partagez votre adresse ou montrez le QR code
+ Réception indisponible pour le moment
+ Révéler
+ Afficher les détails
+ Échangez n\'importe quel actif de votre portefeuille contre une carte
+ Détails de la carte
+ Dégeler la carte
+ Retirer
+ Retrait indisponible pour le moment
+ Vous ne pouvez pas lancer d\'échange ou de nouveau retrait tant que le retrait actuel n\'est pas terminé.
+ Retrait en cours
+ Modifier le code PIN
+ Revenez dans l\'application si vous l\'oubliez.
+ Échec de l\'émission de la carte
+ Une erreur technique s\'est produite, veuillez réessayer en cliquant sur le bouton ci-dessous
+ Une erreur technique s\'est produite, veuillez contacter le support
+ Utilisez vos cryptomonnaies pour vos dépenses quotidiennes. \nC\'est une carte de paiement unique en son genre.
+ Obtenir Tangem Pay
+ Contacter le support
+ Cela prend généralement jusqu\'à 15 minutes
+ Configuration de votre carte Tangem
+ Émission de votre carte
+ Nous préparons votre carte. Cela peut prendre un peu de temps.
+ Tangem Pay
+ Nous n\'avons pas pu vérifier votre profil. Pour toute question, veuillez contacter le support.
+ Malheureusement, nous n\'avons pas pu vérifier votre identité
+ KYC en cours
+ Voir le statut
+ KYC en cours pour Tangem Pay
+ Utilisez vos cryptomonnaies pour vos dépenses du quotidien. \nC\'est une carte de paiement unique en son genre.
+ Tangem Visa Card
+ Obtenir la carte
+ Avec carte numérique compatible Apple Pay et Google Pay
+ Dépensez vos actifs partout
+ Aucuns frais supplémentaires pour les achats
+ Payez exactement ce que vous voyez
+ Un compte de paiement séparé sera créé sans divulguer vos adresses et actifs
+ Confidentialité inégalée
+ Obtenez votre carte Tangem Pay gratuite en quelques minutes
+ Payment account
+ Synchronisation du compte de paiement nécessaire
+ Nous réparons un problème technique. Veuillez réessayer plus tard.
+ Service temporairement indisponible
+ Le service est actuellement indisponible. Veuillez réessayer plus tard.
+ Synchronisation requise
+ Tangem Visa Card
+ Tangem Pay est temporairement indisponible
+ Tangem Pay
+ Utilisez votre carte ou votre bague pour restaurer l\'accès à votre compte de paiement
+ Votre code PIN
C\'est mon portefeuille
Soldes masqués
Soldes affichés
@@ -1217,6 +1402,8 @@
- disponible pour %d jours
Soldes et Limites
+ Le code d\'accès sera utilisé pour gérer votre compte de paiement et le protéger contre tout accès non autorisé.
+ Code d\'accès
Êtes-vous sûr de vouloir quitter ? Vous pourrez reprendre plus tard là où vous vous étiez arrêté.
Cela ne prendra pas longtemps. Nous configurons votre compte.
Cela ne prendra pas longtemps. Nous terminons l\'activation.
@@ -1230,6 +1417,7 @@
Déverrouiller
Scannez votre carte pour déverrouiller l\'accès
Déverrouillage nécessaire
+ Choisissez comment ajouter votre portefeuille
La blockchain n\'est pas accessible. Réessayez plus tard
Scanner la carte ou la bague
Ce portefeuille a déjà été activé auparavant.\nSi cela n\'a pas été fait par vous, veuillez contacter le support.\nTangem ne vend jamais de portefeuilles avec le code d\'accès pré-généré.
@@ -1265,6 +1453,21 @@
Se connecter aux dApps
WalletConnect
La connexion peut prendre quelques secondes
+ Créer un Tangem Wallet
+ Vous souhaitez acheter le Tangem Wallet ?
+ Acheter maintenant
+ Récupérer un portefeuille existant via la sauvegarde Google Drive
+ Importer depuis Google Drive
+ Ajouter un portefeuille existant
+ Dispositifs physiques qui stockent votre clé privée hors ligne en toute sécurité.
+ Scanner un Tangem Wallet
+ Importez un portefeuille existant à l\'aide de votre seedphrase.
+ Importer un wallet
+ Entrez la seedphrase
+ Vous avez sauvegardé votre portefeuille avec succès.
+ Importer un wallet
+ Importation terminée
+ Importer un portefeuille
%s Prix du marché
dernières 24h
%s réseau
@@ -1273,8 +1476,13 @@
Obtenez-le maintenant avec 10 % de réduction
Accédez à plus de 13 000 cryptomonnaies. Achetez, vendez, échangez et stakez en un seul clic.\nAssociez jusqu\'à trois cartes pour une sauvegarde.
Découvrez le Portefeuille Tangem
+ Ce code d\'accès protège votre portefeuille et sert à vous connecter et à signer des transactions.
+ Définir/Modifier le code d\'accès
+ Modifier le code d\'accès
Recevez des notifications sur les transactions entrantes du portefeuille et les mises à jour de Tangem.
+ Les notifications push peuvent ne pas fonctionner actuellement sur les appareils Huawei. Nous travaillons activement à la résolution de ce problème et publierons un correctif dans une prochaine mise à jour. Merci de votre compréhension !
Notifications de transaction
+ Définir le code d\'accès
Paramètres du portefeuille
Tangem
Utilisez %s ou scannez une carte/bague pour déverrouiller l\'accès à votre portefeuille
@@ -1456,7 +1664,20 @@
Demande de transaction
Montant illimité
WalletConnect
- En savoir plus
+ Vous avez déjà un portefeuille Tangem ?
+ Des milliers d\'actifs
+ Le meilleur hardware wallet de sa catégorie
+ Livraison rapide
+ Commencez en un seul clic
+ Sans faille et sécurisé
+ Facile à utiliser
+ Créez un harware wallet avec Tangem. Aussi fin qu\'une carte bancaire, aussi sûr qu\'un coffre-fort.
+ Créer ou importer un portefeuille logiciel
+ Créez ou importez un portefeuille logiciel sur votre téléphone.
+ Commencez avec Mobile Wallet
+ Autre méthode
+ Utilisez le hardware wallet Tangem
+ En savoir plus et acheter
Ignorer
Vous avez une sauvegarde interrompue. Voulez-vous la reprendre ?
Oui, reprendre
@@ -1507,7 +1728,10 @@
Politique tarifaire
Tangem prélève également des frais de service de 15% sur les revenus générés.
Vos fonds seront automatiquement transférés vers Aave dès que les frais de réseau seront moins élevés ou que votre solde atteindra le montant minimum requis.
+ Les frais sont plus élevés que d\'habitude car le marché est très actif. Vous pouvez procéder maintenant ou revenir plus tard lorsque les frais seront moins élevés.
+ Frais de réseau élevés
Rendements historiques
+ Activez %1$s%% APY sur votre solde
L\'autorisation pour votre token en mode Rendement a été révoquée. Ouvrez le token pour accorder à nouveau l\'autorisation.
Approbations de tokens nécessaires
Vérifiez votre connexion réseau.
@@ -1522,7 +1746,9 @@
Décentralisé et auto-détenu
En utilisant ce service, vous acceptez les conditions générales du fournisseur %1$s et %2$s.
Connecter Aave
+ Activez %1$s%% APY\nsur votre solde
Aave %1$s%% • Taux d\'intérêt variable
+ Taux d\'intérêt variable
Aave
Moyenne %s
Rendements de l\'année dernière
@@ -1541,6 +1767,7 @@
En désactivant cela, vos fonds seront retirés d\'Aave vers %s dans votre portefeuille et vous ne gagnerez plus de récompenses.
Des frais de réseau sont prélevés par la blockchain lorsque vous quittez le mode Yield.
Désactiver le mode rendement
+ Réserves
Rendement annuel brut (APY)
APY
Les intérêts sont cumulés automatiquement.
@@ -1548,12 +1775,16 @@
Mode Rendement
Traitement de votre dépôt
Mode Rendement
+ Déployer le contrat du mode rendement
Mode rendement activé
%1$s fourni à Aave
Mode rendement désactivé
%1$s retiré d\'Aave
+ Initialisation du mode de rendement
+ Réactiver le mode Rendement
Approvisionnement à Aave
%1$s fourni à Aave
+ Retrait depuis Aave
Automatique
Déposez %1$s %2$s pour couvrir les frais de réseau liés aux transactions.
Impossible de couvrir les frais %s
diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml
index a585f2e30a..179f7018a0 100644
--- a/core/res/src/main/res/values-it/strings.xml
+++ b/core/res/src/main/res/values-it/strings.xml
@@ -68,6 +68,111 @@
Importo non valido
La commissione supera il saldo
Il totale supera il saldo
+ Saremo felici di ricevere il tuo feedback
+ Tangem Pay ora in beta
+ Carta congelata
+ Pagamento con carta
+ Deposito
+ Contestazione
+ Esplora transazione
+ Commissioni
+ Commissione
+ Proteggi il tuo denaro. Puoi sbloccarlo in qualsiasi momento.
+ Bloccare la tua carta?
+ Impossibile congelare la carta. Riprova più tardi.
+ Blocca
+ La tua carta è congelata.
+ Ottenere aiuto
+ Altro
+ Completato
+ Rifiutato
+ In sospeso
+ Termini, commissioni e limiti
+ Termini e limiti
+ La banca ha rifiutato questa richiesta di transazione.
+ Questa commissione copre il costo della gestione del tuo trasferimento.
+ Continua a usare i tuoi soldi. Puoi congelarli in qualsiasi momento.
+ Sbloccare la tua carta?
+ Impossibile sbloccare la carta. Riprova più tardi.
+ La tua carta è sbloccata.
+ Prelievo
+ Aggiungi fondi
+ Opzioni di ricarica
+ Numero carta
+ Modifica PIN
+ La carta è completamente pronta per i pagamenti.
+ Codice PIN creato
+ CVC
+ Impossibile caricare i dati. Riprova più tardi.
+ Scadenza
+ Blocca carta
+ Nascondi dettagli
+ Nascondi
+ Apri Google Wallet
+ Configura Tangem Pay in pochi tocchi e inizia a pagare con Google Pay.
+ Configura Tangem Pay in pochi tocchi e inizia a pagare con Apple Pay
+ Aggiungi la tua carta a Google Pay
+ Aggiungi la tua carta ad Apple Pay
+ Apri Google Wallet
+ Tocca il pulsante \"+\" in alto a destra
+ Apri Apple Wallet
+ Tocca « Aggiungi una carta »
+ Tocca “Carta di debito o di credito”
+ Inserisci manualmente i dettagli della carta
+ Verifica la carta utilizzando l\'OTP inviato al tuo dispositivo.
+ Tutto pronto! La tua carta è pronta per l\'uso.
+ Aggiungi carta a Google Pay
+ Aggiungi carta ad Apple Pay
+ Condividi il tuo indirizzo o mostra il QR code
+ Ricezione non disponibile al momento
+ Rivela
+ Mostra dettagli
+ Scambia qualsiasi asset nel tuo portafoglio con una carta
+ Dettagli carta
+ Sblocca carta
+ Ritiro
+ Ritiro non disponibile ora
+ Non puoi avviare uno swap o un nuovo prelievo finché quello attuale non è terminato
+ Prelievo in corso
+ Cambia codice PIN
+ Torna all\'app se lo dimentichi.
+ Impossibile emettere la carta
+ Si è verificato un errore tecnico, riprova cliccando il pulsante qui sotto
+ Si è verificato un errore tecnico, contatta il supporto
+ Utilizza le tue attività per fare acquisti nel mondo reale. \nÈ una carta di pagamento diversa da qualsiasi altra.
+ Ottieni Tangem Pay
+ Vai al supporto
+ Di solito richiede fino a 15 minuti
+ Configurazione della tua carta Tangem
+ Emissione della tua carta
+ Stiamo preparando la tua carta. Ci vorrà un po\' di tempo.
+ Tangem Pay
+ Non siamo riusciti a verificare il tuo profilo. Per domande, contatta il supporto.
+ Purtroppo non siamo riusciti a verificare la tua identità
+ KYC in corso
+ Visualizza stato
+ KYC in corso per Tangem Pay
+ Utilizza le tue attività per fare acquisti nel mondo reale. \nÈ una carta di pagamento diversa da qualsiasi altra.
+ Tangem Visa Card
+ Ottieni carta
+ Con carta digitale che funziona con Apple Pay e Google Pay
+ Spendi i tuoi asset ovunque
+ Non ci sono costi aggiuntivi per gli acquisti
+ Paga esattamente quello che vedi
+ Verrà creato un conto di pagamento separato senza divulgare i tuoi indirizzi e asset
+ Privacy senza rivali
+ Ottieni la tua carta Tangem Pay gratuita in pochi minuti
+ Payment account
+ Sincronizzazione del conto di pagamento necessaria
+ Stiamo risolvendo un problema tecnico. Riprova più tardi.
+ Servizio temporaneamente non disponibile
+ Il servizio è attualmente non raggiungibile. Riprova più tardi.
+ Sincronizzazione necessaria
+ Tangem Visa Card
+ Tangem Pay è temporaneamente non disponibile
+ Tangem Pay
+ Usa la tua carta o il tuo anello per ripristinare l\'accesso al tuo conto di pagamento
+ Il tuo codice PIN
Tangem Twin
WalletConnect
L\'indirizzo è stato copiato con successo
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 849f728af4..94e4382158 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -5,7 +5,7 @@
アクセスコードが設定されていません
コードを変更
アクセスコードは、ウォレットのロック解除・資産へのアクセス保護に使用されます
- このまま使用
+ このまま使う
このアクセスコードは簡単に推測される可能性があります
アクセスコードを入力
アクセスコードが間違っています。あと%s回間違えると、モバイルウォレットが削除されます。
@@ -144,7 +144,7 @@
残高は非表示
ブロックチェーン開発者によると、Kaspaトークンは現在ベータ版です。アップデートをお楽しみに!
ベータモード
- デバイスで生体認証がオフになっているため、ウォレットのロック解除に使用できません。\n生体認証を再度利用するには、デバイスの設定で有効にしてください。
+ デバイスで生体認証がオフになっているため、ウォレットのロック解除に使用できません。生体認証を再度利用するには、デバイスの設定で有効にしてください。
生体認証が無効になっています
カードまたはリングをスキャンしてください
生体認証の試行回数が上限に達しました。端末タップまたはアクセスコードでウォレットを解除してください。
@@ -152,7 +152,7 @@
30秒後に再試行するか、カードまたはリングをスキャンしてください
生体認証ログインは一時的にロックされています。30秒後にもう一度お試しいただくか、端末タップまたはアクセスコードでウォレットを解除してください。
試行回数が多すぎます
- お使いの携帯電話で生体認証が無効になっているため、アプリにウォレットを保存できません。ウォレットを保存するには、携帯電話の設定で生体認証機能を有効にしてください。
+ スマートフォンの生体認証が無効になっているため、アプリにウォレットを保存できません。ウォレットを保存するには、スマートフォンの設定で生体認証機能を有効にしてください。
デバイスの生体認証が更新されました。再び生体認証ログインを有効にするため、ウォレットを選び、アクセスコードを入力してください。
対応が必要です
プロモーションコードの処理中にエラーが発生しました。しばらくしてからもう一度お試しください。
@@ -217,7 +217,7 @@
許可する
金額
アナリティクス
- そして
+ と
適用する
承認
承認
@@ -245,6 +245,7 @@
近日公開
確認
接続中
+ サポートへのお問い合わせ
Tangemサポートへ問い合わせる
Visaサポートへ問い合わせる
続ける
@@ -269,7 +270,7 @@
有効にする
有効
エラー
- ネットワーク手数料
+ 入金時のネットワーク手数料
スワップ
移動する
取引履歴を調べる
@@ -366,6 +367,7 @@
利用規約
利用規約
宛先
+ %sへ
今日
- %d トークン
@@ -418,6 +420,7 @@
トークンは誰でも作成できることに注意してください。
Tangemウォレットを購入
チャット
+ Tangem Visaを入手
アクセスコード
カードをスキャンする前に、正しいアクセスコードを送信する必要があります。
長くタップ
@@ -429,7 +432,6 @@
デバイスの画面を下に向けると、残高をすばやく非表示にしたり表示したりできます。
%sハッシュ
デバイスID
- サポートへのお問い合わせ
サポートチャットを開く
他のカードをリンクする
アプリ通貨
@@ -518,7 +520,7 @@
%sを買い付けています
%sを買い付けています...
この取引を非表示にする
- 一度非表示にすると、取引状況を再度表示することはできません。代わりに、スワイプして閉じることができます。
+ この取引を非表示にすると、ステータス画面には表示されなくなります。ステータス画面を閉じて後で戻りたい場合は、画面をスワイプして閉じてください。
取引状況を非表示にしますか?
このトークンはサポートされていません。別のトークンを選択してスワップしてください。
%sはサポートされていません
@@ -684,11 +686,13 @@
APY %s
私のポートフォリオ
マーケット
+ Tangemで稼ぐ
選択したネットワークのアドレスを生成するには、Tangemウォレットカードまたはリングをスキャンする必要があります。
トークンを追加するには、これをスワイプするか、検索バーをタップしてください。
このセクションのデータは、次のネットワークから取得されています: %s
データを読み込めません…
データなし
+ マーケット動向
クイックアクション
マーケットから探す
結果
@@ -777,11 +781,17 @@
取引量
これをドラッグするか、検索窓をタップして、マーケットから直接トークンを追加します
トークンを追加
- 資産を即時アクセス可能な状態に保ったまま、パワーアップさせよう。%s
+ 資産を常時アクセス可能な状態に保ったまま、パワーアップさせよう。%s
利息モードを有効にする
モバイルウォレットを作成するには、%1$sにアップデートする必要があります
モバイルウォレットを使用するには、%1$s以降が必要です
すべてのニュース
+
+ - %d時間前
+
+
+ - %d分前
+
最新情報を入手
お使いのデバイスではNFCが使用できません
NFTについて
@@ -837,6 +847,9 @@
送信先アカウントには、送金されるアセットのトラストラインがありません。
ウォレットごとに$10相当のBTCをプレゼント\nお早めに!
ブラックフライデー:最大30% オフ
+ 今すぐチェック
+ 理想のTangemセットを揃えよう。期間限定。
+ 1+1:1つ購入で、2つ目が50%オフ
今すぐ参加
コードを共有すると、販売ごとに5 USDTを獲得できます。お友達は10%割引になります。
友達への紹介で報酬を獲得しよう!
@@ -849,7 +862,7 @@
保護する
後で各カードおよびリングに個別のアクセスコードを設定できます。
パーソナライズする
- アクセスコードは、リンクされたカードおよびリング1つで復元できます。すべてのデバイスを1か所に保管しないでください。
+ リンクされたカードまたはリングを使って、アクセスコードを復元できます。すべてのデバイスを同じ場所に保管しないでください。
復元する
アクセスコードとして任意の単語、フレーズ、または数字を選択してください
アクセスコードの作成
@@ -1193,6 +1206,8 @@
ネットワーク手数料とは、ブロックチェーン上で取引を処理し、承認するために支払う少額の料金です。
ステーキングを始めるには、1 TONを取引してTONアカウントを有効化する必要があります。資金はウォレット内にそのまま残ります。これは、ステーキング有効化のためのステップにすぎません。
アカウントの有効化
+ ネットワーク手数料が変更されました。続行する前に新しい金額をご確認ください。
+ ネットワーク手数料が更新されました
ステーキング金額は %s 以上である必要があります
ネットワークルールにより、ステーキング金額は%1$s TRX に切り上げられます。
ネットワークルールにより、ステーキング解除の量は%1$s TRX に切り上げられます。
@@ -1230,6 +1245,7 @@
ネットワークは、ステーキングのためにトークンの使用を承認していることを確認するために、トークン承認手数料を請求します。
ステーキング機能を使用すると、プロバイダーの%1$sと%2$sに同意したことになります
ロック中
+ 最大金額:%s
移行
ネイティブステーキング
現在、ステーキングに使用できるアクティブなバリデータは見つかりません。しばらくしてからもう一度お試しください。
@@ -1366,6 +1382,8 @@
受け取る
トークンを選択
利用不可
+ 皆様からのフィードバックをお待ちしております
+ Tangem Payのベータ版を公開しました
カードが凍結されています
カード決済
入金
@@ -1379,6 +1397,7 @@
一時停止
カードが凍結されています
サポートを受ける
+ その他
完了
拒否
保留中
@@ -1430,11 +1449,14 @@
現在、出金ができません
現在の処理が完了するまでは、スワップや新しい出金を開始することはできません。
出金処理中です
+ PINコードを変更
+ 忘れた場合はアプリに戻って確認できます。
カードの発行に失敗しました
技術的なエラーが発生しました。下のボタンをクリックして、もう一度お試しください。
技術的なエラーが発生しました。サポートへお問い合わせください。
暗号資産を日常の支払いに使おう。\n\nこれまでにないタイプの決済カード。
Tangem Payを入手
+ サポートへ移動
通常は最大で15分ほどかかります
Tangemカードのセットアップ
カードを発行しています
@@ -1442,8 +1464,11 @@
Tangem Pay
プロフィールを確認できませんでした。ご不明な点があればサポートまでお問い合わせください。
申し訳ございませんが、本人確認を行うことができませんでした
+ KYC進行中
ステータスを表示
Tangem PayのKYC手続き進行中
+ 暗号資産を、リアルな支払いに。\n他とはまったく違う、新しいタイプの決済カード。
+ Tangem Visaカード
カードをGET
Apple PayとGoogle Payに対応したデジタルカード付き
どこでも暗号資産を使える
@@ -1457,10 +1482,12 @@
技術的な問題を修正しています。後でもう一度お試しください。
サービスは一時的に利用できません
現在サービスに接続できません。後ほどもう一度お試しください。
+ 同期が必要です
Tangem Visaカード
Tangem Payは一時的に利用できません
Tangem Pay
カードまたはリングを使用して、支払いアカウントへのアクセスを復元してください。
+ PINコード
これは私のウォレットです
残高非表示
残高表示
@@ -1555,6 +1582,7 @@
- %d日間利用可能
残高と限度額
+ アクセスコードは、支払いアカウントの管理および不正アクセスからの保護に使用されます。
アクセスコード
本当に終了してもよろしいですか?中断したところから後で続行できます。
長くはかかりません。アカウントを設定しています。
@@ -1851,7 +1879,7 @@
ワンタップで開始
シームレスで安全
シンプルな操作
- Tangemでハードウェアウォレットを作成しましょう。銀行のカードのようにスリムで、金庫のように安全です。
+ Tangemでハードウェアウォレットを作成しよう。キャッシュカードのようにスリムで、金庫のように安全。
ソフトウェアウォレットを作成またはインポート
スマートフォン上にソフトウェアウォレットを作成またはインポートする。
モバイルウォレットから始める
@@ -1908,6 +1936,8 @@
入金手数料ポリシー
Tangemは、生成された利息に対して15%サービス手数料も徴収します。
ネットワーク手数料が下がるか、残高が最低必要額に達すると、資金は自動的にAaveに供給されます。
+ 市場が非常に活発なため、現在の手数料は通常よりも高くなっています。今すぐ続行するか、手数料が下がるのを待って後で再確認することもできます。
+ ネットワーク手数料が高額です
過去のリターン
保有資産に年利%1$s%%を適用
利息モードでのトークンの承認が取り消されました。トークンを開いて再度許可してください。
@@ -1924,7 +1954,7 @@
分散型・自己管理型
このサービスを利用することにより、プロバイダー\n%1$sおよび%2$sに同意するものとします。
Aaveに接続
- 残高に %1$s%% の年利(APY)を適用
+ 保有資産に\n年利%1$s%%を適用
Aave %1$s%% • 変動金利
変動金利
Aave
@@ -1954,12 +1984,12 @@
利息モードの有効化
利息モード
利息モードコントラクトのデプロイ
- 利息モードを有効にする
+ 利息モードが有効になりました
%1$sがAaveに供給されました
- 利息モードを無効にする
+ 利息モードが無効になりました
%1$sがAaveから引き出されました
- 利息モードをセットアップ
- 利息モードの再有効化
+ 利息モードを初期化しました
+ 利息モードが再度有効になりました
Aaveへの供給
%1$sがAaveに供給されました
Aaveから引き出す
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 62446f8321..b5587d3945 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -3,6 +3,10 @@
Ваш кошелёк не защищён без кода доступа.
Сбросить все равно
Код доступа не задан
+ Изменить код
+ Ваш код доступа будет использоваться для разблокировки кошелька и защиты доступа к вашим активам.
+ Использовать
+ Код доступа может быть легко угадан
Введите код доступа
Неверный код доступа. Ваш мобильный кошелёк будет удалён после ещё %s неверных попыток.
Подтвердите ваш код доступа, чтобы продолжить.
@@ -14,6 +18,7 @@
Невозможно добавить новый аккаунт.
Архивированные аккаунты
Восстановить
+ Восстановить аккаунт
Лимит в 20 активных аккаунтов достигнут. Пожалуйста, зархивируйте один аккаунт, чтобы продолжить.
Невозможно восстановить аккаунт
В архиве
@@ -36,6 +41,7 @@
Новый аккаунт
Добавить аккаунт
Редактировать аккаунт
+ Попробуйте позже. Если проблема повторится, обратитесь в поддержку — мы поможем её решить.
%1$s в %2$s
Основной аккаунт
Лимит %1$s активных аккаунтов превышен. Архивируйте один, чтобы продолжить.
@@ -113,7 +119,7 @@
Вы успешно завершили бэкап вашего кошелька.
Эти слова невозможно восстановить, если они будут потеряны. Храните их в надёжном месте.
Бэкап завершен
- Ваша секретная фраза восстановления — это фиксированный набор из %s случайных слов для доступа к вашему кошельку и его восстановления.
+ Ваша фраза восстановления — фиксированный набор из %s случайных слов для доступа и восстановления кошелька.
Эти слова невозможно восстановить, если они будут потеряны. Храните их в безопасности.
Храните в безопасности
Сохраните эти %s слов в безопасном месте и никому их не сообщайте.
@@ -129,10 +135,16 @@
Балансы скрыты
Согласно информации от разработчиков сети, токены Kaspa находятся в режиме бета. Следите за обновлениями!
Бета режим
+ Биометрия отключена на вашем устройстве, поэтому вы не можете использовать её для разблокировки кошельков. Включите биометрию в настройках устройства, чтобы снова использовать этот способ.
+ Биометрическая аутентификации отключена
Пожалуйста, отсканируйте карту или кольцо
+ Вы достигли лимита попыток биометрии. Разблокируйте кошелёк с помощью касания устройства или введите код доступа.
+ Биометрическая аутентификации заблокирована
Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту или кольцо
+ Вход по биометрии временно заблокирован. Попробуйте снова через 30 секунд или разблокируйте кошелёк с помощью прикладывания устройства или кода доступа.
Слишком много попыток
Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона.
+ Внимание
При обработке промокода произошла ошибка. Пожалуйста, попробуйте позже.
Ошибка активации
Ваш промокод был успешно активирован. Награда будет зачислена на ваш Bitcoin адрес в течение 14 дней.
@@ -207,7 +219,7 @@
Разрешить
Внимание
Доступные сети
- Резервное копирование
+ Резервная копия
Баланс: %s
Баланс
биометрическую аутентификацию
@@ -229,6 +241,7 @@
Скоро появится
Подтвердить
Подключение
+ Обратиться в поддержку
Продолжить
Конвертировать
Копировать
@@ -419,7 +432,6 @@
Переверните экран вашего устройства вниз, чтобы быстро скрыть и отобразить балансы
%s хэшей
Номер устройства
- Обратиться в поддержку
Открыть чат поддержки
Добавить еще карты
Валюта приложения
@@ -576,16 +588,16 @@
Сначала завершите создание резервной копии
Не завершено
Другие способы
- Сохраните свою recovery-фразу вручную в надёжном месте. Держите её в секрете, чтобы защитить свои средства.
+ Сохраните фразу восстановления в безопасном месте и держите её в секрете.
Фраза восстановления
Чтобы защитить ваш кошелёк с помощью кода доступа, сначала завершите резервное копирование.
Чтобы улучшить кошелёк до аппаратного, сначала создайте резервную копию.
Ваши приватные ключи надёжно зашифрованы и хранятся на вашем телефоне
Ключи хранятся в приложении
- Создайте или восстановите свой кошелёк с помощью фразы восстановления — вашей встроенной резервной копии
- Резервная копия сид-фразы
+ Создайте или импортируйте кошелёк с помощью вашей фразы восстановления.
+ Резервная копия
Создать мобильный кошелек
- Импортировать существующий кошелек
+ Импортировать существующий
Эта фраза восстановления уже была импортирована
Мобильный кошелек
Забыть кошелек
@@ -602,7 +614,7 @@
Я понимаю, что если я не создал резервную копию кошелька перед его удалением, я могу потерять к нему доступ.
Я понимаю, что удаление моего кошелька не стирает его — оно просто удаляет его с моего устройства.
Фраза восстановления больше не нужна — ваша карта или кольцо Tangem становятся вашей надёжной резервной копией.
- Резервное копирование с Tangem
+ Резервное копирование
Это устройство не может быть использовано для апгрейда, оно уже содержит другой кошелек.
Выберите другое устройство. Это нельзя использовать для обновления.
Во время операции произошла ошибка.
@@ -680,6 +692,7 @@
Данные раздела получены из следующих сетей: %s
Невозможно загрузить данные
Нет данных
+ Пульс рынка
Быстрые действия
Поиск на рынке
Результат
@@ -774,6 +787,10 @@
Объем
Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка
Добавить токены
+ Увеличивайте доход с активов, сохраняя мгновенный доступ к ним. %s
+ Активировать режим доходности
+ Обновитесь до версии %1$s, чтобы создать мобильный кошелёк
+ Мобильный кошелек требует %1$s или новее.
Функция NFC недоступна на вашем устройстве
О NFT
NFT
@@ -834,6 +851,9 @@
Аккаунт получателя не содержит трастлайна для отправляемого актива.
Получите $10 в BTC за каждый кошелёк.\nПоторопитесь!
Чёрная пятница: скидки до 30%
+ Купить
+ Время ограничено!
+ 1+1: Купи один кошелёк — получи 50% скидку
Присоединиться
Поделись промокодом — заработай 5 USDT с каждой покупки. Твои друзья получат скидку 10% на карту Tangem!
Получай бонусы за каждого друга!
@@ -1379,12 +1399,112 @@
Вы получите
Выберите токен
не доступен
- Пополнить
+ Будем рады вашей обратной связи
+ Tangem Pay в режиме beta
+ Карта заморожена
+ Оплата картой
+ Пополнение
+ Оспорить
+ Посмотреть в обозревателе
+ Сервисная комиссия
Комиссия
- Получить помощь
- Скрыть детали
- Всё готово! Ваша карта готова к использованию.
- Получить карту
+ Всегда можно разморозить
+ Заморозить карту?
+ Не удалось заморозить карту, попробуйте еще раз
+ Заморозить
+ Карта заморожена
+ Обратиться в поддержку
+ Другое
+ Успешно завершено
+ Отклонено
+ В процессе
+ Тарифы и полные условия
+ Тарифы и лимиты
+ Банк отклонил транзакцию
+ Эта комиссия покрывает стоимость обработки вашего перевода.
+ Продолжайте пользоваться картой, заморозить всегда успеете
+ Разморозить карту?
+ Не удалось разморозить карту, попробуйте еще раз
+ Карта разморожена
+ Вывод
+ Пополнить
+ Способы пополнения
+ Номер
+ Сменить ПИН
+ Карта готова к покупкам
+ ПИН-код установлен
+ CVC
+ Не удалось загрузить данные. Повторите попытку позже.
+ Срок
+ Заморозить карту
+ Скрыть
+ Скрыть
+ Открыть Google Wallet
+ Настройте в пару кликов и начините платить
+ Настройте в пару кликов и начините платить
+ Добавьте карту в Google Pay
+ Добавьте карту в Apple Pay
+ Откройте Google Wallet
+ Нажмите на кнопку “+” сверху справа
+ Откройте Apple Wallet
+ Нажмите \"Добавить карту\"
+ Выберите \"Дебетовая или кредитная карта\"
+ Заполните данные карты вручную
+ Введите код подтверждения, направленный в email или СМС
+ Всё готово! Можно пользоваться картой
+ Добавьте карту в Google Pay
+ Добавить карту в Apple Pay
+ Скопируйте свой адрес или покажите QR
+ Техническая ошибка. Попробуйте позже или обратитесь в поддержку.
+ Пополнение недоступно
+ Показать
+ Реквизиты
+ Пополните карту любым активом через обмен
+ Реквизиты
+ Разморозить карту
+ Вывод
+ Вывод сейчас недоступен
+ Вы не можете начать обмен или новый вывод, пока не завершится текущий.
+ Вывод выполняется
+ Изменить PIN-код
+ Можно посмотреть здесь, если забудете его.
+ Не удалось выпустить карту
+ Техническая ошибка, попробуйте ещё раз, нажав кнопку ниже
+ Техническая ошибка, свяжитесь с поддержкой
+ Используйте криптовалюту в реальной жизни. \nКарта, не похожая ни на одну другую.
+ Получить Tangem Pay
+ Написать в поддержку
+ Обычно это занимает до 15 минут
+ Готовим вашу Tangem Card
+ Выпускаем карту
+ Выпускаем карту, это займет немного времени.
+ Tangem Pay
+ Не удалось подтвердить ваш профиль. Если у вас есть вопросы, обратитесь в службу поддержки.
+ К сожалению, нам не удалось подтвердить вашу личность
+ KYC в процессе
+ Посмотреть статус
+ KYC в процессе для Tangem Pay
+ Используйте криптовалюту в реальной жизни. \nКарта, не похожая ни на одну другую.
+ Tangem Visa Card
+ Открыть карту
+ Виртуальную карту можно добавить в Apple Pay и Google Pay
+ Покупайте где угодно
+ Никаких дополнительных комиссий за покупки
+ Сколько видишь – столько платишь
+ Мы создадим отдельный платежный счет без раскрытия ваших активов \nи их адресов
+ Абсолютная приватность
+ Откройте виртуальную \nTangem Pay Card
+ Payment account
+ Требуется синхронизация платежного счета
+ Мы устраняем техническую проблему. Пожалуйста, попробуйте позже.
+ Сервис временно недоступен
+ Сервис временно недоступен. Пожалуйста, попробуйте позже.
+ Требуется синхронизация
+ Tangem Visa Card
+ Tangem Pay временно недоступен
+ Tangem Pay
+ Используйте вашу карту или кольцо для восстановления доступа к платежному счету
+ Ваш PIN-код
Это мой кошелек
Балансы скрыты
Балансы показаны
@@ -1428,6 +1548,7 @@
Несколько адресов
История транзакций в настоящее время не поддерживается для этого блокчейна. Но не волнуйтесь, мы работаем над этим! А пока вы можете проверить ее в обозревателе.
Операция
+ для: %s
от: %s
на: %s
валидатор: %s
@@ -1814,6 +1935,7 @@
Tangem взимает комиссию за обслуживание в размере 15% от полученного дохода.
Ваши средства будут автоматически переведены в Aave, как только комиссия сети снизится или баланс достигнет минимально необходимой суммы.
Историческая доходность
+ Получай до %1$s APY на свой баланс
Разрешение для вашего токена в режиме доходности было отозвано. Откройте токен, чтобы выдать разрешение снова.
Необходимо разрешение для токена
Проверьте ваше интернет соединение
@@ -1828,7 +1950,9 @@
Децентрализованный и некастодиальный
Используя сервис, вы соглашаетесь с %1$s и %2$s
Подключить Aave
+ Подключите %1$s %% APY\nна ваш баланс
Aave %1$s%% • Плавающая ставка
+ Динамическая процентная ставка
Aave
Сред. %s
Доходность за прошлый год
@@ -1855,12 +1979,16 @@
Режим доходности
Включение режима доходности
Режим доходности
- Включение режима доходности
+ Установка контракта режима доходности
+ Режим доходности подключен
%1$s отправлено в Aave
- Отключение режима доходности
+ Режим доходности отключен
%1$s выведено из Aave
+ Режим доходности инициализирован
+ Режим доходности реактивирован
Перевод средств в Aave
%1$s отправлено в Aave
+ Вывод из Aave
Автоматически
Внесите немного %1$s %2$s, чтобы покрыть комиссию сети за транзакции.
Невозможно покрыть комиссию в %s
diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml
index 3bb44e8897..98ddcc642a 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -133,6 +133,7 @@
Закрити
Підтвердити
Підключення
+ Звернутися у підтримку
Продовжити
Конвертувати
Копіювати
@@ -298,7 +299,6 @@
Переверніть екран пристрою вниз, щоб швидко приховати та відобразити баланси
%s хешів
Номер пристрою
- Звернутися у підтримку
Додати більше карток
Валюта застосунку
Приховувати баланси жестом перевороту
@@ -1127,6 +1127,22 @@
Ви отримаєте
Оберіть токен
недоступно
+ Номер картки
+ CVC
+ Термін дії
+ Сховати деталі
+ Додайте свою картку в Apple Pay
+ Натисніть кнопку \"+\" у верхньому правому куті
+ Відкрити Apple Wallet
+ Натисніть «Дебетова або кредитна картка»
+ Все готово! Ваша картка готова до використання.
+ Додайте свою картку в Apple Pay
+ Показати деталі
+ Обміняйте будь-який актив у вашому портфелі на картку
+ Розморозити картку
+ Tangem Visa Card
+ Ми усуваємо технічну проблему. Будь ласка, спробуйте пізніше.
+ Сервіс тимчасово недоступний
Це мій гаманець
Баланси приховано
Баланси показано
diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml
index 2e0760aa10..b2f04957dd 100644
--- a/core/res/src/main/res/values-zh-rTW/strings.xml
+++ b/core/res/src/main/res/values-zh-rTW/strings.xml
@@ -311,6 +311,111 @@
交易
選擇代幣
無法使用
+ 我們期待聆聽您的寶貴意見
+ Tangem Pay現已開放測試版
+ 卡片已凍結
+ 信用卡支付
+ 充值
+ 爭議
+ 探索交易
+ 服務費
+ 手續費
+ 保護資金安全,隨時可解凍
+ 凍結您的卡片?
+ 無法凍結卡片。請稍後再試。
+ 凍結
+ 您的卡片已凍結。
+ 獲取幫助
+ 其他
+ 已完成
+ 已拒絕
+ 處理中
+ 條款、費用與限制
+ 條款與限制
+ 銀行拒絕了此交易請求。
+ 此費用用於支付處理您轉帳的成本。
+ 繼續使用您的資金。您可以隨時凍結。
+ 解凍您的卡片?
+ 無法解凍卡片。請稍後再試。
+ 您的卡片已解凍。
+ 提現
+ 添加资金
+ 充值选项
+ 卡號
+ 修改 PIN
+ 卡片已完全准备好进行支付。
+ PIN 碼已建立
+ CVC
+ 無法載入資料。請稍後再試。
+ 有效期
+ 冻结卡片
+ 隱藏詳情
+ 隐藏
+ 打開 Google Wallet
+ 只需幾次點擊即可設置 Tangem Pay 並開始使用 Google Pay 付款。
+ 设置Tangem Pay只需几次点击,即可开始使用Apple Pay支付
+ 将您的卡片添加到 Google Pay
+ 將卡片添加到 Apple Pay
+ 打開 Google Wallet
+ 点击右上角的“+”按钮
+ 打开Apple Wallet
+ 点击“添加卡片”
+ 點擊「借記卡或信用卡」
+ 手動輸入卡片詳情
+ 使用發送到您設備的 OTP 驗證卡片。
+ 全部完成!您的卡片已準備就緒。
+ 將卡片添加到 Google Pay
+ 添加卡片到 Apple Pay
+ 分享您的地址或显示二维码
+ 暫時無法接收
+ 显示
+ 顯示詳情
+ 將您投資組合中的任何資產兌換成卡片
+ 卡片详情
+ 解凍卡片
+ 提现
+ 目前无法提现
+ 在当前操作完成之前,您无法启动兑换或新的提款。
+ 提款进行中
+ 修改PIN码
+ 如果忘记了,请返回应用查看。
+ 无法发行卡片
+ 出现技术错误,请点击下方按钮重试
+ 出现技术错误,请联系客服
+ 使用您的加密货币进行真实世界消费。\n这是一张与众不同的支付卡。
+ 获取Tangem Pay
+ 前往客服中心
+ 通常需要最多15分钟
+ 設置您的 Tangem 卡
+ 正在发行您的卡片
+ 正在为您准备卡片,这可能需要一些时间
+ Tangem Pay
+ 我们无法验证您的资料。如有任何疑问,请联系客服。
+ 很抱歉,我们无法验证您的身份
+ KYC进行中
+ 查看状态
+ Tangem Pay 的 KYC 正在進行中
+ 使用您的加密货币进行真实世界消费。这是一张与众不同的支付卡。
+ Tangem Visa Card
+ 获取卡片
+ 使用支援 Apple Pay 和 Google Pay 的數位卡
+ 在任何地方花费您的资产
+ 購物無額外費用
+ 所見即所付
+ 將創建單獨的支付帳戶,且不會透露您的地址和資產
+ 無與倫比的隱私
+ 在幾分鐘內獲得免費的 Tangem Pay 卡
+ Payment account
+ 需要同步支付账户
+ 我们正在修复技术问题。请稍后再试。
+ 服務暫時無法使用
+ 服务当前不可用,请稍后再试
+ 需要同步
+ Tangem Visa Card
+ Tangem Pay暂时不可用
+ Tangem Pay
+ 使用您的卡片或戒指恢复对支付账户的访问
+ 您的PIN码
隱藏
您即將在主屏幕上隱藏此代幣。您可以隨時通過管理代幣頁面將其添加回來。
隱藏 %s
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 0bbcf90b6a..8c52e119ff 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -248,6 +248,7 @@
Coming soon
Confirm
Connecting
+ Contact support
Contact Tangem Support
Contact Visa Support
Continue
@@ -427,6 +428,7 @@
Note that tokens can be created by anyone
Buy Tangem Wallet
Chat
+ Get Tangem Visa
Access code
You will have to submit the correct access code before scanning the card
Long Tap
@@ -438,7 +440,6 @@
Flip your device screen down to quickly hide and show balances
%s hashes
Device ID
- Contact support
Open support chat
Link More Cards
App Currency
@@ -602,7 +603,7 @@
Other methods
Manually save your recovery phrase in a secure place and keep it private to protect your funds.
Recovery phrase
- To secure your wallet with a Access Code, complete the backup first.
+ To secure your wallet with an Access Code, complete the backup first.
To upgrade your wallet to hardware, back it up first.
Your private keys are securely encrypted and stored on your phone
Keys are stored in the app
@@ -799,6 +800,14 @@
You must update to %1$s in order to create mobile wallet
Mobile Wallet requires %1$s or later
All news
+
+
+ - %dh ago
+
+
+ - %d minute ago
+ - %d minutes ago
+
Stay in the loop
NFC is not available on your device
About NFT
@@ -856,6 +865,9 @@
The destination account does not have a trustline for the asset being sent.
Get $10 in BTC with every wallet\nHurry!
Black Friday: up to 30% OFF
+ Let’s go
+ Limited time!
+ 1+1: Buy One Wallet, Get 50% OFF
Join Now
Share your code - earn 5 USDT per sale. Your friend gets 10% OFF.
Get REWARDS for every friend!
@@ -1218,6 +1230,8 @@
A network fee is a small payment required to process and confirm your transaction on the blockchain.
To begin staking, your TON account must be activated with a transaction of 1 TON. The funds stay in your account because this step only activates it for staking.
Account activation
+ The network fee has changed. Please review the new amount before proceeding.
+ Network fee updated
The amount to stake must be at least %s
Staking amount will be rounded to %1$s TRX due to network rules.
Unstaking amount will be rounded to %1$s TRX due to network rules.
@@ -1256,6 +1270,7 @@
The network will charge a token approval fee to verify that you are authorizing the use of your token for the staking.
By using staking functionality, you agree with provider’s %1$s and %2$s
Locked
+ Maximum amount: %s
Migrate
Native staking
No active validators available for staking at the moment. Please try again later.
@@ -1392,6 +1407,8 @@
You receive
Choose token
not available
+ We would be happy to receive your feedback
+ Tangem Pay is now in beta
Card frozen
Card payment
Deposit
@@ -1399,7 +1416,7 @@
Explore transaction
Service fees
Fee
- Keep your money safe if your card is lost or stolen. You can unfreeze anytime.
+ Keep your money safe. You can unfreeze anytime.
Freeze your card?
Failed to freeze the card. Try again later.
Freeze
@@ -1434,14 +1451,14 @@
Set up Tangem Pay in a few taps and start paying with Google Pay.
Set up Tangem Pay in a few taps and start paying with Apple Pay.
Add your card to Google Pay
- Add your card to Apple Pay
+ Add your card to Apple Pay
Open Google Wallet
Tap “+” button on the top right
Open Apple Wallet
Tap “Add a card”
Tap “Debit or Credit Card”
Enter the card details manually
- Verify card using the OTP sent to your device.
+ Verify card using the OTP sent to your device.
All set! Your card is ready to use.
Add card to Google Pay
Add card to Apple Pay
@@ -1457,11 +1474,14 @@
Withdraw unavailable now
You can\'t initiate swap or new withdrawal till the current one is finished
Withdrawal in progress
+ Change PIN-code
+ Come back to the app if you forget it.
Failed to issue card
A technical error has occurred, please try again by clicking the button below.
A technical error has occurred, please contact support.
Use your crypto for real world spending. \nIt\'s a payment card unlike any other.
Get Tangem Pay
+ Go to Support
It usually takes up to 15 minutes
Setting up your Tangem Card
Issuing your card
@@ -1472,6 +1492,8 @@
KYC in progress
View Status
KYC in progress for Tangem Pay
+ Use your crypto for real world spending. \nIt’s a payment card unlike any other.
+ Tangem Visa Card
Get card
With digital card that works with Apple Pay and Google Pay
Spend your assets anywhere
@@ -1490,6 +1512,7 @@
Tangem Pay is temporarily unavailable
Tangem Pay
Use your card or ring to restore access to your payment account
+ Your PIN code
This is my wallet
Balances hidden
Balances shown
@@ -1986,6 +2009,8 @@
Top-up fee policy
Tangem also takes a 15% service fee on yield generated.
Your funds will be automatically supplied to Aave once network fees are lower or your balance meets the minimum required amount.
+ Fees are higher than usual because the market is very active. You can proceed now or check back later when the fees are lower.
+ High Network Fees
Historical returns
Enable %1$s%% APY on your balance
Approval for your token in Yield Mode has been revoked. Open the token to grant permission again.
@@ -2032,12 +2057,12 @@
Enabling Yield Mode
Yield Mode
Yield Mode contract deploy
- Yield Mode enable
+ Yield Mode enabled
%1$s supplied to Aave
- Yield Mode disable
+ Yield Mode disabled
%1$s withdrawn from Aave
- Yield Mode initialize
- Yield Mode reactivate
+ Yield Mode initialized
+ Yield Mode reactivated
Supply to Aave
%1$s supplied to Aave
Withdraw from Aave
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt
index e7644ee705..25b0a54929 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt
@@ -61,7 +61,6 @@ fun NetworkTitle(
) {
Box(
modifier = Modifier
- .weight(weight = 1f)
.heightIn(min = TangemTheme.dimens.size20),
contentAlignment = Alignment.CenterStart,
content = title,
@@ -99,4 +98,4 @@ private fun NetworkTitlePreview(@PreviewParameter(NetworkTitleIconVisibilityProv
}
}
-private object NetworkTitleIconVisibilityProvider : CollectionPreviewParameterProvider(listOf(true, false))
\ No newline at end of file
+private class NetworkTitleIconVisibilityProvider : CollectionPreviewParameterProvider(listOf(true, false))
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/internal/GroupTitleItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/internal/GroupTitleItem.kt
index a3f882b7ba..719f825aed 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/internal/GroupTitleItem.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/internal/GroupTitleItem.kt
@@ -124,4 +124,4 @@ private fun NetworkTitleItemPreview(@PreviewParameter(GroupTitleItemProvider::cl
}
}
-private object GroupTitleItemProvider : CollectionPreviewParameterProvider(collection = listOf(true, false))
\ No newline at end of file
+private class GroupTitleItemProvider : CollectionPreviewParameterProvider(collection = listOf(true, false))
\ No newline at end of file
diff --git a/core/ui/src/main/res/drawable/img_one_plus_one_promo.webp b/core/ui/src/main/res/drawable/img_one_plus_one_promo.webp
new file mode 100644
index 0000000000..729a3af0c7
Binary files /dev/null and b/core/ui/src/main/res/drawable/img_one_plus_one_promo.webp differ
diff --git a/core/ui/src/main/res/drawable/img_visa_label_26_16.xml b/core/ui/src/main/res/drawable/img_visa_label_26_16.xml
deleted file mode 100644
index c5859a2fda..0000000000
--- a/core/ui/src/main/res/drawable/img_visa_label_26_16.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
diff --git a/core/ui/src/main/res/drawable/img_visa_notification.webp b/core/ui/src/main/res/drawable/img_visa_notification.webp
new file mode 100644
index 0000000000..b0b4afb8b6
Binary files /dev/null and b/core/ui/src/main/res/drawable/img_visa_notification.webp differ
diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/StringExt.kt b/core/utils/src/main/java/com/tangem/utils/extensions/StringExt.kt
index bba0975985..fd6c6e5846 100644
--- a/core/utils/src/main/java/com/tangem/utils/extensions/StringExt.kt
+++ b/core/utils/src/main/java/com/tangem/utils/extensions/StringExt.kt
@@ -9,4 +9,8 @@ fun String.uriValidate(): Boolean {
val regex = DEEPLINK_VALIDATION_REGEX.toRegex()
return !regex.containsMatchIn(this)
+}
+
+fun String.addHexPrefix(): String {
+ return if (this.startsWith("0x")) this else "0x$this"
}
\ No newline at end of file
diff --git a/data/account/detekt-baseline-debug.xml b/data/account/detekt-baseline-debug.xml
index f0b34d22a4..014cb3a866 100644
--- a/data/account/detekt-baseline-debug.xml
+++ b/data/account/detekt-baseline-debug.xml
@@ -6,8 +6,6 @@
MultilineLambdaItParameter:DefaultAccountsCRUDRepository.kt$DefaultAccountsCRUDRepository${ if (it is HttpException && it.code == HttpException.Code.NOT_MODIFIED) { null } else { throw it } }
MultilineLambdaItParameter:GetWalletAccountsResponseExt.kt${ enrichedTokensByAccountId[it].orEmpty().map { token -> // Tokens from unexisting accounts should be copied to the main account token.copy(accountId = accountDTO.id) } }
NoNameShadowing:GetWalletAccountsResponseExt.kt$tokens
- NullableToStringCall:AccountListCryptoCurrenciesProducer.kt$AccountListCryptoCurrenciesProducer$${this::class.simpleName}
- NullableToStringCall:DefaultMultiWalletCryptoCurrenciesProducer.kt$DefaultMultiWalletCryptoCurrenciesProducer$${this::class.simpleName}
UnnecessaryLet:DefaultAccountsCRUDRepository.kt$DefaultAccountsCRUDRepository$let(AccountName::invoke)
diff --git a/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt b/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt
index 99d4ac7ff6..3bc99dc784 100644
--- a/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt
+++ b/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt
@@ -115,7 +115,7 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
saveETag(userWalletId, apiResponse)
- apiResponse.bind()
+ apiResponse.bind().enrichByAccountId()
},
onError = { error ->
if (error.isNetworkError(code = Code.PRECONDITION_FAILED)) {
@@ -142,10 +142,11 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
saveETag(userWalletId, apiResponse)
- val responseBody = apiResponse.bind()
- store(userWalletId = userWalletId, response = responseBody)
+ val response = apiResponse.bind().enrichByAccountId()
- FetchResult(responseBody)
+ store(userWalletId = userWalletId, response = response)
+
+ FetchResult(response)
},
onError = { throwable ->
// pushWalletAccounts and storeWalletAccounts help to avoid cyclic dependency
@@ -215,6 +216,18 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
}
}
+ private fun GetWalletAccountsResponse.enrichByAccountId(): GetWalletAccountsResponse {
+ return copy(
+ accounts = accounts.map { accountDTO ->
+ accountDTO.copy(
+ tokens = accountDTO.tokens?.map { token ->
+ token.copy(accountId = accountDTO.id)
+ },
+ )
+ },
+ )
+ }
+
private fun getAccountsResponseStore(userWalletId: UserWalletId): AccountsResponseStore {
return accountsResponseStoreFactory.create(userWalletId = userWalletId)
}
diff --git a/data/account/src/main/kotlin/com/tangem/data/account/producer/AccountListCryptoCurrenciesProducer.kt b/data/account/src/main/kotlin/com/tangem/data/account/producer/AccountListCryptoCurrenciesProducer.kt
index 9722001f29..4cd149ae75 100644
--- a/data/account/src/main/kotlin/com/tangem/data/account/producer/AccountListCryptoCurrenciesProducer.kt
+++ b/data/account/src/main/kotlin/com/tangem/data/account/producer/AccountListCryptoCurrenciesProducer.kt
@@ -36,11 +36,12 @@ internal class AccountListCryptoCurrenciesProducer @AssistedInject constructor(
override val fallback: Option> = emptySet().some()
+ @Suppress("NullableToStringCall")
override fun produce(): Flow> {
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
if (!userWallet.isMultiCurrency) {
- error("${this::class.simpleName} supports only multi-currency wallet")
+ error("${this::class.simpleName ?: this::class.toString()} supports only multi-currency wallet")
}
return accountsResponseStoreFactory.create(userWalletId = userWallet.walletId).data
@@ -49,10 +50,13 @@ internal class AccountListCryptoCurrenciesProducer @AssistedInject constructor(
if (response == null) return@map emptySet()
response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
+ val accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull()
+ ?: return@map emptySet()
+
responseCryptoCurrenciesFactory.createCurrencies(
tokens = accountDTO.tokens.orEmpty(),
userWallet = userWallet,
- accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull(),
+ accountIndex = accountIndex,
)
}
}
diff --git a/data/account/src/main/kotlin/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducer.kt b/data/account/src/main/kotlin/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducer.kt
index 8c46088747..25255d60b4 100644
--- a/data/account/src/main/kotlin/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducer.kt
+++ b/data/account/src/main/kotlin/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducer.kt
@@ -5,6 +5,7 @@ import arrow.core.some
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
+import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
@@ -39,7 +40,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducer @AssistedInject constr
val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId)
if (!userWallet.isMultiCurrency) {
- error("${this::class.simpleName} supports only multi-currency wallet")
+ error("${this::class.simpleName ?: this::class.toString()} supports only multi-currency wallet")
}
return userTokensResponseStore.get(userWalletId = params.userWalletId)
@@ -50,6 +51,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducer @AssistedInject constr
responseCryptoCurrenciesFactory.createCurrencies(
response = response,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
).toSet()
}
.onEmpty { emit(emptySet()) }
diff --git a/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt b/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt
index d1a0449ebc..d4e9d0325f 100644
--- a/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt
+++ b/data/account/src/main/kotlin/com/tangem/data/account/tokens/DefaultMainAccountTokensMigration.kt
@@ -61,7 +61,7 @@ internal class DefaultMainAccountTokensMigration(
val unassignedTokens = mainAccount.findUnassignedTokens(derivationIndex)
- if (unassignedTokens == null) {
+ if (unassignedTokens.isNullOrEmpty()) {
Timber.i("No unassigned tokens found for migration")
return@either
}
diff --git a/data/account/src/test/java/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducerTest.kt b/data/account/src/test/java/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducerTest.kt
index 983ba0706e..c622f073a4 100644
--- a/data/account/src/test/java/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducerTest.kt
+++ b/data/account/src/test/java/com/tangem/data/account/producer/DefaultMultiWalletCryptoCurrenciesProducerTest.kt
@@ -10,6 +10,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.configs.GenericCardConfig
+import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
@@ -72,7 +73,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
}
verify(inverse = true) {
- responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any())
+ responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any(), accountIndex = any())
}
}
@@ -115,6 +116,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
} returns cryptoCurrencies.toList()
@@ -122,6 +124,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = updatedUserTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
} returns updatedCryptoCurrencies.toList()
@@ -144,6 +147,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
@@ -162,6 +166,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = updatedUserTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
}
@@ -186,6 +191,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
} returns cryptoCurrencies.toList()
@@ -208,6 +214,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
@@ -252,6 +259,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
} returns cryptoCurrencies.toList()
@@ -283,6 +291,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
}
@@ -307,7 +316,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
}
verify(inverse = true) {
- responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any())
+ responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any(), accountIndex = any())
}
}
@@ -335,7 +344,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
verify(inverse = true) {
userTokensResponseStore.get(any())
- responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any())
+ responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any(), accountIndex = any())
}
}
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt
index 626f998bfb..2d7a8855d7 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt
@@ -144,6 +144,9 @@ internal class DefaultCardCryptoCurrencyFactory(
?: return emptyMap()
response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
+ val accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull()
+ ?: return@flatMapTo emptySet()
+
responseCryptoCurrenciesFactory.createCurrencies(
tokens = accountDTO.tokens.orEmpty().filter { token ->
networks.any {
@@ -151,7 +154,7 @@ internal class DefaultCardCryptoCurrencyFactory(
}
},
userWallet = userWallet,
- accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull(),
+ accountIndex = accountIndex,
)
}
} else {
@@ -163,6 +166,7 @@ internal class DefaultCardCryptoCurrencyFactory(
networks.any { it.backendId == token.networkId && it.derivationPath.value == token.derivationPath }
},
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
.groupBy(CryptoCurrency::network)
@@ -181,10 +185,13 @@ internal class DefaultCardCryptoCurrencyFactory(
?: return emptyMap()
response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
+ val accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull()
+ ?: return@flatMapTo emptySet()
+
responseCryptoCurrenciesFactory.createCurrencies(
tokens = accountDTO.tokens.orEmpty().filter { token -> token.networkId in networkIds },
userWallet = userWallet,
- accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull(),
+ accountIndex = accountIndex,
)
}
} else {
@@ -194,6 +201,7 @@ internal class DefaultCardCryptoCurrencyFactory(
responseCryptoCurrenciesFactory.createCurrencies(
tokens = response.tokens.filter { token -> token.networkId in networkIds },
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
.groupBy { it.network.id.rawId }
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt
index 66faea0a74..eccf6e4952 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt
@@ -22,7 +22,7 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
fun createCurrencies(
response: UserTokensResponse,
userWallet: UserWallet,
- accountIndex: DerivationIndex? = null,
+ accountIndex: DerivationIndex,
): List {
return createCurrencies(tokens = response.tokens, userWallet = userWallet, accountIndex = accountIndex)
}
@@ -30,7 +30,7 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
fun createCurrencies(
tokens: List,
userWallet: UserWallet,
- accountIndex: DerivationIndex? = null,
+ accountIndex: DerivationIndex,
): List {
return tokens
.asSequence()
@@ -42,7 +42,7 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
fun createCurrency(
responseToken: UserTokensResponse.Token,
userWallet: UserWallet,
- accountIndex: DerivationIndex? = null,
+ accountIndex: DerivationIndex,
): CryptoCurrency? {
var blockchain = Blockchain.fromNetworkId(responseToken.networkId)
if (blockchain == null || blockchain == Blockchain.Unknown) {
@@ -103,7 +103,7 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
blockchain: Blockchain,
responseToken: UserTokensResponse.Token,
network: Network,
- ): CryptoCurrency.Coin? {
+ ): CryptoCurrency.Coin {
return CryptoCurrency.Coin(
id = getCoinId(network, blockchain.toCoinId()),
network = network,
@@ -127,7 +127,7 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
}
}
- private fun createToken(blockchain: Blockchain, sdkToken: Token, network: Network): CryptoCurrency.Token? {
+ private fun createToken(blockchain: Blockchain, sdkToken: Token, network: Network): CryptoCurrency.Token {
val id = getTokenId(network, sdkToken)
return CryptoCurrency.Token(
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt
index 4ffb1c9210..265418339e 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt
@@ -74,6 +74,7 @@ class NetworkFactory @Inject constructor(
blockchain = blockchain,
excludedBlockchains = excludedBlockchains,
),
+ shouldCheckChia = false,
)
}
@@ -128,9 +129,10 @@ class NetworkFactory @Inject constructor(
derivationPath: Network.DerivationPath,
canHandleTokens: Boolean,
accountIndex: DerivationIndex? = null,
+ shouldCheckChia: Boolean = true,
): Network? {
if (!blockchain.isBlockchainSupported()) return null
- if (blockchain == Blockchain.Chia && accountIndex != DerivationIndex.Main) return null
+ if (shouldCheckChia && blockchain == Blockchain.Chia && accountIndex != DerivationIndex.Main) return null
return runCatching {
Network(
diff --git a/data/feedback/detekt-baseline-debug.xml b/data/feedback/detekt-baseline-debug.xml
index 596e50e191..d10388bff4 100644
--- a/data/feedback/detekt-baseline-debug.xml
+++ b/data/feedback/detekt-baseline-debug.xml
@@ -3,7 +3,5 @@
BooleanPropertyNaming:DefaultFeedbackRepository.kt$DefaultFeedbackRepository$private val useNewUserWalletsRepository: Boolean
- MultilineLambdaItParameter:DefaultFeedbackRepository.kt$DefaultFeedbackRepository${ it.toMutableMap().apply { put(userWallet.walletId, error) } }
- UseOrEmpty:BlockchainInfoConverter.kt$BlockchainInfoConverter$value.wallet.publicKey.derivationPath?.rawPath ?: ""
diff --git a/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt b/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt
index 10bb463509..dcadace780 100644
--- a/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt
+++ b/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt
@@ -97,9 +97,9 @@ internal class DefaultFeedbackRepository(
override fun saveBlockchainErrorInfo(error: BlockchainErrorInfo) {
val userWallet = getSelectedWalletUseCase.sync().getOrNull() ?: error("UserWallet is not selected")
- blockchainsErrors.update {
- it.toMutableMap().apply {
- put(userWallet.walletId, error)
+ blockchainsErrors.update { map ->
+ map.toMutableMap().apply {
+ this[userWallet.walletId] = error
}
}
}
diff --git a/data/feedback/src/main/java/com/tangem/data/feedback/converters/BlockchainInfoConverter.kt b/data/feedback/src/main/java/com/tangem/data/feedback/converters/BlockchainInfoConverter.kt
index 686cec71dd..8c430ced24 100644
--- a/data/feedback/src/main/java/com/tangem/data/feedback/converters/BlockchainInfoConverter.kt
+++ b/data/feedback/src/main/java/com/tangem/data/feedback/converters/BlockchainInfoConverter.kt
@@ -16,9 +16,11 @@ import com.tangem.domain.feedback.models.BlockchainInfo.Addresses as BlockchainA
internal object BlockchainInfoConverter : Converter {
override fun convert(value: WalletManager): BlockchainInfo {
+ val derivationPath = value.wallet.publicKey.derivationPath
+
return BlockchainInfo(
blockchain = value.wallet.blockchain.fullName,
- derivationPath = value.wallet.publicKey.derivationPath?.rawPath ?: "",
+ derivationPath = derivationPath?.rawPath.orEmpty(),
outputsCount = value.outputsCount?.toString(),
host = value.currentHost,
addresses = value.wallet.mapAddresses(Address::value),
diff --git a/data/manage-tokens/detekt-baseline-debug.xml b/data/manage-tokens/detekt-baseline-debug.xml
index d384bf15ce..a9b6d430bb 100644
--- a/data/manage-tokens/detekt-baseline-debug.xml
+++ b/data/manage-tokens/detekt-baseline-debug.xml
@@ -3,15 +3,7 @@
MultilineLambdaItParameter:DefaultCustomTokensRepository.kt$DefaultCustomTokensRepository${ // TODO: refactor https://tangem.atlassian.net/browse/AND-10006\ if (it.isTestnet() || it in excludedBlockchains || it in hotWalletExcludedBlockchains) { return@mapNotNull null } networkFactory.create( blockchain = it, extraDerivationPath = null, userWallet = userWallet, ) }
- MultilineLambdaItParameter:DefaultManageTokensRepository.kt$DefaultManageTokensRepository${ it.contractAddress != null && it.networkId == network.backendId && it.derivationPath == network.derivationPath.value }
MultilineLambdaItParameter:ManageTokensUpdateFetcher.kt$ManageTokensUpdateFetcher${ if (it.key == toUpdate[index].key) { Batch(it.key, updatedItems) } else { null } }
- NamedArguments:ManagedCryptoCurrencyFactory.kt$ManagedCryptoCurrencyFactory$create(coinsResponse, tokensResponse, userWallet, accountIndex)
- NamedArguments:ManagedCryptoCurrencyFactory.kt$ManagedCryptoCurrencyFactory$createToken(coin, tokensResponse, coinsResponse.imageHost, userWallet, accountIndex)
- NamedArguments:ManagedCryptoCurrencyFactory.kt$ManagedCryptoCurrencyFactory$findAddedInNetworks(coinResponse.id, tokensResponse, userWallet, accountIndex)
- NamedArguments:ManagedCryptoCurrencyFactory.kt$ManagedCryptoCurrencyFactory$findAddedInNetworks(testnetToken.id, tokensResponse, userWallet, accountIndex)
- SuspendFunSwallowedCancellation:DefaultManageTokensRepository.kt$DefaultManageTokensRepository$runCatching
UnsafeCallOnNullableType:DefaultCustomTokensRepository.kt$DefaultCustomTokensRepository$coinNetwork.decimalCount!!
- UseOrEmpty:ManagedCryptoCurrencyFactory.kt$ManagedCryptoCurrencyFactory$testnetToken.networks?.mapNotNull { network -> createSource( networkId = network.id, contractAddress = network.address, decimals = network.decimalCount, userWallet = userWallet, accountIndex = accountIndex, ) } ?: emptyList()
- UseOrEmpty:ManagedCryptoCurrencyFactory.kt$ManagedCryptoCurrencyFactory$tokensResponse ?.let { createCustomTokens(it, userWallet, accountIndex) } ?: emptyList()
diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt
index ed2887e114..84eea67a90 100644
--- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt
+++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt
@@ -41,6 +41,7 @@ import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher.Request
import com.tangem.pagination.toBatchFlow
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import com.tangem.utils.coroutines.runSuspendCatching
@Suppress("LongParameterList", "LargeClass")
internal class DefaultManageTokensRepository(
@@ -176,7 +177,7 @@ internal class DefaultManageTokensRepository(
val shouldFetch = loadUserTokensFromRemote && userWallet != null
val fetchedResponse = if (shouldFetch) {
- runCatching { walletAccountsFetcher.fetch(userWalletId = userWallet.walletId) }.getOrNull()
+ runSuspendCatching { walletAccountsFetcher.fetch(userWalletId = userWallet.walletId) }.getOrNull()
} else {
null
}
@@ -214,19 +215,22 @@ internal class DefaultManageTokensRepository(
userWallet != null &&
query == null
+ val accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull()
+ ?: return emptyList()
+
val items = if (isCreateWithCustom) {
managedCryptoCurrencyFactory.createWithCustomTokens(
coinsResponse = updatedCoinsResponse,
tokensResponse = tokensResponse,
userWallet = userWallet,
- accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull(),
+ accountIndex = accountIndex,
)
} else {
managedCryptoCurrencyFactory.create(
coinsResponse = updatedCoinsResponse,
tokensResponse = tokensResponse,
userWallet = userWallet,
- accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull(),
+ accountIndex = accountIndex,
)
}
@@ -262,14 +266,14 @@ internal class DefaultManageTokensRepository(
coinsResponse = updatedCoinsResponse,
tokensResponse = tokensResponse,
userWallet = userWallet,
- accountIndex = null,
+ accountIndex = DerivationIndex.Main,
)
} else {
managedCryptoCurrencyFactory.create(
coinsResponse = updatedCoinsResponse,
tokensResponse = tokensResponse,
userWallet = userWallet,
- accountIndex = null,
+ accountIndex = DerivationIndex.Main,
)
}
}
@@ -307,6 +311,13 @@ internal class DefaultManageTokensRepository(
)
}
+ val accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull()
+ ?: return BatchFetchResult.Success(
+ data = emptyList(),
+ empty = true,
+ last = true,
+ )
+
val items = managedCryptoCurrencyFactory.createTestnetWithCustomTokens(
testnetTokensConfig = if (!searchText.isNullOrBlank()) {
testnetTokensConfig.copy(
@@ -320,7 +331,7 @@ internal class DefaultManageTokensRepository(
},
tokensResponse = tokensResponse,
userWallet = userWallet,
- accountIndex = accountDTO?.derivationIndex?.let(DerivationIndex::invoke)?.getOrNull(),
+ accountIndex = accountIndex,
)
return BatchFetchResult.Success(
@@ -350,7 +361,7 @@ internal class DefaultManageTokensRepository(
},
tokensResponse = getSavedUserTokensResponseSync(userWallet.walletId),
userWallet = userWallet,
- accountIndex = null,
+ accountIndex = DerivationIndex.Main,
)
return BatchFetchResult.Success(
@@ -392,10 +403,10 @@ internal class DefaultManageTokensRepository(
)
val newTokensList = storedTokens.tokens + addedTokens - removedTokens.toSet()
- return newTokensList.any {
- it.contractAddress != null &&
- it.networkId == network.backendId &&
- it.derivationPath == network.derivationPath.value
+ return newTokensList.any { token ->
+ token.contractAddress != null &&
+ token.networkId == network.backendId &&
+ token.derivationPath == network.derivationPath.value
}
}
diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt
index 5ecaec838c..49ec7c78de 100644
--- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt
+++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt
@@ -35,10 +35,16 @@ internal class ManagedCryptoCurrencyFactory(
coinsResponse: CoinsResponse,
tokensResponse: UserTokensResponse?,
userWallet: UserWallet?,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): List {
return coinsResponse.coins.mapNotNull { coin ->
- createToken(coin, tokensResponse, coinsResponse.imageHost, userWallet, accountIndex)
+ createToken(
+ coinResponse = coin,
+ tokensResponse = tokensResponse,
+ imageHost = coinsResponse.imageHost,
+ userWallet = userWallet,
+ accountIndex = accountIndex,
+ )
}
}
@@ -46,10 +52,15 @@ internal class ManagedCryptoCurrencyFactory(
coinsResponse: CoinsResponse,
tokensResponse: UserTokensResponse,
userWallet: UserWallet,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): List {
val customTokens = createCustomTokens(tokensResponse, userWallet, accountIndex)
- val tokens = create(coinsResponse, tokensResponse, userWallet, accountIndex)
+ val tokens = create(
+ coinsResponse = coinsResponse,
+ tokensResponse = tokensResponse,
+ userWallet = userWallet,
+ accountIndex = accountIndex,
+ )
return customTokens + tokens
}
@@ -58,11 +69,11 @@ internal class ManagedCryptoCurrencyFactory(
testnetTokensConfig: TestnetTokensConfig,
tokensResponse: UserTokensResponse?,
userWallet: UserWallet,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): List {
val customTokens = tokensResponse
?.let { createCustomTokens(it, userWallet, accountIndex) }
- ?: emptyList()
+ .orEmpty()
val testnetTokens = testnetTokensConfig.tokens.map { testnetToken ->
ManagedCryptoCurrency.Token(
id = ManagedCryptoCurrency.ID(testnetToken.id),
@@ -77,8 +88,13 @@ internal class ManagedCryptoCurrencyFactory(
userWallet = userWallet,
accountIndex = accountIndex,
)
- } ?: emptyList(),
- addedIn = findAddedInNetworks(testnetToken.id, tokensResponse, userWallet, accountIndex),
+ }.orEmpty(),
+ addedIn = findAddedInNetworks(
+ currencyId = testnetToken.id,
+ tokensResponse = tokensResponse,
+ userWallet = userWallet,
+ accountIndex = accountIndex,
+ ),
)
}
@@ -88,7 +104,7 @@ internal class ManagedCryptoCurrencyFactory(
private fun createCustomTokens(
tokensResponse: UserTokensResponse,
userWallet: UserWallet,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): List = tokensResponse.tokens
.mapNotNull { token ->
maybeCreateCustomToken(token, userWallet, accountIndex)
@@ -97,7 +113,7 @@ internal class ManagedCryptoCurrencyFactory(
private fun maybeCreateCustomToken(
token: UserTokensResponse.Token,
userWallet: UserWallet,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): ManagedCryptoCurrency? {
val blockchain = Blockchain.fromNetworkId(token.networkId)
?.takeUnless { it in excludedBlockchains }
@@ -161,7 +177,7 @@ internal class ManagedCryptoCurrencyFactory(
tokensResponse: UserTokensResponse?,
imageHost: String?,
userWallet: UserWallet?,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): ManagedCryptoCurrency? {
if (coinResponse.networks.isEmpty() || !coinResponse.active) return null
@@ -184,7 +200,12 @@ internal class ManagedCryptoCurrencyFactory(
symbol = coinResponse.symbol,
iconUrl = getIconUrl(coinResponse.id, imageHost),
availableNetworks = availableNetworks,
- addedIn = findAddedInNetworks(coinResponse.id, tokensResponse, userWallet, accountIndex),
+ addedIn = findAddedInNetworks(
+ currencyId = coinResponse.id,
+ tokensResponse = tokensResponse,
+ userWallet = userWallet,
+ accountIndex = accountIndex,
+ ),
)
}
@@ -194,7 +215,7 @@ internal class ManagedCryptoCurrencyFactory(
decimals: Int?,
userWallet: UserWallet?,
extraDerivationPath: String? = null,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): SourceNetwork? {
val blockchain = Blockchain.fromNetworkId(networkId)
?.takeUnless { it in excludedBlockchains }
@@ -235,7 +256,7 @@ internal class ManagedCryptoCurrencyFactory(
currencyId: String,
tokensResponse: UserTokensResponse?,
userWallet: UserWallet?,
- accountIndex: DerivationIndex?,
+ accountIndex: DerivationIndex,
): Set {
if (tokensResponse == null) return emptySet()
diff --git a/data/promo/detekt-baseline-debug.xml b/data/promo/detekt-baseline-debug.xml
index 0f6f378450..7ac3d42cb4 100644
--- a/data/promo/detekt-baseline-debug.xml
+++ b/data/promo/detekt-baseline-debug.xml
@@ -2,8 +2,6 @@
- NullableBooleanCheck:DefaultPromoRepository.kt$DefaultPromoRepository$getSepaPromoBanner()?.isActive ?: false
- NullableBooleanCheck:DefaultPromoRepository.kt$DefaultPromoRepository$getVisaPromoBanner()?.isActive ?: false
SuspendFunSwallowedCancellation:DefaultPromoRepository.kt$DefaultPromoRepository$runCatching
SuspendFunWithFlowReturnType:DefaultPromoRepository.kt$DefaultPromoRepository$suspend
diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
index 86c501406d..d6041cb705 100644
--- a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
+++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
@@ -58,6 +58,11 @@ internal class DefaultPromoRepository(
PromoId.BlackFriday -> {
val isActive = getBlackFridayPromoBanner()?.isActive == true
+ isActive && shouldShow
+ }
+ PromoId.OnePlusOne -> {
+ val isActive = getOnePlusOnePromoBanner()?.isActive == true
+
isActive && shouldShow
}
}
@@ -70,6 +75,7 @@ internal class DefaultPromoRepository(
PromoId.Sepa -> flowOf(false)
PromoId.VisaPresale -> flowOf(false)
PromoId.BlackFriday -> flowOf(false)
+ PromoId.OnePlusOne -> flowOf(false)
}
}
@@ -161,10 +167,19 @@ internal class DefaultPromoRepository(
}.getOrNull()
}
+ private suspend fun getOnePlusOnePromoBanner(): PromoBanner? {
+ return runCatching(dispatchers.io) {
+ promoBannerConverter.convert(
+ tangemApi.getPromoBanner(ONE_PLUS_ONE_NAME).getOrThrow(),
+ )
+ }.getOrNull()
+ }
+
private companion object {
const val SEPA_NAME = "sepa"
const val VISA_NAME = "visa-waitlist"
const val BLACK_FRIDAY_NAME = "black-friday"
+ const val ONE_PLUS_ONE_NAME = "one-plus-one"
const val STORIES_LOAD_DELAY = 1000L
}
}
\ No newline at end of file
diff --git a/data/swap/build.gradle.kts b/data/swap/build.gradle.kts
index 78b5b851c7..a7ea5bd55c 100644
--- a/data/swap/build.gradle.kts
+++ b/data/swap/build.gradle.kts
@@ -44,6 +44,7 @@ dependencies {
/** Libs */
implementation(projects.libs.blockchainSdk)
+ implementation(projects.libs.crypto)
/** Other */
implementation(deps.androidx.datastore)
diff --git a/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionConverter.kt b/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionConverter.kt
index c07926cd50..8895c05ffd 100644
--- a/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionConverter.kt
+++ b/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionConverter.kt
@@ -1,12 +1,16 @@
package com.tangem.data.swap.converter.transaction
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.swap.models.SwapStatusDTO
import com.tangem.data.swap.models.SwapTransactionDTO
import com.tangem.data.swap.models.SwapTxTypeDTO
+import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapTransactionModel
import com.tangem.domain.swap.models.SwapTxType
+import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
import com.tangem.utils.converter.TwoWayConverter
internal class SavedSwapTransactionConverter(
@@ -48,9 +52,23 @@ internal class SavedSwapTransactionConverter(
): SwapTransactionModel {
val status = txStatuses[value.txId]
val refundCurrency = status?.refundTokensResponse?.let { id ->
+ val blockchain = Blockchain.fromNetworkId(id.networkId) ?: return@let null
+ val derivationPath = id.derivationPath ?: return@let null
+
+ val accountIndex = if (blockchain == Blockchain.Chia) {
+ DerivationIndex.Main
+ } else {
+ val recognizer = AccountNodeRecognizer(blockchain = blockchain)
+ val index = recognizer.recognize(derivationPathValue = derivationPath)?.toInt()
+ ?: return@let null
+
+ DerivationIndex(index).getOrNull() ?: return@let null
+ }
+
responseCryptoCurrenciesFactory.createCurrency(
responseToken = id,
userWallet = userWallet,
+ accountIndex = accountIndex,
)
}
val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency)
diff --git a/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionListConverter.kt b/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionListConverter.kt
index 9688b3086b..051b7d9a47 100644
--- a/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionListConverter.kt
+++ b/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapTransactionListConverter.kt
@@ -72,7 +72,11 @@ internal class SavedSwapTransactionListConverter(
return SwapTransactionListModel(
transactions = value.transactions.map { tx ->
- savedSwapTransactionConverter.convertBack(tx, userWallet, txStatuses)
+ savedSwapTransactionConverter.convertBack(
+ value = tx,
+ userWallet = userWallet,
+ txStatuses = txStatuses,
+ )
},
userWalletId = value.userWalletId,
fromCryptoCurrencyId = value.fromCryptoCurrencyId,
diff --git a/data/tokens/detekt-baseline-debug.xml b/data/tokens/detekt-baseline-debug.xml
index 8be9ea8bd3..bc443d0652 100644
--- a/data/tokens/detekt-baseline-debug.xml
+++ b/data/tokens/detekt-baseline-debug.xml
@@ -3,11 +3,8 @@
MultilineLambdaItParameter:CustomTokensMerger.kt$CustomTokensMerger${ Timber.e(it, "Unable to fetch token:\n$token") null }
- MultilineLambdaItParameter:DefaultCurrenciesRepository.kt$DefaultCurrenciesRepository${ it.networkId == blockchainNetworkId && compareIdWithMigrations(it, coinId) && it.derivationPath == derivationPath.value }
NullableToStringCall:AccountListCryptoCurrenciesFetcher.kt$AccountListCryptoCurrenciesFetcher$${this::class.simpleName}
NullableToStringCall:DefaultMultiWalletCryptoCurrenciesFetcher.kt$DefaultMultiWalletCryptoCurrenciesFetcher$${this::class.simpleName}
- SuspendFunSwallowedCancellation:DefaultCurrenciesRepository.kt$DefaultCurrenciesRepository$runCatching
- SuspendFunWithFlowReturnType:DefaultCurrenciesRepository.kt$DefaultCurrenciesRepository$suspend
UseOrEmpty:DefaultYieldSupplyWarningsViewedRepository.kt$DefaultYieldSupplyWarningsViewedRepository$appPreferencesStore.getObjectSet<String>(PreferencesKeys.YIELD_SUPPLY_WARNINGS_STATES_KEY).firstOrNull() ?: emptySet()
diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt
index e2988f92ce..ffb98afb6c 100644
--- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt
+++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt
@@ -21,6 +21,7 @@ import com.tangem.domain.core.error.DataError
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.express.ExpressServiceFetcher
import com.tangem.domain.express.models.ExpressAsset
+import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
@@ -31,6 +32,7 @@ import com.tangem.domain.tokens.model.FeePaidCurrency
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import com.tangem.utils.coroutines.runSuspendCatching
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import timber.log.Timber
@@ -301,8 +303,9 @@ internal class DefaultCurrenciesRepository(
)
responseCryptoCurrenciesFactory.createCurrencies(
- storedTokens,
+ response = storedTokens,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
@@ -357,15 +360,16 @@ internal class DefaultCurrenciesRepository(
val coinId = blockchain.toCoinId()
val storedCoin = storedTokens.tokens
- .find {
- it.networkId == blockchainNetworkId &&
- compareIdWithMigrations(it, coinId) &&
- it.derivationPath == derivationPath.value
+ .find { token ->
+ token.networkId == blockchainNetworkId &&
+ compareIdWithMigrations(token, coinId) &&
+ token.derivationPath == derivationPath.value
} ?: error("Coin in this network $networkId not found")
val coin = responseCryptoCurrenciesFactory.createCurrency(
responseToken = storedCoin,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
coin as? CryptoCurrency.Coin ?: error("Unable to create currency")
@@ -525,6 +529,7 @@ internal class DefaultCurrenciesRepository(
}
}
+ @Suppress("SuspendFunWithFlowReturnType")
private suspend fun getCurrenciesForWallet(
userWallet: UserWallet,
currencyRawId: CryptoCurrency.RawID,
@@ -539,6 +544,7 @@ internal class DefaultCurrenciesRepository(
responseCryptoCurrenciesFactory.createCurrencies(
response = storedTokens.copy(tokens = filterResponse),
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
}
@@ -570,7 +576,7 @@ internal class DefaultCurrenciesRepository(
}
override suspend fun syncTokens(userWalletId: UserWalletId) {
- runCatching {
+ runSuspendCatching {
val savedCurrencies = requireNotNull(
value = getSavedUserTokensResponseSync(key = userWalletId),
lazyMessage = { "Saved tokens empty. Can not perform add currencies action" },
@@ -591,6 +597,7 @@ internal class DefaultCurrenciesRepository(
responseCryptoCurrenciesFactory.createCurrencies(
response = storedTokens,
userWallet = userWallet,
+ accountIndex = DerivationIndex.Main,
)
}
}
diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt
index 8c77f51a78..de056fb431 100644
--- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt
+++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt
@@ -155,9 +155,10 @@ internal class DefaultCurrencyChecksRepository(
override suspend fun getRentExemptionError(
userWalletId: UserWalletId,
- currencyStatus: CryptoCurrencyStatus,
+ currencyStatus: CryptoCurrencyStatus?,
balanceAfterTransaction: BigDecimal,
): CryptoCurrencyWarning.Rent? {
+ if (currencyStatus == null) return null
val rentData = walletManagersFacade.getRentInfo(userWalletId, currencyStatus.currency.network) ?: return null
return when {
balanceAfterTransaction.isZero() -> null
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayCryptoCurrencyFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayCryptoCurrencyFactory.kt
new file mode 100644
index 0000000000..505383a099
--- /dev/null
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayCryptoCurrencyFactory.kt
@@ -0,0 +1,60 @@
+package com.tangem.data.pay
+
+import arrow.core.Either
+import arrow.core.Either.Companion.catch
+import com.tangem.blockchain.blockchains.ethereum.Chain
+import com.tangem.blockchainsdk.utils.ExcludedBlockchains
+import com.tangem.core.error.UniversalError
+import com.tangem.data.common.currency.CryptoCurrencyFactory
+import com.tangem.data.common.network.NetworkFactory
+import com.tangem.data.pay.util.TangemPayErrorConverter
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
+import timber.log.Timber
+import javax.inject.Inject
+
+private const val TAG = "TangemPay: DefaultTangemPayCryptoCurrencyFactory"
+/**
+ * Custom token parameters. Will be used only for F&F.
+ */
+private const val TOKEN_ID = "usd-coin"
+private const val TOKEN_NAME = "USDC"
+private const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
+private const val TOKEN_DECIMALS = 6
+
+internal class DefaultTangemPayCryptoCurrencyFactory @Inject constructor(
+ excludedBlockchains: ExcludedBlockchains,
+ private val errorConverter: TangemPayErrorConverter,
+) : TangemPayCryptoCurrencyFactory {
+
+ private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
+ CryptoCurrencyFactory(excludedBlockchains)
+ }
+ private val networkFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
+ NetworkFactory(excludedBlockchains)
+ }
+
+ override fun create(userWallet: UserWallet, chainId: Int): Either {
+ return catch {
+ val chain = requireNotNull(Chain.entries.find { it.id == chainId }) { "Can not find chain with $chainId" }
+ val blockchain = requireNotNull(chain.blockchain)
+ val network = networkFactory.create(
+ blockchain = blockchain,
+ extraDerivationPath = null,
+ userWallet = userWallet,
+ )
+ cryptoCurrencyFactory.createToken(
+ network = requireNotNull(network),
+ rawId = CryptoCurrency.RawID(TOKEN_ID),
+ name = TOKEN_NAME,
+ symbol = TOKEN_NAME,
+ contractAddress = TOKEN_CONTRACT_ADDRESS,
+ decimals = TOKEN_DECIMALS,
+ )
+ }.mapLeft { exception ->
+ Timber.tag(TAG).e(exception)
+ errorConverter.convert(exception)
+ }
+ }
+}
\ No newline at end of file
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPaySwapDataFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPaySwapDataFactory.kt
deleted file mode 100644
index b7f0683742..0000000000
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPaySwapDataFactory.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.tangem.data.pay
-
-import arrow.core.Either
-import arrow.core.Either.Companion.catch
-import com.squareup.moshi.Moshi
-import com.tangem.blockchain.blockchains.ethereum.Chain
-import com.tangem.blockchainsdk.utils.ExcludedBlockchains
-import com.tangem.core.error.UniversalError
-import com.tangem.data.common.currency.CryptoCurrencyFactory
-import com.tangem.data.common.network.NetworkFactory
-import com.tangem.data.pay.util.TangemPayErrorConverter
-import com.tangem.data.pay.util.TangemPayWalletsManager
-import com.tangem.datasource.di.NetworkMoshi
-import com.tangem.domain.models.ReceiveAddressModel
-import com.tangem.domain.models.ReceiveAddressModel.NameService
-import com.tangem.domain.models.currency.CryptoCurrency
-import com.tangem.domain.models.wallet.UserWallet
-import com.tangem.domain.pay.TangemPayTopUpData
-import com.tangem.domain.pay.TangemPaySwapDataFactory
-import timber.log.Timber
-import java.math.BigDecimal
-import javax.inject.Inject
-
-private const val TAG = "TangemPay: DefaultDataForTopUpFactory"
-/**
- * Custom token parameters. Will be used only for F&F.
- */
-private const val TOKEN_ID = "usd-coin"
-private const val TOKEN_NAME = "USDC"
-private const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
-private const val TOKEN_DECIMALS = 6
-
-internal class DefaultTangemPaySwapDataFactory @Inject constructor(
- @NetworkMoshi moshi: Moshi,
- private val tangemPayWalletsManager: TangemPayWalletsManager,
- excludedBlockchains: ExcludedBlockchains,
-) : TangemPaySwapDataFactory {
-
- private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
- CryptoCurrencyFactory(excludedBlockchains)
- }
- private val networkFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
- NetworkFactory(excludedBlockchains)
- }
- private val errorConverter by lazy(mode = LazyThreadSafetyMode.NONE) { TangemPayErrorConverter(moshi) }
-
- private fun getCurrency(userWallet: UserWallet, chainId: Int): CryptoCurrency {
- val chain = requireNotNull(Chain.entries.find { it.id == chainId }) { "Can not find chain with $chainId" }
- val blockchain = requireNotNull(chain.blockchain)
- val network = networkFactory.create(
- blockchain = blockchain,
- extraDerivationPath = null,
- userWallet = userWallet,
- )
- return cryptoCurrencyFactory.createToken(
- network = requireNotNull(network),
- rawId = CryptoCurrency.RawID(TOKEN_ID),
- name = TOKEN_NAME,
- symbol = TOKEN_NAME,
- contractAddress = TOKEN_CONTRACT_ADDRESS,
- decimals = TOKEN_DECIMALS,
- )
- }
-
- override fun create(
- depositAddress: String,
- chainId: Int,
- cryptoBalance: BigDecimal,
- fiatBalance: BigDecimal,
- ): Either {
- return catch {
- val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking()
- val currency = getCurrency(wallet, chainId)
- TangemPayTopUpData(
- currency = currency,
- walletId = wallet.walletId,
- cryptoBalance = cryptoBalance,
- fiatBalance = fiatBalance,
- depositAddress = depositAddress,
- receiveAddress = listOf(ReceiveAddressModel(nameService = NameService.Default, value = depositAddress)),
- )
- }.mapLeft { exception ->
- Timber.tag(TAG).e(exception)
- errorConverter.convert(exception)
- }
- }
-}
\ No newline at end of file
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt
index 49bc15d0db..bae2d97baf 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt
@@ -1,10 +1,10 @@
package com.tangem.data.pay.di
-import com.tangem.data.pay.DefaultTangemPaySwapDataFactory
+import com.tangem.data.pay.DefaultTangemPayCryptoCurrencyFactory
import com.tangem.data.pay.repository.*
import com.tangem.data.pay.usecase.DefaultGetTangemPayCurrencyStatusUseCase
import com.tangem.data.pay.usecase.DefaultTangemPayWithdrawUseCase
-import com.tangem.domain.pay.TangemPaySwapDataFactory
+import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.pay.repository.*
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
@@ -48,7 +48,9 @@ internal interface TangemPayDataModule {
@Binds
@Singleton
- fun bindTangemPaySwapDataFactory(factory: DefaultTangemPaySwapDataFactory): TangemPaySwapDataFactory
+ fun bindTangemPayCryptoCurrencyFactory(
+ factory: DefaultTangemPayCryptoCurrencyFactory,
+ ): TangemPayCryptoCurrencyFactory
@Binds
@Singleton
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt
index 21077cd428..1dafcfd572 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt
@@ -1,8 +1,12 @@
package com.tangem.data.pay.repository
import arrow.core.Either
+import arrow.core.left
+import arrow.core.raise.catch
+import arrow.core.right
import com.tangem.core.error.UniversalError
import com.tangem.data.pay.util.RainCryptoUtil
+import com.tangem.data.pay.util.TangemPayErrorConverter
import com.tangem.data.visa.config.VisaLibLoader
import com.tangem.datasource.api.common.config.ApiConfig
import com.tangem.datasource.api.common.config.ApiEnvironment
@@ -40,6 +44,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
private val rainCryptoUtil: RainCryptoUtil,
private val storage: TangemPayStorage,
private val cardFrozenStateStore: TangemPayCardFrozenStateStore,
+ private val errorConverter: TangemPayErrorConverter,
) : TangemPayCardDetailsRepository {
private val pollingScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@@ -55,6 +60,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
fiatBalance = result.fiat.availableBalance,
currencyCode = result.fiat.currency,
cryptoBalance = result.crypto.balance,
+ availableForWithdrawal = result.availableForWithdrawal.amount,
chainId = result.crypto.chainId,
depositAddress = result.crypto.depositAddress,
contractAddress = result.crypto.tokenContractAddress,
@@ -63,37 +69,39 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
}
override suspend fun revealCardDetails(userWalletId: UserWalletId): Either {
- return requestHelper.runWithErrorLogs(TAG) {
- val publicKeyBase64 = getPublicKeyBase64()
- val (secretKeyBytes, sessionId) = rainCryptoUtil.generateSecretKeyAndSessionId(publicKeyBase64)
+ return catch(
+ block = {
+ val publicKeyBase64 = getPublicKeyBase64()
+ val (secretKeyBytes, sessionId) = rainCryptoUtil.generateSecretKeyAndSessionId(publicKeyBase64)
+ val result = requestHelper.performRequest(userWalletId = userWalletId) { authHeader ->
+ tangemPayApi.revealCardDetails(
+ authHeader = authHeader,
+ body = CardDetailsRequest(sessionId = sessionId),
+ )
+ }.getOrNull()?.result ?: error("Cannot reveal card details")
- val result = requestHelper.request(userWalletId) { authHeader ->
- tangemPayApi.revealCardDetails(
- authHeader = authHeader,
- body = CardDetailsRequest(sessionId = sessionId),
+ val pan = rainCryptoUtil.decryptSecret(
+ base64Secret = result.pan.secret,
+ base64Iv = result.pan.iv,
+ secretKeyBytes = secretKeyBytes,
)
- }.result ?: error("Cannot reveal card details")
- val pan = rainCryptoUtil.decryptSecret(
- base64Secret = result.pan.secret,
- base64Iv = result.pan.iv,
- secretKeyBytes = secretKeyBytes,
- )
+ val cvv = rainCryptoUtil.decryptSecret(
+ base64Secret = result.cvv.secret,
+ base64Iv = result.cvv.iv,
+ secretKeyBytes = secretKeyBytes,
+ )
+ secretKeyBytes.fill(0)
- val cvv = rainCryptoUtil.decryptSecret(
- base64Secret = result.cvv.secret,
- base64Iv = result.cvv.iv,
- secretKeyBytes = secretKeyBytes,
- )
- secretKeyBytes.fill(0)
-
- TangemPayCardDetails(
- pan = pan,
- cvv = cvv,
- expirationYear = result.expirationYear,
- expirationMonth = result.expirationMonth,
- )
- }
+ TangemPayCardDetails(
+ pan = pan,
+ cvv = cvv,
+ expirationYear = result.expirationYear,
+ expirationMonth = result.expirationMonth,
+ ).right()
+ },
+ catch = { errorConverter.convert(it).left() },
+ )
}
override suspend fun setPin(userWalletId: UserWalletId, pin: String): Either {
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPaySwapRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPaySwapRepository.kt
index d1d72250d3..042686b6e7 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPaySwapRepository.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPaySwapRepository.kt
@@ -18,6 +18,7 @@ import com.tangem.domain.pay.repository.TangemPaySwapRepository
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.hotwallet.HotWalletFeatureToggles
+import com.tangem.utils.extensions.addHexPrefix
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.Currency
@@ -66,7 +67,7 @@ internal class DefaultTangemPaySwapRepository @Inject constructor(
recipientAddress = receiverAddress,
adminSalt = result.salt,
senderAddress = result.senderAddress,
- adminSignature = signatureResult.signature,
+ adminSignature = signatureResult.signature.addHexPrefix(),
)
tangemPayApi.withdraw(authHeader = authHeader, body = request)
}
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayTxHistoryRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayTxHistoryRepository.kt
index 243e008c99..021605d89a 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayTxHistoryRepository.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayTxHistoryRepository.kt
@@ -21,7 +21,6 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import javax.inject.Inject
private const val INITIAL_CURSOR = "initial_cursor_key"
-private const val TAG = "TangemPay: TangemPayTxHistoryRepository:"
internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
private val requestPerformer: TangemPayRequestPerformer,
@@ -106,12 +105,14 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
cursor: String?,
pageSize: Int,
) {
- requestPerformer.runWithErrorLogs(TAG) {
- val result = requestPerformer.request(userWalletId) { authHeader ->
- visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
- }.result
+ requestPerformer.performRequest(userWalletId = userWalletId) { authHeader ->
+ visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
+ }.onLeft {
+ error(it.toString())
+ }.onRight { response ->
+ val result = response.result
val items = txHistoryItemConverter.convertList(result.transactions).filterNotNull()
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
- }.onLeft { error(it.toString()) }
+ }
}
}
\ No newline at end of file
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt
index f3aacb104e..788aafe9d8 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt
@@ -5,14 +5,12 @@ import arrow.core.getOrElse
import arrow.core.left
import arrow.core.raise.catch
import arrow.core.right
-import com.squareup.moshi.Moshi
import com.squareup.wire.Instant
import com.tangem.data.pay.util.TangemPayErrorConverter
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.pay.TangemPayAuthApi
import com.tangem.datasource.api.pay.models.request.RefreshCustomerWalletAccessTokenRequest
import com.tangem.datasource.api.pay.models.response.TangemPayGetTokensResponse
-import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.models.wallet.UserWalletId
@@ -21,11 +19,11 @@ import com.tangem.domain.visa.model.TangemPayAuthTokens
import com.tangem.domain.visa.model.getAuthHeader
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import timber.log.Timber
+import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
@@ -33,16 +31,15 @@ private const val TAG = "TangemPayRequestPerformer"
@Singleton
internal class TangemPayRequestPerformer @Inject constructor(
- @NetworkMoshi moshi: Moshi,
+ private val errorConverter: TangemPayErrorConverter,
private val environmentConfigStorage: EnvironmentConfigStorage,
private val dispatchers: CoroutineDispatcherProvider,
private val tangemPayAuthApi: TangemPayAuthApi,
private val tangemPayStorage: TangemPayStorage,
) {
- private val customerWalletAddress = MutableStateFlow(null)
+ private val customerWalletAddresses = ConcurrentHashMap()
private val tokensMutex = Mutex()
- private val errorConverter = TangemPayErrorConverter(moshi)
@Deprecated("Do not use this method")
suspend fun runWithErrorLogs(tag: String, requestBlock: suspend () -> T): Either {
@@ -110,7 +107,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
}
suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String {
- val existingAddress = customerWalletAddress.value
+ val existingAddress = customerWalletAddresses[userWalletId]
if (existingAddress != null) {
return existingAddress
}
@@ -118,7 +115,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
userWalletId = userWalletId,
) ?: error("Can not find customer address")
- customerWalletAddress.value = storedAddress
+ customerWalletAddresses[userWalletId] = storedAddress
return storedAddress
}
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayErrorConverter.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayErrorConverter.kt
index 145269f0f5..8080f3b642 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayErrorConverter.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayErrorConverter.kt
@@ -3,10 +3,16 @@ package com.tangem.data.pay.util
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.pay.models.response.VisaErrorResponse
+import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.utils.converter.Converter
+import javax.inject.Inject
+import javax.inject.Singleton
-class TangemPayErrorConverter(moshi: Moshi) : Converter {
+@Singleton
+internal class TangemPayErrorConverter @Inject constructor(
+ @NetworkMoshi moshi: Moshi,
+) : Converter {
private val visaErrorAdapter by lazy { moshi.adapter(VisaErrorResponse::class.java) }
diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayWalletsManager.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayWalletsManager.kt
index b4c31a08cb..4423dddf32 100644
--- a/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayWalletsManager.kt
+++ b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayWalletsManager.kt
@@ -15,12 +15,14 @@ class TangemPayWalletsManager @Inject constructor(
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) {
+ @Deprecated("Don't use and put userWallet in features that need it")
suspend fun getDefaultWalletForTangemPay(): UserWallet.Cold {
val userWalletsFlow = if (useNewRepository()) repository.userWallets else manager.userWallets
val userWallets = userWalletsFlow.filter { !it.isNullOrEmpty() }.first()
return findColdWallet(userWallets)
}
+ @Deprecated("Don't use and put userWallet in features that need it")
fun getDefaultWalletForTangemPayBlocking(): UserWallet.Cold {
val userWallets = if (useNewRepository()) repository.userWallets.value else manager.userWalletsSync
return findColdWallet(userWallets)
@@ -29,7 +31,8 @@ class TangemPayWalletsManager @Inject constructor(
private fun useNewRepository(): Boolean = hotWalletFeatureToggles.isHotWalletEnabled
private fun findColdWallet(userWallets: List?): UserWallet.Cold {
- return userWallets?.find { it is UserWallet.Cold } as? UserWallet.Cold
- ?: error("Cannot find cold user wallet")
+ return userWallets?.find {
+ it is UserWallet.Cold && it.isMultiCurrency
+ } as? UserWallet.Cold ?: error("Cannot find cold user wallet")
}
}
\ No newline at end of file
diff --git a/data/wallets/detekt-baseline-debug.xml b/data/wallets/detekt-baseline-debug.xml
index 26f5591932..7ac40bc693 100644
--- a/data/wallets/detekt-baseline-debug.xml
+++ b/data/wallets/detekt-baseline-debug.xml
@@ -3,8 +3,6 @@
MultilineLambdaItParameter:DefaultColdMapDerivationsRepository.kt$DefaultColdMapDerivationsRepository${ networkFactory.create( blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null, extraDerivationPath = null, userWallet = userWallet, ) }
- MultilineLambdaItParameter:DefaultDerivationsRepository.kt$DefaultDerivationsRepository${ userWallet.update(it.first) it.second }
- MultilineLambdaItParameter:DefaultHotMapDerivationsRepository.kt$DefaultHotMapDerivationsRepository${ networkFactory.create( blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null, extraDerivationPath = null, userWallet = userWallet, ) }
MultilineLambdaItParameter:DefaultHotWalletAccessCodeAttemptsRepository.kt$DefaultHotWalletAccessCodeAttemptsRepository${ AttemptsPersistentData( attempts = it[PreferencesKeys.getHotWalletUnlockAttemptsKey(id.attemptIdKey())] ?: 0, bootCount = it[PreferencesKeys.getHotWalletUnlockBootKey(id.attemptIdKey())] ?: 0, deadline = it[PreferencesKeys.getHotWalletUnlockDeadlineKey(id.attemptIdKey())] ?: 0L, ) }
MultilineLambdaItParameter:DefaultHotWalletAccessCodeAttemptsRepository.kt$DefaultHotWalletAccessCodeAttemptsRepository${ it.remove(PreferencesKeys.getHotWalletUnlockAttemptsKey(authAttemptId.attemptIdKey())) it.remove(PreferencesKeys.getHotWalletUnlockAttemptsKey(noAuthAttemptId.attemptIdKey())) it.remove(PreferencesKeys.getHotWalletUnlockBootKey(authAttemptId.attemptIdKey())) it.remove(PreferencesKeys.getHotWalletUnlockBootKey(noAuthAttemptId.attemptIdKey())) it.remove(PreferencesKeys.getHotWalletUnlockDeadlineKey(authAttemptId.attemptIdKey())) it.remove(PreferencesKeys.getHotWalletUnlockDeadlineKey(noAuthAttemptId.attemptIdKey())) }
MultilineLambdaItParameter:DefaultHotWalletAccessCodeAttemptsRepository.kt$DefaultHotWalletAccessCodeAttemptsRepository${ while (true) { emit(toState(id, it.attempts, it.deadline, it.bootCount)) val remaining = remainingSeconds(it.deadline, it.bootCount) if (remaining <= 0) break delay(timeMillis = 1000) } }
@@ -16,9 +14,7 @@
SuspendFunSwallowedCancellation:DefaultHotWalletAccessor.kt$DefaultHotWalletAccessor$runCatching
SuspendFunSwallowedCancellation:TangemHotWalletSigner.kt$TangemHotWalletSigner$runCatching
UnnecessaryLet:MissedDerivationsFinder.kt$MissedDerivationsFinder$let(::findByNetworks)
- UnusedImports:DefaultDerivationsRepository.kt$import com.tangem.common.map
UseOrEmpty:DefaultColdMapDerivationsRepository.kt$DefaultColdMapDerivationsRepository$oldKeys[walletKey] ?: emptyMap()
- UseOrEmpty:DefaultHotMapDerivationsRepository.kt$DefaultHotMapDerivationsRepository$oldKeys[walletKey] ?: emptyMap()
VarCouldBeVal:DefaultHotWalletAccessor.kt$DefaultHotWalletAccessor$private var contextualUnlockHotWallet: ConcurrentHashMap<HotWalletId, UnlockHotWallet?> = ConcurrentHashMap()
diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt
index 862b90b035..3a3dcf27ac 100644
--- a/data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt
+++ b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt
@@ -2,16 +2,16 @@ package com.tangem.data.wallets.derivations
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.ByteArrayKey
-import com.tangem.common.map
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.datasource.local.userwallet.UserWalletsStore
-import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository
-import com.tangem.domain.wallets.derivations.DerivationsRepository
-import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository
+import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository
+import com.tangem.domain.wallets.derivations.DerivationsRepository
+import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository
import com.tangem.domain.wallets.usecase.BackendId
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@@ -29,11 +29,17 @@ internal class DefaultDerivationsRepository @Inject constructor(
derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network))
}
- override suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List) {
+ override suspend fun derivePublicKeysByNetworkIds(
+ userWalletId: UserWalletId,
+ networkIds: List,
+ accountIndex: DerivationIndex,
+ ) {
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
when (userWallet) {
is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeysByNetworkIds(userWallet, networkIds)
- is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeysByNetworkIds(userWallet, networkIds)
+ is UserWallet.Hot -> {
+ hotDerivationsRepository.derivePublicKeysByNetworkIds(userWallet, networkIds, accountIndex)
+ }
}.also {
userWallet.update(it)
}
@@ -57,9 +63,9 @@ internal class DefaultDerivationsRepository @Inject constructor(
return when (userWallet) {
is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeys(userWallet, derivations)
is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeys(userWallet, derivations)
- }.let {
- userWallet.update(it.first)
- it.second
+ }.let { publicKeysMapByUserWallet ->
+ userWallet.update(publicKeysMapByUserWallet.first)
+ publicKeysMapByUserWallet.second
}
}
diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt
index bdc3f7fdf2..987dd65cc5 100644
--- a/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt
+++ b/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt
@@ -7,6 +7,8 @@ import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.wallets.derivations.MissedDerivationsFinder
+import com.tangem.datasource.local.userwallet.UserWalletsStore
+import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
@@ -21,6 +23,7 @@ import timber.log.Timber
import javax.inject.Inject
internal class DefaultHotMapDerivationsRepository @Inject constructor(
+ private val userWalletsStore: UserWalletsStore,
private val networkFactory: NetworkFactory,
private val hotWalletAccessor: HotWalletAccessor,
private val dispatchers: CoroutineDispatcherProvider,
@@ -36,14 +39,16 @@ internal class DefaultHotMapDerivationsRepository @Inject constructor(
override suspend fun derivePublicKeysByNetworkIds(
userWallet: UserWallet.Hot,
networkIds: List,
+ accountIndex: DerivationIndex,
): UserWallet.Hot {
return derivePublicKeysByNetworks(
userWallet = userWallet,
- networks = networkIds.mapNotNull {
+ networks = networkIds.mapNotNull { networkRawId ->
networkFactory.create(
- blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null,
+ blockchain = Blockchain.fromNetworkId(networkRawId.value) ?: return@mapNotNull null,
extraDerivationPath = null,
userWallet = userWallet,
+ accountIndex = accountIndex,
)
},
)
@@ -82,10 +87,15 @@ internal class DefaultHotMapDerivationsRepository @Inject constructor(
hotWalletId = userWallet.hotWalletId,
request = request,
)
+
+ // Get the updated user wallet from the store to ensure we have the latest data
+ // in case it was modified during the derive operation
+ val updatedUserWallet = userWalletsStore.getSyncStrict(userWallet.walletId) as UserWallet.Hot
+
val newKeys =
result.responses.associate { ByteArrayKey(it.seedKey.publicKey) to ExtendedPublicKeysMap(it.publicKeys) }
- return userWallet.updateWithNewKeys(newKeys) to newKeys
+ return updatedUserWallet.updateWithNewKeys(newKeys) to newKeys
}
override suspend fun hasMissedDerivations(
@@ -131,7 +141,7 @@ internal class DefaultHotMapDerivationsRepository @Inject constructor(
): Map {
return (oldKeys.keys + newKeys.keys).toSet()
.associateWith { walletKey ->
- val oldDerivations = ExtendedPublicKeysMap(oldKeys[walletKey] ?: emptyMap())
+ val oldDerivations = ExtendedPublicKeysMap(oldKeys[walletKey].orEmpty())
val newDerivations = newKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap())
ExtendedPublicKeysMap(oldDerivations + newDerivations)
diff --git a/detekt_baseline_report.txt b/detekt_baseline_report.txt
index 3a9f9a9044..85ca6a00e5 100644
--- a/detekt_baseline_report.txt
+++ b/detekt_baseline_report.txt
@@ -1,7 +1,7 @@
==========================================
Detekt Baseline Updater & Issue Counter
==========================================
-Date: 2025-11-27 14:13:20
+Date: 2025-12-02 13:19:49
Step 1: Running detekt to check for new issues...
@@ -17,13 +17,13 @@ Counting issues in baseline files...
==========================================
Summary:
- Total Issues: 1593
- Modules with Issues: 68
+ Total Issues: 1435
+ Modules with Issues: 62
Average Issues per Module: 23
Progress:
- Fixed: 209 out of 1802 (11%)
- Remaining: 1593
+ Fixed: 367 out of 1802 (20%)
+ Remaining: 1435
==========================================
All Modules with Issues (sorted by count)
@@ -31,72 +31,66 @@ All Modules with Issues (sorted by count)
Module Issues
────────────────────────────────────────────────────────────────
-features/wallet/impl 169
-features/markets/impl 155
-features/onboarding-v2/impl 131
-features/send-v2/impl 80
-features/swap/impl 73
-features/hot-wallet/impl 57
-features/staking/impl 56
+features/wallet/impl 148
+features/markets/impl 148
+features/onboarding-v2/impl 130
+features/swap/impl 67
+features/send-v2/impl 57
data/wallet-connect 55
-features/swap-v2/impl 53
-features/walletconnect/impl 51
-features/tokendetails/impl 49
+features/hot-wallet/impl 53
+features/tokendetails/impl 48
+features/staking/impl 48
+features/walletconnect/impl 47
features/manage-tokens/impl 45
-domain/wallets 39
-features/nft/impl 36
+features/swap-v2/impl 40
+domain/wallets 37
+features/nft/impl 34
features/tester/impl 31
domain/tokens 28
-features/swap/domain 27
core/ui 27
-features/yield-supply/impl 26
common/ui 26
-data/visa 23
-features/tangempay/details/impl 22
+features/swap/domain 25
+data/visa 22
+features/yield-supply/impl 21
+features/tangempay/details/impl 21
data/nft 20
-data/wallets 18
features/swap/data 15
+data/wallets 14
data/swap 13
features/token-recieve/impl 11
features/qr-scanning/impl 11
domain/account/status 11
data/onramp 11
-data/manage-tokens 11
core/datasource 11
-features/referral/impl 10
-features/details/impl 10
data/markets 10
+features/details/impl 9
domain/staking 9
data/yield-supply 9
data/networks 9
-features/welcome/impl 8
-features/home/impl 8
+features/referral/impl 8
domain/transaction 8
libs/tangem-sdk-api 7
-features/onramp/impl 7
data/txhistory 7
-data/tokens 7
-data/account 7
-features/send-v2/api 6
-domain/markets 6
+features/welcome/impl 6
data/wallet-manager 6
libs/visa 5
+features/send-v2/api 5
+features/home/impl 5
+domain/markets 5
domain/legacy 5
data/transaction 5
+data/account 5
features/referral/domain 4
features/biometry/impl 4
-features/account/impl 4
-features/account/api 4
-data/promo 4
-core/config-toggles 4
-features/wallet-settings/impl 3
+data/tokens 4
features/txhistory/impl 3
features/tangempay/onboarding/impl 3
features/create-wallet-start/impl 3
domain/manage-tokens 3
-domain/demo/models 3
-domain/card 3
-data/feedback 3
+data/manage-tokens 3
common/routing 3
-features/yield-supply/api 2
+features/account/api 2
+data/promo 2
+core/config-toggles 2
+data/feedback 1
────────────────────────────────────────────────────────────────
\ No newline at end of file
diff --git a/domain/card/detekt-baseline-debug.xml b/domain/card/detekt-baseline-debug.xml
deleted file mode 100644
index 277a01c1fe..0000000000
--- a/domain/card/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
- NamedArguments:TangemCardTypesResolver.kt$TangemCardTypesResolver$Token( cardToken.name, cardToken.symbol, cardToken.contractAddress, cardToken.decimals, )
- PropertyUsedBeforeDeclaration:TapWorkarounds.kt$TapWorkarounds$excludedBatches
- PropertyUsedBeforeDeclaration:TapWorkarounds.kt$TapWorkarounds$excludedIssuers
-
-
diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt
index db4b0973cb..3e6943f724 100644
--- a/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt
+++ b/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt
@@ -36,15 +36,15 @@ internal class TangemCardTypesResolver(
override fun isTangemWallet(): Boolean {
return card.settings.isBackupAllowed && card.settings.isHDWalletAllowed &&
- card.firmwareVersion >= FirmwareVersion.Companion.MultiWalletAvailable
+ card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable
}
override fun isShibaWallet(): Boolean {
- return card.firmwareVersion.compareTo(FirmwareVersion.Companion.KeysImportAvailable) == 0
+ return card.firmwareVersion.compareTo(FirmwareVersion.KeysImportAvailable) == 0
}
override fun isWhiteWallet(): Boolean {
- return walletData == null && card.firmwareVersion <= FirmwareVersion.Companion.HDWalletAvailable
+ return walletData == null && card.firmwareVersion <= FirmwareVersion.HDWalletAvailable
}
override fun isWallet2(): Boolean = card.isWallet2
@@ -73,7 +73,7 @@ internal class TangemCardTypesResolver(
(multiWalletAvailable() || card.wallets.firstOrNull()?.curve == EllipticCurve.Secp256k1)
}
- private fun multiWalletAvailable() = card.firmwareVersion >= FirmwareVersion.Companion.MultiWalletAvailable
+ private fun multiWalletAvailable() = card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable
override fun getBlockchain(): Blockchain {
return when (productType) {
@@ -93,12 +93,15 @@ internal class TangemCardTypesResolver(
override fun getPrimaryToken(): Token? {
val cardToken = walletData?.token ?: return null
- return Token(
- cardToken.name,
- cardToken.symbol,
- cardToken.contractAddress,
- cardToken.decimals,
- )
+
+ return with(cardToken) {
+ Token(
+ name = name,
+ symbol = symbol,
+ contractAddress = contractAddress,
+ decimals = decimals,
+ )
+ }
}
override fun isReleaseFirmwareType(): Boolean = card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release
diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/common/TapWorkarounds.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/common/TapWorkarounds.kt
index 550ddd0749..f012a0bfaf 100644
--- a/domain/card/src/main/kotlin/com/tangem/domain/card/common/TapWorkarounds.kt
+++ b/domain/card/src/main/kotlin/com/tangem/domain/card/common/TapWorkarounds.kt
@@ -38,6 +38,7 @@ object TapWorkarounds {
val CardDTO.hasOldStyleDerivation: Boolean
get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95"
+ @Suppress("PropertyUsedBeforeDeclaration")
val CardDTO.isExcluded: Boolean
get() {
val isBatchExcluded = excludedBatches.contains(batchId)
diff --git a/domain/demo/models/detekt-baseline-debug.xml b/domain/demo/models/detekt-baseline-debug.xml
deleted file mode 100644
index 638c5c1870..0000000000
--- a/domain/demo/models/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
- PropertyUsedBeforeDeclaration:DemoConfig.kt$DemoConfig$debugTestDemoCardIds
- PropertyUsedBeforeDeclaration:DemoConfig.kt$DemoConfig$releaseDemoCardIds
- PropertyUsedBeforeDeclaration:DemoConfig.kt$DemoConfig$testDemoCardIds
-
-
diff --git a/domain/demo/models/src/main/kotlin/com/tangem/domain/demo/models/DemoConfig.kt b/domain/demo/models/src/main/kotlin/com/tangem/domain/demo/models/DemoConfig.kt
index 0ec194c730..5ed3b8e893 100644
--- a/domain/demo/models/src/main/kotlin/com/tangem/domain/demo/models/DemoConfig.kt
+++ b/domain/demo/models/src/main/kotlin/com/tangem/domain/demo/models/DemoConfig.kt
@@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import java.math.BigDecimal
-@Suppress("LargeClass")
+@Suppress("LargeClass", "ClassOrdering", "PropertyUsedBeforeDeclaration")
object DemoConfig {
/**
@@ -65,7 +65,6 @@ object DemoConfig {
return (releaseDemoCardIds + testDemoCardIds).distinct()
}
- @Suppress("ClassOrdering")
private val releaseDemoCardIds = mutableListOf(
// === Not from the Google Sheet table ===
"AC01000000041225",
@@ -449,7 +448,6 @@ object DemoConfig {
"AF10100000000084",
)
- @Suppress("ClassOrdering")
private val testDemoCardIds = listOf(
"FB20000000000186", // Note ETH
"FB10000000000196", // Note BTC
@@ -457,6 +455,5 @@ object DemoConfig {
"FB04000000000152", // Wallet 2
)
- @Suppress("ClassOrdering")
private val debugTestDemoCardIds = emptyList()
}
\ No newline at end of file
diff --git a/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt b/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt
index 5786ee9dac..520f49e395 100644
--- a/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt
+++ b/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt
@@ -57,7 +57,6 @@ sealed interface FeedbackEmailType {
}
sealed class Visa : FeedbackEmailType {
- data class FailedIssueCard(override val walletMetaInfo: WalletMetaInfo) : Visa()
data class DirectUserRequest(override val walletMetaInfo: WalletMetaInfo) : Visa()
@@ -68,6 +67,8 @@ sealed interface FeedbackEmailType {
override val walletMetaInfo: WalletMetaInfo,
) : Visa()
+ data class FailedIssueCard(override val walletMetaInfo: WalletMetaInfo) : Visa()
+
data class DisputeV2(
val item: TangemPayTxHistoryItem,
override val walletMetaInfo: WalletMetaInfo,
@@ -78,5 +79,7 @@ sealed interface FeedbackEmailType {
val providerName: String,
val txId: String,
) : Visa()
+
+ data class FeatureIsBeta(override val walletMetaInfo: WalletMetaInfo) : Visa()
}
}
\ No newline at end of file
diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt
index fac64d171a..00815bea99 100644
--- a/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt
+++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt
@@ -12,6 +12,7 @@ internal class FeedbackDataBuilder {
fun addTangemPayTxInfo(item: TangemPayTxHistoryItem) {
builder.append(item.jsonRepresentation)
+ builder.breakLine()
}
fun addVisaTxInfo(txDetails: VisaTxDetails) {
@@ -49,6 +50,10 @@ internal class FeedbackDataBuilder {
builder.appendKeyValue("Total saved wallets", userWalletsInfo.totalUserWallets.toString())
}
+ fun addUserWalletId(userWalletId: String) {
+ builder.appendKeyValue("User Wallet ID", userWalletId)
+ }
+
fun addUserWalletMetaInfo(walletMetaInfo: WalletMetaInfo) {
builder.appendKeyValue("Mobile Wallet is backed up", walletMetaInfo.hotWalletIsBackedUp?.toString())
builder.appendKeyValue("Card ID", walletMetaInfo.cardId)
@@ -104,6 +109,26 @@ internal class FeedbackDataBuilder {
builder.appendKeyValue("App version", phoneInfo.appVersion)
}
+ fun addTangemPayIssueType(type: FeedbackEmailType.Visa) {
+ val issueType = when (type) {
+ is FeedbackEmailType.Visa.Activation,
+ is FeedbackEmailType.Visa.DirectUserRequest,
+ is FeedbackEmailType.Visa.Dispute,
+ is FeedbackEmailType.Visa.FeatureIsBeta,
+ is FeedbackEmailType.Visa.Withdrawal,
+ -> return
+ is FeedbackEmailType.Visa.DisputeV2 -> when (type.item) {
+ is TangemPayTxHistoryItem.Collateral -> "Receive/Withdraw"
+ is TangemPayTxHistoryItem.Fee,
+ is TangemPayTxHistoryItem.Spend,
+ is TangemPayTxHistoryItem.Payment,
+ -> "Transaction"
+ }
+ is FeedbackEmailType.Visa.FailedIssueCard -> "Card issuing"
+ }
+ builder.appendKeyValue("Issue type", issueType)
+ }
+
fun addBlockchainError(info: BlockchainInfo, error: BlockchainErrorInfo) {
builder.appendKeyValue("Blockchain", info.blockchain)
builder.appendAddresses(
diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt
index 04e4d5db8f..07e0eb137e 100644
--- a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt
+++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt
@@ -6,6 +6,9 @@ import com.tangem.domain.feedback.models.FeedbackEmail
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.domain.feedback.utils.*
+import java.io.File
+
+private const val BINDER_MAX_SIZE_BYTES = 500_000 // Safe limit under Android's 1MB Binder limit
/**
* Get email with feedback for support
@@ -29,22 +32,30 @@ class SendFeedbackEmailUseCase(
address = getAddress(type),
subject = emailSubjectResolver.resolve(type),
message = createMessage(type),
- file = feedbackRepository.getZipLogFile(),
+ file = getFile(type),
)
feedbackRepository.sendEmail(email)
}
+ private suspend fun getFile(type: FeedbackEmailType): File? {
+ return if (type.isVisaEmail()) {
+ null
+ } else {
+ feedbackRepository.getZipLogFile()
+ }
+ }
+
private fun getAddress(type: FeedbackEmailType): String {
return when {
- type is FeedbackEmailType.Visa || type.walletMetaInfo?.isVisa == true -> TANGEM_VISA_SUPPORT_EMAIL
+ type.isVisaEmail() -> TANGEM_VISA_SUPPORT_EMAIL
type.walletMetaInfo?.isStart2Coin == true -> START2COIN_SUPPORT_EMAIL
else -> TANGEM_SUPPORT_EMAIL
}
}
private suspend fun createMessage(type: FeedbackEmailType): String {
- return StringBuilder().apply {
+ val fullMessage = buildString {
val title = emailMessageTitleResolver.resolve(type)
append(title)
@@ -54,7 +65,25 @@ class SendFeedbackEmailUseCase(
val body = emailMessageBodyResolver.resolve(type)
append(body)
- }.toString()
+ }
+
+ return truncateMessageIfNeeded(fullMessage)
+ }
+
+ private fun truncateMessageIfNeeded(message: String): String {
+ val messageBytes = message.toByteArray(Charsets.UTF_8)
+
+ return if (messageBytes.size > BINDER_MAX_SIZE_BYTES) {
+ // Find a safe truncation point (avoid cutting in middle of UTF-8 chars)
+ val truncatedBytes = messageBytes.sliceArray(0 until BINDER_MAX_SIZE_BYTES)
+ String(truncatedBytes, Charsets.UTF_8)
+ } else {
+ message
+ }
+ }
+
+ private fun FeedbackEmailType.isVisaEmail(): Boolean {
+ return this is FeedbackEmailType.Visa || this.walletMetaInfo?.isVisa == true
}
private fun StringBuilder.appendDisclaimerIfNeeded(type: FeedbackEmailType): StringBuilder {
@@ -65,6 +94,7 @@ class SendFeedbackEmailUseCase(
is FeedbackEmailType.CardAttestationFailed,
is FeedbackEmailType.Visa.Dispute,
is FeedbackEmailType.Visa.DisputeV2,
+ is FeedbackEmailType.Visa.FeatureIsBeta,
-> this
is FeedbackEmailType.DirectUserRequest,
is FeedbackEmailType.RateCanBeBetter,
diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt
index 12bd80d66a..1e4ca44bd2 100644
--- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt
+++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt
@@ -4,7 +4,6 @@ import com.tangem.domain.feedback.FeedbackDataBuilder
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.feedback.repository.FeedbackRepository
-import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.domain.visa.model.VisaTxDetails
/**
@@ -34,22 +33,40 @@ internal class EmailMessageBodyResolver(
-> addPhoneInfoBody()
is FeedbackEmailType.Visa.Activation -> addUserRequestBody(type.walletMetaInfo)
is FeedbackEmailType.Visa.DirectUserRequest -> addUserRequestBody(type.walletMetaInfo)
- is FeedbackEmailType.Visa.FailedIssueCard -> addUserRequestBody(type.walletMetaInfo)
is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(type.walletMetaInfo, type.visaTxDetails)
- is FeedbackEmailType.Visa.DisputeV2 -> addTangemPayRequestBody(type.walletMetaInfo, type.item)
+ is FeedbackEmailType.Visa.FailedIssueCard -> addTangemPayFailedIssuingCardBody(type)
+ is FeedbackEmailType.Visa.DisputeV2 -> addTangemPayDisputeRequestBody(type)
is FeedbackEmailType.Visa.Withdrawal -> addTangemPayWithdrawalRequestBody(type)
+ is FeedbackEmailType.Visa.FeatureIsBeta -> addTangemPayBetaRequestBody(type)
}
return build()
}
- private suspend fun FeedbackDataBuilder.addTangemPayRequestBody(
- walletMetaInfo: WalletMetaInfo,
- item: TangemPayTxHistoryItem,
- ) {
- addUserRequestBody(walletMetaInfo)
+ private fun FeedbackDataBuilder.addTangemPayFailedIssuingCardBody(type: FeedbackEmailType.Visa.FailedIssueCard) {
+ addTangemPayPhoneInfoBody(type)
addDelimiter()
- addTangemPayTxInfo(item)
+ type.walletMetaInfo.userWalletId?.let { userWalletId ->
+ addUserWalletId(userWalletId = userWalletId.stringValue)
+ }
+ }
+
+ private fun FeedbackDataBuilder.addTangemPayDisputeRequestBody(type: FeedbackEmailType.Visa.DisputeV2) {
+ addTangemPayPhoneInfoBody(type = type)
+ addDelimiter()
+ addTangemPayTxInfo(type.item)
+ addDelimiter()
+ type.walletMetaInfo.userWalletId?.let { userWalletId ->
+ addUserWalletId(userWalletId = userWalletId.stringValue)
+ }
+ }
+
+ private fun FeedbackDataBuilder.addTangemPayBetaRequestBody(type: FeedbackEmailType.Visa) {
+ addTangemPayPhoneInfoBody(type)
+ addDelimiter()
+ type.walletMetaInfo?.userWalletId?.let { userWalletId ->
+ addUserWalletId(userWalletId = userWalletId.stringValue)
+ }
}
private suspend fun FeedbackDataBuilder.addTangemPayWithdrawalRequestBody(
@@ -112,6 +129,11 @@ internal class EmailMessageBodyResolver(
addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
}
+ private fun FeedbackDataBuilder.addTangemPayPhoneInfoBody(type: FeedbackEmailType.Visa) {
+ addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo())
+ addTangemPayIssueType(type)
+ }
+
private suspend fun FeedbackDataBuilder.addTransactionSendingProblemBody(walletMetaInfo: WalletMetaInfo) {
addUserWalletMetaInfo(walletMetaInfo)
addDelimiter()
diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt
index 7d42928a99..36940c948e 100644
--- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt
+++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt
@@ -26,6 +26,8 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
is FeedbackEmailType.Visa.DisputeV2,
is FeedbackEmailType.Visa.FailedIssueCard,
is FeedbackEmailType.Visa.Withdrawal,
+ is FeedbackEmailType.Visa.FeatureIsBeta,
+ is FeedbackEmailType.PreActivatedWallet,
-> R.string.feedback_preface_support
is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative
is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed
@@ -33,7 +35,6 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
is FeedbackEmailType.StakingProblem,
is FeedbackEmailType.SwapProblem,
-> R.string.feedback_preface_tx_failed
- is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_preface_support
}
return resources.getStringSafe(resId)
diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt
index 6e01c9989f..4b1e4f9799 100644
--- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt
+++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt
@@ -15,6 +15,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
internal class EmailSubjectResolver(private val resources: Resources) {
/** Resolve email message body by [type] */
+ @Suppress("CyclomaticComplexMethod")
fun resolve(type: FeedbackEmailType): String {
return when (type) {
is FeedbackEmailType.DirectUserRequest -> {
@@ -43,8 +44,8 @@ internal class EmailSubjectResolver(private val resources: Resources) {
is FeedbackEmailType.Visa.Dispute,
is FeedbackEmailType.Visa.DisputeV2,
-> "[Visa] [DISPUTE] {auto-filled subject}"
- is FeedbackEmailType.Visa.Withdrawal,
- -> "[Visa] [WITHDRAWAL] {auto-filled subject}"
+ is FeedbackEmailType.Visa.Withdrawal -> "[Visa] [WITHDRAWAL] {auto-filled subject}"
+ is FeedbackEmailType.Visa.FeatureIsBeta -> "[VISA] [FEEDBACK]"
}
}
}
\ No newline at end of file
diff --git a/domain/markets/detekt-baseline-debug.xml b/domain/markets/detekt-baseline-debug.xml
index ef5fed0f16..64d62630c1 100644
--- a/domain/markets/detekt-baseline-debug.xml
+++ b/domain/markets/detekt-baseline-debug.xml
@@ -5,7 +5,6 @@
BooleanPropertyNaming:FilterAvailableNetworksForWalletUseCase.kt$FilterAvailableNetworksForWalletUseCase$private val useNewRepository: Boolean
BooleanPropertyNaming:GetStakingNotificationMaxApyUseCase.kt$GetStakingNotificationMaxApyUseCase$val showStakingNotification = if (!hideClicked && walletFirstUsageDate != 0L) { currentDate - walletFirstUsageDate > TWO_WEEKS_IN_MILLIS } else { false }
MultilineLambdaItParameter:FilterAvailableNetworksForWalletUseCase.kt$FilterAvailableNetworksForWalletUseCase${ val blockchain = Blockchain.fromNetworkId(it.networkId) supportedBlockchains.contains(blockchain) }
- MultilineLambdaItParameter:SaveMarketTokensUseCase.kt$SaveMarketTokensUseCase${ marketsTokenRepository.createCryptoCurrency( userWalletId = userWalletId, token = tokenMarketParams, network = it, ) }
SuspendFunWithFlowReturnType:GetStakingNotificationMaxApyUseCase.kt$GetStakingNotificationMaxApyUseCase$suspend
SuspendFunWithFlowReturnType:MarketsTokenRepository.kt$MarketsTokenRepository$suspend
diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt
index 77e9666e1e..c1d1b50552 100644
--- a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt
+++ b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt
@@ -2,6 +2,7 @@ package com.tangem.domain.markets
import arrow.core.Either
import com.tangem.domain.markets.repositories.MarketsTokenRepository
+import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
@@ -45,11 +46,11 @@ class SaveMarketTokensUseCase(
removedNetworks: Set,
): Either = Either.catch {
if (removedNetworks.isNotEmpty()) {
- val removedCurrencies = removedNetworks.mapNotNull {
+ val removedCurrencies = removedNetworks.mapNotNull { network ->
marketsTokenRepository.createCryptoCurrency(
userWalletId = userWalletId,
token = tokenMarketParams,
- network = it,
+ network = network,
)
}
@@ -60,13 +61,15 @@ class SaveMarketTokensUseCase(
derivationsRepository.derivePublicKeysByNetworkIds(
userWalletId = userWalletId,
networkIds = addedNetworks.map { Network.RawID(it.networkId) },
+ accountIndex = DerivationIndex.Main,
)
- val addedCurrencies = addedNetworks.mapNotNull {
+ val addedCurrencies = addedNetworks.mapNotNull { network ->
marketsTokenRepository.createCryptoCurrency(
userWalletId = userWalletId,
token = tokenMarketParams,
- network = it,
+ network = network,
+ accountIndex = DerivationIndex.Main,
)
}
diff --git a/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt b/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
index 764871ed95..9f8055dc60 100644
--- a/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
+++ b/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
@@ -30,4 +30,5 @@ enum class PromoId {
Sepa,
VisaPresale,
BlackFriday,
+ OnePlusOne,
}
\ No newline at end of file
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
index d5b48a464e..5cab5b1cde 100644
--- a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
+++ b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
@@ -32,6 +32,7 @@ class ShouldShowPromoWalletUseCase(
PromoId.Referral,
PromoId.VisaPresale,
PromoId.BlackFriday,
+ PromoId.OnePlusOne,
-> true
PromoId.Sepa -> {
val walletFirstUsageDate = settingsRepository.getWalletFirstUsageDate()
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt
index e0c863bf61..8eb3724038 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt
@@ -59,5 +59,6 @@ sealed class PromoAnalyticsEvent(
Empty("Empty"),
Sepa("Sepa"),
BlackFriday("Black Friday"),
+ OnePlusOne("One-Plus-One"),
}
}
\ No newline at end of file
diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt
index 43c65d397a..e1073f1bd0 100644
--- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt
+++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt
@@ -14,9 +14,11 @@ class GetCurrencyCheckUseCase(
private val dispatchers: CoroutineDispatcherProvider,
) {
+ @Suppress("LongParameterList")
suspend operator fun invoke(
userWalletId: UserWalletId,
currencyStatus: CryptoCurrencyStatus,
+ feeCurrencyStatus: CryptoCurrencyStatus?,
amount: BigDecimal?,
fee: BigDecimal?,
feeCurrencyBalanceAfterTransaction: BigDecimal?,
@@ -31,7 +33,7 @@ class GetCurrencyCheckUseCase(
val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network)
val rentWarning = currencyChecksRepository.getRentExemptionError(
userWalletId = userWalletId,
- currencyStatus = currencyStatus,
+ currencyStatus = feeCurrencyStatus,
balanceAfterTransaction = feeCurrencyBalanceAfterTransaction ?: BigDecimal.ZERO,
)
val isAccountFunded = recipientAddress?.let {
diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt
index 8351958b10..41266e0b39 100644
--- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt
+++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt
@@ -58,7 +58,7 @@ interface CurrencyChecksRepository {
*/
suspend fun getRentExemptionError(
userWalletId: UserWalletId,
- currencyStatus: CryptoCurrencyStatus,
+ currencyStatus: CryptoCurrencyStatus?,
balanceAfterTransaction: BigDecimal,
): CryptoCurrencyWarning.Rent?
}
\ No newline at end of file
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayCryptoCurrencyFactory.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayCryptoCurrencyFactory.kt
new file mode 100644
index 0000000000..5f3223e514
--- /dev/null
+++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayCryptoCurrencyFactory.kt
@@ -0,0 +1,11 @@
+package com.tangem.domain.pay
+
+import arrow.core.Either
+import com.tangem.core.error.UniversalError
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWallet
+
+interface TangemPayCryptoCurrencyFactory {
+
+ fun create(userWallet: UserWallet, chainId: Int): Either
+}
\ No newline at end of file
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt
index 7f1092646f..ad9583243f 100644
--- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt
+++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt
@@ -3,6 +3,11 @@ package com.tangem.domain.pay.model
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import java.math.BigDecimal
+sealed class MainCustomerInfoContentState {
+ object Loading : MainCustomerInfoContentState()
+ data class Content(val info: MainScreenCustomerInfo) : MainCustomerInfoContentState()
+}
+
data class MainScreenCustomerInfo(
val info: CustomerInfo,
val orderStatus: OrderStatus,
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCardBalance.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCardBalance.kt
index 90d3375e9c..911aead866 100644
--- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCardBalance.kt
+++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayCardBalance.kt
@@ -6,6 +6,7 @@ data class TangemPayCardBalance(
val fiatBalance: BigDecimal,
val currencyCode: String,
val cryptoBalance: BigDecimal,
+ val availableForWithdrawal: BigDecimal,
val chainId: Int,
val depositAddress: String?,
val contractAddress: String,
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPaySwapDataFactory.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayTopUpData.kt
similarity index 57%
rename from domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPaySwapDataFactory.kt
rename to domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayTopUpData.kt
index fe6cb7af82..3d40efda8e 100644
--- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPaySwapDataFactory.kt
+++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/TangemPayTopUpData.kt
@@ -1,22 +1,10 @@
-package com.tangem.domain.pay
+package com.tangem.domain.pay.model
-import arrow.core.Either
-import com.tangem.core.error.UniversalError
import com.tangem.domain.models.ReceiveAddressModel
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import java.math.BigDecimal
-interface TangemPaySwapDataFactory {
-
- fun create(
- depositAddress: String,
- chainId: Int,
- cryptoBalance: BigDecimal,
- fiatBalance: BigDecimal,
- ): Either
-}
-
data class TangemPayTopUpData(
val walletId: UserWalletId,
val currency: CryptoCurrency,
diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt
index 313aa94bb3..07950a5cec 100644
--- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt
+++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/usecase/TangemPayMainScreenCustomerInfoUseCase.kt
@@ -2,16 +2,13 @@ package com.tangem.domain.pay.usecase
import arrow.core.Either
import arrow.core.left
-import arrow.core.raise.catch
import arrow.core.right
import com.tangem.domain.models.wallet.UserWalletId
-import com.tangem.domain.pay.model.CustomerInfo
-import com.tangem.domain.pay.model.MainScreenCustomerInfo
-import com.tangem.domain.pay.model.OrderStatus
-import com.tangem.domain.pay.model.TangemPayCustomerInfoError
+import com.tangem.domain.pay.model.*
import com.tangem.domain.pay.repository.CustomerOrderRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.visa.error.VisaApiError
+import kotlinx.coroutines.flow.*
import timber.log.Timber
private const val TAG = "TangemPayMainScreenCustomerInfoUseCase"
@@ -26,32 +23,50 @@ class TangemPayMainScreenCustomerInfoUseCase(
private val tangemPayOnboardingRepository: OnboardingRepository,
) {
- suspend operator fun invoke(
- userWalletId: UserWalletId,
- ): Either = catch(
- block = {
- Timber.tag(TAG).d("checkCustomerWallet")
- repository.checkCustomerWallet(userWalletId)
- .fold(
- ifLeft = { error ->
- Timber.tag(TAG).e("Failed to check customer wallet ${error.javaClass.simpleName}")
- TangemPayCustomerInfoError.UnknownError.left()
- },
- ifRight = { hasTangemPay ->
- Timber.tag(TAG).i("checkCustomerWallet $hasTangemPay")
- if (hasTangemPay) {
- proceedWithPaeraCustomerResult(userWalletId)
- } else {
- TangemPayCustomerInfoError.UnknownError.left() // ignore if there's no TangemPay
+ val state: StateFlow