diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 9d6af9296f..0ef437226d 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -22,7 +22,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.core.net.toUri import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen -import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import arrow.core.getOrElse @@ -44,11 +43,8 @@ import com.tangem.domain.settings.SetGoogleServicesAvailabilityUseCase import com.tangem.domain.settings.ShouldInitiallyAskPermissionUseCase import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.staking.SendUnsubmittedHashesUseCase -import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase -import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.usecase.ClearAllHotWalletContextualUnlockUseCase -import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.features.hotwallet.HotWalletFeatureToggles import com.tangem.features.tangempay.TangemPayFeatureToggles import com.tangem.features.tester.api.TesterMenuLauncher @@ -115,12 +111,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { @Inject lateinit var sendUnsubmittedHashesUseCase: SendUnsubmittedHashesUseCase - @Inject - lateinit var getPolkadotCheckHasResetUseCase: GetPolkadotCheckHasResetUseCase - - @Inject - lateinit var getPolkadotCheckHasImmortalUseCase: GetPolkadotCheckHasImmortalUseCase - @Inject lateinit var analyticsEventsHandler: AnalyticsEventHandler @@ -228,7 +218,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { initContent() - observePolkadotAccountHealthCheck() sendStakingUnsubmittedHashes() checkGoogleServicesAvailability() @@ -436,27 +425,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { } } - private fun observePolkadotAccountHealthCheck() { - lifecycleScope.launch { - getPolkadotCheckHasResetUseCase() - .flowWithLifecycle(lifecycle, minActiveState = Lifecycle.State.CREATED) - .distinctUntilChanged() - .collect { - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.Token.PolkadotAccountReset(it.second)) - } - } - lifecycleScope.launch { - getPolkadotCheckHasImmortalUseCase() - .flowWithLifecycle(lifecycle, minActiveState = Lifecycle.State.CREATED) - .distinctUntilChanged() - .collect { (_, hasImmortalTransaction) -> - analyticsEventsHandler.send( - WalletScreenAnalyticsEvent.Token.PolkadotImmortalTransactions(hasImmortalTransaction), - ) - } - } - } - private fun sendStakingUnsubmittedHashes() { lifecycleScope.launch { sendUnsubmittedHashesUseCase.invoke() diff --git a/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt b/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt index d113493313..784db9dbc2 100644 --- a/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt +++ b/app/src/main/java/com/tangem/tap/data/DefaultCardSdkProvider.kt @@ -15,6 +15,7 @@ import com.tangem.core.analytics.models.ExceptionAnalyticsEvent import com.tangem.crypto.bip39.Wordlist import com.tangem.data.card.sdk.CardSdkOwner import com.tangem.data.card.sdk.CardSdkProvider +import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.common.config.ApiConfig import com.tangem.datasource.api.common.config.ApiEnvironmentConfig import com.tangem.datasource.api.common.config.managers.ApiConfigsManager @@ -28,6 +29,7 @@ import com.tangem.sdk.nfc.AndroidNfcAvailabilityProvider import com.tangem.sdk.nfc.NfcManager import com.tangem.sdk.storage.create import com.tangem.tap.foregroundActivityObserver +import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.info.AppInfoProvider import com.tangem.utils.version.AppVersionProvider @@ -48,6 +50,7 @@ internal class DefaultCardSdkProvider @Inject constructor( private val apiConfigsManager: ApiConfigsManager, appVersionProvider: AppVersionProvider, appInfoProvider: AppInfoProvider, + authProvider: AuthProvider, ) : CardSdkProvider, CardSdkOwner { private val observer = Observer() @@ -68,13 +71,16 @@ internal class DefaultCardSdkProvider @Inject constructor( }, ) + val apiEnvironment = Provider { + apiConfigsManager.getEnvironmentConfig(ApiConfig.ID.TangemTech).environment + } + val platformHeaders = RequestHeader.AppVersionPlatformHeaders( + appVersionProvider = appVersionProvider, + appInfoProvider = appInfoProvider, + ) + val apiKeyHeader = RequestHeader.TangemApiKeyHeader(authProvider, apiEnvironment) TangemApiServiceSettings.addInterceptors( - AddHeadersInterceptor( - requestHeader = RequestHeader.AppVersionPlatformHeaders( - appVersionProvider = appVersionProvider, - appInfoProvider = appInfoProvider, - ), - ), + AddHeadersInterceptor(platformHeaders.values + apiKeyHeader.values), ) } diff --git a/app/src/main/java/com/tangem/tap/di/ActivityModule.kt b/app/src/main/java/com/tangem/tap/di/ActivityModule.kt index 987366bd71..1f40496096 100644 --- a/app/src/main/java/com/tangem/tap/di/ActivityModule.kt +++ b/app/src/main/java/com/tangem/tap/di/ActivityModule.kt @@ -6,10 +6,7 @@ import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.express.ExpressServiceFetcher -import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase -import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.sdk.api.TangemSdkManager import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository @@ -83,20 +80,4 @@ internal object ActivityModule { userWalletProvider = { getSelectedWalletUseCase.sync().getOrNull() }, ) } - - @Provides - @Singleton - fun provideGetPolkadotCheckHasResetUseCase( - polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository, - ): GetPolkadotCheckHasResetUseCase { - return GetPolkadotCheckHasResetUseCase(polkadotAccountHealthCheckRepository) - } - - @Provides - @Singleton - fun provideGetPolkadotCheckHasImmortalUseCase( - polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository, - ): GetPolkadotCheckHasImmortalUseCase { - return GetPolkadotCheckHasImmortalUseCase(polkadotAccountHealthCheckRepository) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt index 89bd3b4ca9..c4e44fe950 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt @@ -1,6 +1,5 @@ package com.tangem.tap.di.domain -import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.domain.managetokens.* import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.ManageTokensRepository @@ -10,11 +9,14 @@ import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob import javax.inject.Singleton @Module @@ -74,6 +76,7 @@ internal object ManageTokensDomainModule { multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, stakingIdFactory: StakingIdFactory, + dispatchers: CoroutineDispatcherProvider, ): SaveManagedTokensUseCase { return SaveManagedTokensUseCase( customTokensRepository = customTokensRepository, @@ -84,6 +87,7 @@ internal object ManageTokensDomainModule { multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, stakingIdFactory = stakingIdFactory, + parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default), ) } diff --git a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt index 60c8926df6..9b33e83a66 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt @@ -15,10 +15,13 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.features.hotwallet.HotWalletFeatureToggles +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob import javax.inject.Singleton @Module @@ -67,6 +70,7 @@ object MarketsDomainModule { multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, stakingIdFactory: StakingIdFactory, + dispatchers: CoroutineDispatcherProvider, ): SaveMarketTokensUseCase { return SaveMarketTokensUseCase( derivationsRepository = derivationsRepository, @@ -76,6 +80,7 @@ object MarketsDomainModule { multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, stakingIdFactory = stakingIdFactory, + parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default), ) } diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index f38de6e571..c9c0d08868 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -294,14 +294,6 @@ internal object TokensDomainModule { return IsAmountSubtractAvailableUseCase(currenciesRepository, dispatchers) } - @Provides - @Singleton - fun provideRunPolkadotAccountHealthCheckUseCase( - repository: PolkadotAccountHealthCheckRepository, - ): RunPolkadotAccountHealthCheckUseCase { - return RunPolkadotAccountHealthCheckUseCase(repository) - } - @Provides @Singleton fun provideGetNetworkStatusesUseCase(networksRepository: NetworksRepository): GetNetworkAddressesUseCase { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index 45653eeeea..93a1c1f2be 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -19,7 +19,7 @@ import com.tangem.domain.card.common.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins import com.tangem.domain.card.common.TapWorkarounds.isVisa -import com.tangem.domain.common.TwinsHelper +import com.tangem.domain.card.common.TwinsHelper import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.domain.card.common.visa.VisaUtilities import com.tangem.domain.card.configs.CardConfig diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt index b7b896682c..659921880b 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt @@ -5,7 +5,7 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.CompletionCallback -import com.tangem.domain.common.TwinsHelper +import com.tangem.domain.card.common.TwinsHelper import com.tangem.operations.attestation.AttestationTask import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.operations.wallet.CreateWalletTask diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index b466017130..000cee1a09 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -8,7 +8,7 @@ import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.CompletionCallback import com.tangem.common.extensions.hexToBytes -import com.tangem.domain.common.TwinsHelper +import com.tangem.domain.card.common.TwinsHelper import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.operations.wallet.CreateWalletTask import com.tangem.operations.wallet.PurgeWalletCommand diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt deleted file mode 100644 index b503705f7e..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.tangem.tap.domain.twins - -import com.tangem.Message -import com.tangem.common.CompletionResult -import com.tangem.common.KeyPair -import com.tangem.common.extensions.hexToBytes -import com.tangem.common.extensions.toHexString -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.operations.wallet.CreateWalletResponse -import com.tangem.tap.common.extensions.inject -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager - -class TwinCardsManager(card: CardDTO) { - - private val issuersConfigStorage by lazy(mode = LazyThreadSafetyMode.NONE) { - store.inject(DaggerGraphState::issuersConfigStorage) - } - - private val firstCardId: String = card.cardId - private val publicKey: String = card.issuer.publicKey.toHexString() - - private var currentCardPublicKey: String? = null - private var secondCardPublicKey: String? = null - - suspend fun createFirstWallet(message: Message): CompletionResult { - val response = tangemSdkManager.createFirstTwinWallet(cardId = firstCardId, initialMessage = message) - - if (response is CompletionResult.Success) { - currentCardPublicKey = response.data.wallet.publicKey.toHexString() - } - - return response - } - - suspend fun createSecondWallet( - initialMessage: Message, - preparingMessage: Message, - creatingWalletMessage: Message, - ): CompletionResult { - val response = tangemSdkManager.createSecondTwinWallet( - firstPublicKey = requireNotNull(currentCardPublicKey), - firstCardId = firstCardId, - issuerKeys = getIssuerKeys(), - preparingMessage = preparingMessage, - creatingWalletMessage = creatingWalletMessage, - initialMessage = initialMessage, - ) - - if (response is CompletionResult.Success) { - secondCardPublicKey = response.data.wallet.publicKey.toHexString() - } - - return response - } - - suspend fun complete(message: Message): CompletionResult { - val response = tangemSdkManager.finalizeTwin( - secondCardPublicKey = requireNotNull(secondCardPublicKey).hexToBytes(), - issuerKeyPair = getIssuerKeys(), - cardId = firstCardId, - initialMessage = message, - ) - - return response - } - - private suspend fun getIssuerKeys(): KeyPair { - val issuer = issuersConfigStorage.getConfig().first { it.publicKey == publicKey } - - return KeyPair( - publicKey = issuer.publicKey.hexToBytes(), - privateKey = issuer.privateKey.hexToBytes(), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt deleted file mode 100644 index ca1f95a650..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt +++ /dev/null @@ -1,174 +0,0 @@ -package com.tangem.tap.domain.twins - -import android.animation.AnimatorSet -import android.animation.ObjectAnimator -import android.animation.PropertyValuesHolder -import android.view.View -import androidx.core.animation.doOnEnd -import com.tangem.common.extensions.VoidCallback -import com.tangem.domain.common.TwinCardNumber -import com.tangem.sdk.ui.widget.leapfrogWidget.LeapView -import com.tangem.sdk.ui.widget.leapfrogWidget.LeapViewState -import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget - -/** -[REDACTED_AUTHOR] - */ -class TwinsCardWidget( - val leapfrogWidget: LeapfrogWidget, - private val deviceScaleFactor: Float = 1f, - val getTopOfAnchorViewForActivateState: () -> Float, -) { - - var state: TwinCardsWidgetState? = null - private set - - private val animationDuration = 300L - - init { - if (leapfrogWidget.getViewsCount() != 2) throw UnsupportedOperationException() - } - - fun toWelcome(animate: Boolean = true, onEnd: VoidCallback = {}) { - if (state == TwinCardsWidgetState.Welcome) return - - state = TwinCardsWidgetState.Welcome - val animator = createAnimator(animate) - animator.playTogether( - createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)), - createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second)), - ) - animator.doOnEnd { onEnd() } - leapfrogWidget.fold(animate) { animator.start() } - } - - fun toLeapfrog(animate: Boolean = true, onEnd: VoidCallback = {}) { - if (state == TwinCardsWidgetState.Leapfrog) return - - state = TwinCardsWidgetState.Leapfrog - val animator = createAnimator(animate) - animator.playTogether( - createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)), - createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second)), - ) - animator.doOnEnd { - leapfrogWidget.initViews() - leapfrogWidget.unfold(animate, onEnd) - } - animator.start() - } - - fun toActivate(animate: Boolean = true, onEnd: VoidCallback = {}) { - if (state == TwinCardsWidgetState.Activate) return - - state = TwinCardsWidgetState.Activate - val animator = createAnimator(animate) - animator.playTogether( - createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)), - createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second)), - ) - animator.doOnEnd { onEnd() } - animator.start() - } - - private fun createAnimator(animate: Boolean): AnimatorSet { - return AnimatorSet().apply { - duration = if (animate) animationDuration else 0 - } - } - - private fun createAnimator(cardNumber: TwinCardNumber, properties: TwinsCardProperties): ObjectAnimator { - val view = getLeapViewByCardNumber(cardNumber).view - val animator = ObjectAnimator.ofPropertyValuesHolder(view, *properties.createValuesHolders().toTypedArray()) - view.elevation = properties.elevation - return animator - } - - @Suppress("MagicNumber") - private fun createWelcomeProperties(cardNumber: TwinCardNumber): TwinsCardProperties { - return when (cardNumber) { - TwinCardNumber.First -> { - TwinsCardProperties( - xTranslation = -120f * deviceScaleFactor, - yTranslation = -220f * deviceScaleFactor, - rotation = -3f, - elevation = 1f, - scale = deviceScaleFactor, - ) - } - TwinCardNumber.Second -> { - TwinsCardProperties( - xTranslation = 170f * deviceScaleFactor, - yTranslation = 170f * deviceScaleFactor, - rotation = -3f, - elevation = 0f, - scale = deviceScaleFactor, - ) - } - } - } - - private fun createLeapfrogProperties(cardNumber: TwinCardNumber): TwinsCardProperties { - val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state) - return twinProperties.copy( - xTranslation = 0f, - yTranslation = 0f, - rotation = 0f, - scale = 1f, - ) - } - - @Suppress("MagicNumber") - private fun createActivateProperties(cardNumber: TwinCardNumber): TwinsCardProperties { - val topOfAnchorView = getTopOfAnchorViewForActivateState() - val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state) - return twinProperties.copy( - xTranslation = twinProperties.xTranslation, - yTranslation = twinProperties.yTranslation - topOfAnchorView, - rotation = 0f, - scale = (twinProperties.scale - 0.5f) * deviceScaleFactor, - ) - } - - private fun getLeapViewByCardNumber(cardNumber: TwinCardNumber): LeapView { - return when (cardNumber) { - TwinCardNumber.First -> leapfrogWidget.getViewByPosition(0) - TwinCardNumber.Second -> leapfrogWidget.getViewByPosition(1) - } - } -} - -enum class TwinCardsWidgetState { - Welcome, Leapfrog, Activate -} - -private data class TwinsCardProperties( - val xTranslation: Float = 0f, - val yTranslation: Float = 0f, - val rotation: Float = 0f, - val elevation: Float = 0f, - val scale: Float = 1.1f, -) { - - fun createValuesHolders(): List { - return listOf( - PropertyValuesHolder.ofFloat(View.TRANSLATION_X, xTranslation), - PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, yTranslation), - PropertyValuesHolder.ofFloat(View.ROTATION, rotation), - PropertyValuesHolder.ofFloat(View.SCALE_X, scale), - PropertyValuesHolder.ofFloat(View.SCALE_Y, scale), - ) - } - - companion object { - fun from(leapViewState: LeapViewState): TwinsCardProperties { - val leapViewProperties = leapViewState.properties - - return TwinsCardProperties( - yTranslation = leapViewProperties.yTranslation, - elevation = leapViewProperties.elevationEnd, - scale = leapViewProperties.scale, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WrongTwinCard.kt b/app/src/main/java/com/tangem/tap/domain/twins/WrongTwinCard.kt index d05d28217e..6752cb5cca 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/WrongTwinCard.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/WrongTwinCard.kt @@ -1,7 +1,7 @@ package com.tangem.tap.domain.twins import com.tangem.common.core.TangemError -import com.tangem.domain.common.TwinCardNumber +import com.tangem.domain.card.common.TwinCardNumber import com.tangem.tap.tangemSdkManager import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt b/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt index 8982e6031a..f873c4683f 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/DefaultAuthProvider.kt @@ -2,14 +2,19 @@ package com.tangem.tap.network.auth import com.tangem.common.extensions.toHexString import com.tangem.datasource.api.common.AuthProvider +import com.tangem.datasource.api.common.config.ApiEnvironment +import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.utils.Provider +import com.tangem.utils.ProviderSuspend internal class DefaultAuthProvider( private val userWalletsListManager: UserWalletsListManager, private val userWalletsListRepository: UserWalletsListRepository, private val shouldUseNewListRepository: Boolean = false, + private val environmentConfigStorage: EnvironmentConfigStorage, ) : AuthProvider { override suspend fun getCardPublicKey(): String { @@ -38,6 +43,20 @@ internal class DefaultAuthProvider( } } + override fun getApiKey(apiEnvironment: Provider): ProviderSuspend { + return ProviderSuspend { + when (apiEnvironment.invoke()) { + ApiEnvironment.MOCK, + ApiEnvironment.DEV, + ApiEnvironment.DEV_2, + ApiEnvironment.DEV_3, + -> environmentConfigStorage.getConfigSync().tangemApiKeyDev + ApiEnvironment.STAGE -> environmentConfigStorage.getConfigSync().tangemApiKeyStage + ApiEnvironment.PROD -> environmentConfigStorage.getConfigSync().tangemApiKey + } ?: error("No tangem tech api config provided") + } + } + private suspend fun getWallets(): List { return if (shouldUseNewListRepository) { userWalletsListRepository.userWalletsSync() diff --git a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt index f1aa594af8..f1ba2de154 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt @@ -28,11 +28,13 @@ internal class AuthModule { userWalletsListManager: UserWalletsListManager, userWalletsListRepository: UserWalletsListRepository, hotWalletFeatureToggles: HotWalletFeatureToggles, + environmentConfigStorage: EnvironmentConfigStorage, ): AuthProvider { return DefaultAuthProvider( userWalletsListManager = userWalletsListManager, userWalletsListRepository = userWalletsListRepository, shouldUseNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled, + environmentConfigStorage = environmentConfigStorage, ) } 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 273839cc0e..9ba606d735 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 @@ -1,5 +1,6 @@ package com.tangem.common.ui.tokens +import com.tangem.blockchain.common.Blockchain import com.tangem.common.ui.R import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter @@ -21,6 +22,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.yieldSupplyKey import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.staking.model.isStakingSupported import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.lib.crypto.BlockchainUtils @@ -211,15 +213,22 @@ class TokenItemStateConverter( } if (stakingApyMap.isNotEmpty()) { - val (stakingRate, hasStaked) = findStakingRate( + val stakingInfo = findStakingRate( currencyStatus = cryptoCurrencyStatus, stakingApyMap = stakingApyMap, ) - if (stakingRate != null) { + val rewardTypeRes = when (stakingInfo.rewardType) { + Yield.RewardType.APR -> R.string.staking_apr_earn_badge + Yield.RewardType.UNKNOWN, + Yield.RewardType.APY, + null, + -> R.string.yield_module_earn_badge + } + if (stakingInfo.rate != null) { return resourceReference( - R.string.yield_module_earn_badge, - wrappedList(stakingRate.format { percent(withPercentSign = false) }), - ) to hasStaked + rewardTypeRes, + wrappedList(stakingInfo.rate.format { percent(withPercentSign = false) }), + ) to stakingInfo.isActive } } @@ -229,21 +238,46 @@ class TokenItemStateConverter( private fun findStakingRate( currencyStatus: CryptoCurrencyStatus, stakingApyMap: Map>, - ): Pair { + ): StakingLocalInfo { val stakingKey = currencyStatus.currency.stakingKey() - val validators = stakingApyMap[stakingKey] ?: return null to false + val validators = stakingApyMap[stakingKey] + ?: return StakingLocalInfo(rate = null, isActive = false, rewardType = null) + val yieldBalance = currencyStatus.value.yieldBalance val hasStakedBalance = yieldBalance is YieldBalance.Data - val rate = if (hasStakedBalance) { + + val rateInfo: Pair? = if (hasStakedBalance) { val validatorsByAddress = validators.associateBy { it.address } yieldBalance.balance.items .mapNotNull { it.validatorAddress } - .firstNotNullOfOrNull { address -> validatorsByAddress[address]?.rewardInfo?.rate } - ?: validators.mapNotNull { it.rewardInfo?.rate }.maxOrNull() + .firstNotNullOfOrNull { address -> + val validator = validatorsByAddress[address] + validator?.rewardInfo?.rate?.let { rate -> + rate to validator.rewardInfo?.type + } + } + ?: validators + .filter { it.preferred } + .mapNotNull { validator -> + validator.rewardInfo?.rate?.let { rate -> rate to validator.rewardInfo?.type } + } + .maxByOrNull { it.first } } else { - validators.mapNotNull { it.rewardInfo?.rate }.maxOrNull() + validators + .filter { it.preferred } + .mapNotNull { validator -> + validator.rewardInfo?.rate?.let { rate -> + rate to validator.rewardInfo?.type + } + } + .maxByOrNull { it.first } } - return rate to hasStakedBalance + + return StakingLocalInfo( + rate = rateInfo?.first, + isActive = hasStakedBalance, + rewardType = rateInfo?.second, + ) } private fun createSubtitleState( @@ -353,7 +387,21 @@ class TokenItemStateConverter( fun CryptoCurrencyStatus.Value.isFlickering(): Boolean = sources.total == StatusSource.CACHE private fun CryptoCurrency.stakingKey(): String { - return "${network.backendId}_$symbol" + if (this is CryptoCurrency.Coin && !network.isStakingSupported) return "" + + if (network.isStakingSupported && this !is CryptoCurrency.Coin) { + val isPolygonTokenOnEthereum = this is CryptoCurrency.Token && + this.network.id.rawId.value == Blockchain.Ethereum.id && + this.symbol == Blockchain.Polygon.currency + if (!isPolygonTokenOnEthereum) return "" + } + return "${id.rawCurrencyId}_$symbol" } } + + private data class StakingLocalInfo( + val rate: BigDecimal?, + val isActive: Boolean, + val rewardType: Yield.RewardType?, + ) } \ No newline at end of file diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt index e784eb24fe..9fa02703d3 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt @@ -132,6 +132,12 @@ sealed class AnalyticsParam { override val token: String, override val feeType: FeeType, ) : TxSentFrom("Send&Swap"), TxData + + data class Earning( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + ) : TxSentFrom("Earning"), TxData } sealed interface TxData { diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt index da1b1aca5f..002f8656ed 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/AuthProvider.kt @@ -1,5 +1,9 @@ package com.tangem.datasource.api.common +import com.tangem.datasource.api.common.config.ApiEnvironment +import com.tangem.utils.Provider +import com.tangem.utils.ProviderSuspend + /** * Provides auth for tangemTech API */ @@ -12,6 +16,8 @@ interface AuthProvider { suspend fun getCardId(): String + fun getApiKey(apiEnvironment: Provider): ProviderSuspend + /** * Returns map where keys(cardId) associated with cardPublicKey */ diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt index 8d44291374..96ef551d23 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/TangemTech.kt @@ -2,15 +2,13 @@ package com.tangem.datasource.api.common.config import com.tangem.datasource.BuildConfig import com.tangem.datasource.api.common.AuthProvider -import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.datasource.utils.RequestHeader -import com.tangem.utils.ProviderSuspend +import com.tangem.utils.Provider import com.tangem.utils.info.AppInfoProvider import com.tangem.utils.version.AppVersionProvider /** TangemTech [ApiConfig] */ internal class TangemTech( - private val environmentConfigStorage: EnvironmentConfigStorage, private val appVersionProvider: AppVersionProvider, private val authProvider: AuthProvider, private val appInfoProvider: AppInfoProvider, @@ -63,20 +61,8 @@ internal class TangemTech( ) private fun createHeaders(apiEnvironment: ApiEnvironment) = buildMap { - put(key = "api-key", value = ProviderSuspend { getApiKey(apiEnvironment) }) + putAll(from = RequestHeader.TangemApiKeyHeader(authProvider, Provider { apiEnvironment }).values) putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values) putAll(from = RequestHeader.AuthenticationHeader(authProvider).values) } - - private fun getApiKey(apiEnvironment: ApiEnvironment): String { - return when (apiEnvironment) { - ApiEnvironment.MOCK, - ApiEnvironment.DEV, - ApiEnvironment.DEV_2, - ApiEnvironment.DEV_3, - -> environmentConfigStorage.getConfigSync().tangemApiKeyDev - ApiEnvironment.STAGE -> environmentConfigStorage.getConfigSync().tangemApiKeyStage - ApiEnvironment.PROD -> environmentConfigStorage.getConfigSync().tangemApiKey - } ?: error("No tangem tech api config provided") - } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/YieldSupplyMarketTokenDto.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/YieldSupplyMarketTokenDto.kt index a2d32540f9..d9d6264389 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/YieldSupplyMarketTokenDto.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/YieldSupplyMarketTokenDto.kt @@ -10,8 +10,9 @@ data class YieldSupplyMarketTokenDto( @Json(name = "tokenSymbol") val tokenSymbol: String? = null, @Json(name = "tokenName") val tokenName: String? = null, @Json(name = "apy") val apy: BigDecimal? = null, + @Json(name = "decimals") val decimals: Int? = null, @Json(name = "isActive") val isActive: Boolean? = null, @Json(name = "chainId") val chainId: Int? = null, - @Json(name = "maxFeeNative") val maxFeeNative: String? = null, - @Json(name = "maxFeeUSD") val maxFeeUSD: String? = null, + @Json(name = "maxFeeNative") val maxFeeNative: BigDecimal? = null, + @Json(name = "maxFeeUSD") val maxFeeUSD: BigDecimal? = null, ) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt index b0c6d9e1d6..d126accfa7 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/ApiConfigsModule.kt @@ -42,12 +42,10 @@ internal object ApiConfigsModule { @Provides @IntoSet fun provideTangemTechConfig( - environmentConfigStorage: EnvironmentConfigStorage, appVersionProvider: AppVersionProvider, authProvider: AuthProvider, appInfoProvider: AppInfoProvider, ): ApiConfig = TangemTech( - environmentConfigStorage = environmentConfigStorage, appVersionProvider = appVersionProvider, authProvider = authProvider, appInfoProvider = appInfoProvider, 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 f7da1d83cf..6e163fbb4c 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 @@ -62,16 +62,6 @@ object PreferencesKeys { val APP_LOGS_KEY by lazy { stringPreferencesKey(name = "app_logs") } - val POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY by lazy { - stringPreferencesKey(name = "POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX") - } - val POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY by lazy { - stringPreferencesKey(name = "POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS") - } - val POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY by lazy { - stringPreferencesKey(name = "POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS") - } - val SEND_TAP_HELP_PREVIEW_KEY by lazy { booleanPreferencesKey(name = "sendTapHelpPreview") } val WAS_APPLICATION_STOPPED_KEY by lazy { booleanPreferencesKey(name = "applicationStopped") } diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt index 5a896ff3e1..3f05524682 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/AddHeadersInterceptor.kt @@ -1,5 +1,6 @@ package com.tangem.datasource.utils +import com.tangem.utils.ProviderSuspend import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Response @@ -12,21 +13,17 @@ import okhttp3.Response [REDACTED_AUTHOR] */ class AddHeadersInterceptor( - private val requestHeaders: Set, + private val requestHeaders: Map>, ) : Interceptor { - constructor(requestHeader: RequestHeader) : this(requestHeaders = setOf(requestHeader)) - override fun intercept(chain: Interceptor.Chain): Response { val requestBuilder = chain.request().newBuilder() - requestHeaders - .flatMap { it.values.toList() } - .forEach { (name, valueProvider) -> - val value = runBlocking { valueProvider() } + requestHeaders.forEach { (name, valueProvider) -> + val value = runBlocking { valueProvider() } - requestBuilder.addHeader(name = name, value = value) - } + requestBuilder.addHeader(name = name, value = value) + } val request = requestBuilder.build() diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt index eb7727aea3..dcc9855b55 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/HttpClientExt.kt @@ -5,13 +5,6 @@ import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.OkHttpClient -/** Extension for adding headers [requestHeaders] to every [OkHttpClient] request */ -internal fun OkHttpClient.Builder.addHeaders(vararg requestHeaders: RequestHeader): OkHttpClient.Builder { - return addInterceptor( - interceptor = AddHeadersInterceptor(requestHeaders = requestHeaders.toSet()), - ) -} - /** Extension for adding headers [requestHeaders] to every [OkHttpClient] request */ internal fun OkHttpClient.Builder.addHeaders( requestHeaders: Map>, diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt index 9e6b000399..ef2b83b6df 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt @@ -2,7 +2,9 @@ package com.tangem.datasource.utils import android.os.Build import com.tangem.datasource.api.common.AuthProvider +import com.tangem.datasource.api.common.config.ApiEnvironment import com.tangem.datasource.utils.RequestHeader.CacheControlHeader.checkHeaderValueOrEmpty +import com.tangem.utils.Provider import com.tangem.utils.ProviderSuspend import com.tangem.utils.info.AppInfoProvider import com.tangem.utils.version.AppVersionProvider @@ -40,6 +42,13 @@ sealed class RequestHeader(vararg pairs: Pair>) "device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() }, ) + /** + * Use ONLY for tangemApi (not express or yields) + */ + class TangemApiKeyHeader(authProvider: AuthProvider, apiEnvironment: Provider) : RequestHeader( + "api-key" to authProvider.getApiKey(apiEnvironment), + ) + /** * Use it to avoid crash in okhttp headers */ diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt index 96b464a53b..ef71a781c8 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/ApiConfigTest.kt @@ -1,7 +1,12 @@ package com.tangem.datasource.api.common.config import com.google.common.truth.Truth +import com.tangem.datasource.api.common.AuthProvider +import com.tangem.utils.ProviderSuspend +import io.mockk.clearMocks +import io.mockk.every import io.mockk.mockk +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import timber.log.Timber @@ -12,6 +17,17 @@ import timber.log.Timber @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ApiConfigTest { + private val appAuthProvider = mockk() + private val apiKeyProvider = mockk>() + + @BeforeEach + fun setup() { + clearMocks( + appAuthProvider, + ) + every { appAuthProvider.getApiKey(any()) } returns apiKeyProvider + } + @Test fun `all baseUrls ends with slash`() { // Arrange @@ -41,15 +57,14 @@ class ApiConfigTest { YieldSupply( environmentConfigStorage = mockk(), appVersionProvider = mockk(), - authProvider = mockk(), + authProvider = appAuthProvider, appInfoProvider = mockk(), ) } ApiConfig.ID.TangemTech -> { TangemTech( - environmentConfigStorage = mockk(), appVersionProvider = mockk(), - authProvider = mockk(), + authProvider = appAuthProvider, appInfoProvider = mockk(), ) } diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt index 276627fc34..08e894665d 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt @@ -12,6 +12,7 @@ import com.tangem.datasource.api.common.config.ApiConfig.Companion.INTERNAL_BUIL import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_TYPE import com.tangem.datasource.api.common.config.ApiConfig.Companion.RELEASE_BUILD_TYPE import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.BLOCK_AID_API_KEY +import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.TANGEM_API_KEY import com.tangem.lib.auth.ExpressAuthProvider import com.tangem.lib.auth.StakeKitAuthProvider import com.tangem.utils.ProviderSuspend @@ -40,8 +41,9 @@ internal class ProdApiConfigsManagerTest { private val stakeKitAuthProvider = mockk() private val appAuthProvider = mockk() private val appInfoProvider = mockk() + private val tangemApiKeyProvider = mockk>() - private val manager = ProdApiConfigsManager(apiConfigs = createApiConfigs()) + private lateinit var manager: ProdApiConfigsManager @BeforeEach fun setup() { @@ -56,9 +58,14 @@ internal class ProdApiConfigsManagerTest { every { appVersionProvider.versionName } returns VERSION_NAME every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY + every { appAuthProvider.getApiKey(any()) } returns tangemApiKeyProvider + coEvery { tangemApiKeyProvider.invoke() } returns TANGEM_API_KEY coEvery { appAuthProvider.getCardId() } returns APP_CARD_ID coEvery { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY + every { appInfoProvider.osVersion } returns "Android 16" + + manager = ProdApiConfigsManager(apiConfigs = createApiConfigs()) } @ParameterizedTest @@ -94,7 +101,6 @@ internal class ProdApiConfigsManagerTest { } ApiConfig.ID.TangemTech -> { TangemTech( - environmentConfigStorage = environmentConfigStorage, appVersionProvider = appVersionProvider, authProvider = appAuthProvider, appInfoProvider = appInfoProvider, @@ -177,7 +183,7 @@ internal class ProdApiConfigsManagerTest { environment = ApiEnvironment.PROD, baseUrl = "https://api.tangem.org/", headers = mapOf( - "api-key" to ProviderSuspend { MockEnvironmentConfigStorage.TANGEM_API_KEY }, + "api-key" to ProviderSuspend { TANGEM_API_KEY }, "card_id" to ProviderSuspend { APP_CARD_ID }, "card_public_key" to ProviderSuspend { APP_CARD_PUBLIC_KEY }, "version" to ProviderSuspend { VERSION_NAME }, diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 656c9a0baa..13d5401bc0 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -7,9 +7,9 @@ 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. - Bestätige Deinen zuvor eingegebenen Code, um fortzufahren + Bestätige Deinen Zugangscode, um fortzufahren. Zugangscode erneut eingeben - Lege einen %s -stelligen Zugangscode fest, um Deine Wallet zu entsperren. + Erstelle einen %s-stelliger Zugangscode, um Deine Wallet zu entsperren. Zugangscode erstellen Zugangscode Du kannst nicht mehr erstellen als %1$s Konto/Konten. Archiviere eines, um es neu hinzuzufügen. @@ -20,13 +20,20 @@ Du bist dabei, dich zu erholen \"%1$s”. Konto wiederherstellen Du hast das Limit von 20 aktiven Konten bereits überschritten. Archiviere eines, um es wiederherzustellen. + Konto kann nicht wiederhergestellt werden Archiviert + Das Konto konnte nicht archiviert werden. Bitte versuche es später erneut. + Dieses Konto nimmt am Empfehlungsprogramm teil. + Dieses Konto kann nicht archiviert werden. + Wir konnten kein Konto erstellen. Bitte versuche es später erneut. Konto erstellt Archivkonto Archiv Du archivierst dieses Konto, kannst es aber jederzeit entarchivieren. + Archivierung... Konto Konto gespeichert + Prämien berücksichtigen Kontonummer %s – wird zur Adressableitung verwendet. Konto hinzufügen Speichern @@ -37,6 +44,9 @@ Konto hinzufügen Konto bearbeiten %1$s in %2$s + Hauptkonto + Du hast das Limit von %1$s aktiven Konten bereits überschritten. Archiviere eines zur Wiederherstellung + Konto kann nicht wiederhergestellt werden Konto wiederhergestellt Tippe lange auf ein Konto, um die Konten neu anzuordnen Weiter bearbeiten @@ -78,7 +88,7 @@ Tokens im %1$s -Netzwerk werden von dieser Karte oder Ring aufgrund einer Firmware-Einschränkung nicht unterstützt. Hast du Probleme beim Scannen deiner Karte oder Ring? Diese Karte oder Ring ist für die Zusammenarbeit mit Tangem nicht geeignet - Verwenden %1$s, um Deine Wallet schnell und sicher zu entsperren und alle sensiblen Aktionen zu autorisieren, wie z. B. das Unterschreiben von Transaktionen. Für Hardware-Wallets benötigst Du weiterhin eine Karte zum signieren. + Verwende die %1$s um Deine Wallet zu entsperren und sensible Aktionen wie das Signieren von Transaktionen zu bestätigen. Bei Hardware-Wallets ist zum Signieren weiterhin eine Karte oder ein Ring erforderlich. Standardgebühr Aktiviere die Option Standardgebühr, um die Transaktionsgebühren automatisch festzulegen und die Gebührenseite beim Senden von Geldern zu überspringen. Du kannst bei Bedarf jederzeit zu dieser Seite zurückkehren. Gehe zu den Einstellungen, um die biometrische Authentifizierung in der Tangem-App zu aktivieren @@ -89,7 +99,7 @@ Durch das Entfernen der gespeicherten Geräte werden alle gespeicherten Wallets und deren Zugangscodes aus der App gelöscht. Dadurch werden alle gespeicherten Wallet-Zugangscodes gelöscht. Für jede weitere Interaktion mit dem Wallet ist die Eingabe des Zugangscodes erforderlich. Zugangscode erforderlich - Mit dieser Option wird die biometrische Authentifizierung für vertrauliche Aktionen deaktiviert. Du musst Deinen Zugangscode jedes Mal eingeben, z. B. wenn Du eine Transaktion unterzeichnest. + Diese Option deaktiviert die biometrische Authentifizierung für sensible Vorgänge. Du musst Deinen Zugangscode jedes Mal eingeben, wenn Du eine Transaktion signierst. Zugangscode speichern Bei Interaktionen mit deiner Karte oder Ring wird anstelle des Zugangscodes eine biometrische Authentifizierung abgefragt. Behalte die Wallet in der App @@ -110,15 +120,15 @@ Du hast Deine Wallet erfolgreich gesichert. Diese Wörter können bei Verlust nicht wiederhergestellt werden. Bewahre diese an einem sicheren Ort auf. Sicherung abgeschlossen - Deine geheime Wiederherstellungsphrase ist ein fester Satz von %s Zufällige Wörter, die für den Zugriff auf und die Wiederherstellung Deiner Wallet verwendet werden. - Diese Wörter können bei Verlust nicht wiederhergestellt werden. Bewahre diese an einem sicheren Ort auf. + Deine geheime Wiederherstellungsphrase ist eine feste Folge von %s zufälligen Wörtern, mit denen Du auf Deiner Wallet zugreifefst und sie wiederherstellen kannst. + Diese Worte sind unwiederbringlich verloren. Bewahre diese gut auf. Sicher aufbewahren - Bewahre diese %s Wörter an einem sicheren Ort auf , z. B. in einem Passwort-Manager, und teile diese niemals mit Dritten. + Speicher diese %s Wörter an einem sicheren Ort und gebe diese niemals an andere weiter. Keine Wiederherstellung möglich Wiederherstellungsphrase Gebe diese Wörter niemals weiter. Jeder, der sie erfährt, kann Deine gesamten Kryptowährungen stehlen. Tangem wird Dich niemals danach fragen. Die folgenden %s Wörter sind die Wiederherstellungsphrase Deiner Wallets. Mit dieser Phrase kannst Du Deine Wallet wiederherstellen, falls Du Dein Gerät verlierst. - Schreibe diese %s Wörter der Reihe nach auf und bewahre sie sicher und vertraulich auf - Die volle Verantwortung für die Sicherheit und Sicherung der Wallet und der Wiederherstellungsphrase liegt beim Benutzer, nicht bei Tangem. + Schreibe diese Wörter %s in numerischer Reihenfolge auf und bewahren diese sicher und vertraulich auf. + Du bist vollumfänglich für die Sicherung und Datensicherung Deiner Wallet und Deine Wiederherstellungsphrase verantwortlich. Wiederherstellungsphrase Um deine Kontostände ein- oder auszublenden, flippe einfach das Display deines Geräts nach unten, oder schalte es in den Einstellungen aus. Nicht mehr anzeigen @@ -132,7 +142,7 @@ 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. Bei der Bearbeitung Deines Aktionscodes ist ein Fehler aufgetreten. Bitte versuche es später noch einmal. Fehler bei der Aktivierung - Ihr Aktionscode wurde erfolgreich aktiviert. Eine Belohnung wird innerhalb von 14 Tagen auf Ihr Konto gutgeschrieben. + Dein Gutscheincode wurde erfolgreich aktiviert. Die Prämie wird deinem Bitcoin-Konto innerhalb von 14 Tagen gutgeschrieben. Promo-Code aktiviert Dieser Aktionscode wurde bereits verwendet und kann nicht erneut aktiviert werden. Code nicht verfügbar @@ -150,6 +160,13 @@ Token Tokens + Bitte setze das nächste Gerät zurück, um fortzufahren. + Wallet zurückgesetzt + Alle Tangem-Geräte wurden zurückgesetzt. Du kannst nun mit dem Upgrade Deiner Wallet fortfahren. + Erneut aktualisieren + Zurücksetzen abgeschlossen + Du hast nicht all Deine Tangem-Geräte zurückgesetzt. + Du hast nicht alle Deine Tangem-Geräte zurückgesetzt. Deaktiviere diese Option, wenn du nicht möchtest, dass diese Karte oder Ring zum Zurücksetzen von Zugangscodes auf anderen Karten oder Ringe in dieser Wallets verwendet wird. Bitte beachte, dass du dann auch den Zugangscode auf dieser Karte oder Ring nicht zurücksetzen kannst. Ermöglicht die Verwendung dieser Karte oder Ring zum Zurücksetzen des Zugangscodes auf anderen Karten oder Ringe in dieser Brieftasche Zugangspin resetten @@ -175,11 +192,13 @@ Nicht genug ADA Akzeptieren Zugang verweigert + Konto Konten Aktivieren Hinzufügen Zum Portfolio hinzufügen Token hinzufügen + Hinzugefügt Vertragsadresse Alle Erlauben @@ -239,6 +258,7 @@ Aktivieren Aktiviert Fehler + Netzwerkdienstgebühr Umtausch Erkunden Transaktionsverlauf einsehen @@ -250,10 +270,12 @@ Langsam Gebühren Beenden + Entfernen Kostenlos Vom Adressen synchronisieren Erste Schritte + Token erhalten Zum Anbieter gehen Zum Token Verstanden @@ -266,6 +288,7 @@ %1$s übrig Legacy Bitcoin Gesperrt + Gesperrte Wallet Hauptnetz monat Netzgebühr @@ -293,10 +316,12 @@ %1$s — %2$s Weiterlesen Empfangen + Empfohlen Ablehnen Neu laden Umbenennen Erforderlich + Zurücksetzen Speichern Änderungen speichern Suchen @@ -316,6 +341,7 @@ Signieren Signieren und senden Überspringen + Etwas ist schiefgelaufen. Staken Staking Start @@ -350,6 +376,8 @@ Ja Vertragsadresse kopiert! Verfügbare Netzwerke + Die Ableitung Deines Tokens entspricht der Ableitung von %1$s. Dein Token wird diesem Konto gutgeschrieben. + Die Herleitung stammt aus einem anderen Bericht. Vertragsadresse Vertragsadresse ist ungültig Bitte wähle das Netzwerk @@ -412,6 +440,7 @@ WalletConnect-Fehler Du hast eine Karte oder Ring aus einer anderen Wallet verwendet. Tippe auf die Karte oder Ring, die dieser Wallet zugeordnet ist. Nicht genug Geld für die Transaktion. Bitte lade dein Konto auf. + Tausche einen beliebigen Vermögenswert in deinem Portfolio gegen diesen Token. Meine Token Du hast noch keine Token hinzugefügt. Füge Token über den Markt zum Tausch hinzu Kann nicht gegen %s ausgetauscht werden @@ -467,6 +496,7 @@ Anbieter Bester Preis Warnliste der FCA + Beste Wahl Anbieter in FCA-Warnliste Verfügbar bis zu %s Erhältlich bei %s @@ -502,13 +532,20 @@ Es ist ein Fehler aufgetreten. Code: %s. Memo erforderlich Nur diese - Gib das Genehmigungslimit für das ausgewählte Token an + Mit Deiner Zustimmung erlaubst Du dem Smart Contract, Deine Token in zukünftigen Transaktionen zu verwenden. Betrag %s Die Genehmigungsfunktion ist erforderlich, um einer anderen Adresse die Berechtigung zur Verwendung einer bestimmten Menge Ihrer Token zu erteilen. Standardmäßig können Smart Contracts nicht auf deine Token zugreifen, es sei denn, du stimmen zu. Indem du deine Token \"freischaltest\", autorisierst du den StakeKit Smart Contract, sie zu verwenden. Die Miner des Netzwerks erhalten eine Gasgebühr (von dir bezahlt), um diese Aktion in der Blockchain aufzuzeichnen. Du kannst deine Token einsetzen, nachdem du die Genehmigung erteilt hast. Um fortzufahren, musst du Polygon Smart Contract erlauben, deine %s zu verwenden Um fortzufahren, erteile %1s Smart Contracts die Berechtigung, dein zu %2s verwenden. Erlaubnis erteilen Unbegrenzt + Dein Backup wird mit 2 oder 3 Tangem-Karten erstellt. Bewahre diese an separaten & sicheren Orten auf, um sie vor Verlust oder Beschädigung zu schützen – es ist keine Seed-Phrase erforderlich. + Backup mit mehreren Karten + Tangem Wallet hinzufügen + Dein privater Schlüssel wird direkt in der Tangem-Karte generiert und verlässt diese nie. + Schlüsselgenerierung + Alle kryptografischen Vorgänge finden innerhalb des sicheren Chips statt, der gegen Klonen und physische Manipulation zertifiziert ist. + Sicherheit auf Hardwareebene Vorhandene Wallet hinzufügen Neues Wallet erstellen Karte oder Ring bestellen @@ -518,42 +555,67 @@ Im %s Netzwerk Willst Du den Vorgang zur Erstellung des Zugangscodes wirklich beenden? Jetzt sichern - Um die Einrichtung abzuschließen, sicher Deine Wallet und sicher den App-Zugriff mit einem Access Code. + Um die Einrichtung abzuschließen, sicher Deine Wallet und schütze die App mit einem Zugangscode. Jetzt beenden - Wallet-Aktivierung abschließen - Um das Setup abzuschließen, sicher den App-Zugriff mit Access Code. + Wallet-Einrichtung abschließen + Schließe die Einrichtung ab, indem Du die App mit einem Zugangscode sicherst. Wenn dies der Fall ist, musst Du von vorne beginnen. - Bist Du sicher, dass Du den Aktivierungsprozess beenden willst? + Möchtest Du den Aktivierungsprozess wirklich abbrechen? + Hält Deine Kryptowährungen sicher und offline. Schlank wie eine Kreditkarte, sicherer als ein Banktresor. Wenn dies der Fall ist, musst Du von vorne beginnen. Vorhandene Wallet über Google Drive-Backup wiederherstellen Google Drive-Backup - Verbesser Deine Sicherheit sofort mit einer erstklassigen Hardware-Wallet von Tangem. + Erstelle eine neue, sichere Wallet und übertrage Deine Gelder, um zusätzlichen Schutz zu gewährleisten. + Neue Wallet erstellen + Erhöhe Deine Sicherheit mit der überlegenen Hardware-Wallet von Tangem. Hardware-Wallet + Übertrage Deine aktuelle Wallet in die Tangem Wallet. + Aktualisiere Deine aktuelle Wallet Gehe zum Backup - Um Deine Wallet mit einem Zugangscode zu sichern, führe zuerst die Sicherung durch. - Sicherung zuerst beenden - Physische Karten, die Deine Kryptowährung sicher offline speichern. + Bitte sicher Deine Wallet, bevor Du einen Zugangscode erstellst. + Zuerst die Sicherung abschließen + Andere Methoden + Physische Geräte, die Deine privaten Schlüssel sicher offline speichern. Wiederherstellungs-Phrase + Um Deine Wallet auf Hardware umzustellen, erstelle vorher ein Backup. Schlüssel werden in der App gespeichert Sicherung der Seed-Phrase Mobile Wallet erstellen Vorhandene Wallet importieren Diese Wiederherstellungsphrase wurde bereits importiert Mobile Wallet + Wallet dauerhaft entfernen + Diese Wallet wird dauerhaft von Deinem Gerät entfernt. + Bist du dir sicher, dass du das tun willst? + Wallet dauerhaft entfernen + Zum Backup wechseln + Backup anzeigen + Entferne Deine Wallet für immer! + Trotzdem entfernen! + Für diese Wallet existiert ein Backup. Überprüfe dieses bitte, bevor Du sie entfernst, um sicherzustellen, dass Du sie später wiederherstellen kannst. + Wenn Du diese Wallet ohne Backup entfernst, verlierst Du dauerhaft den Zugriff auf Deine Assets. + Diese Wallet dauerhaft entfernen? + Mir ist bewusst, dass ich den Zugriff auf meine Wallet verliere kann, wenn ich sie vor der Entfernung nicht gesichert habe. + Mir ist bewusst, dass durch das Entfernen meiner Wallet diese nicht gelöscht, sondern lediglich von meinem Gerät entfernt wird. + Es wird keine Seed-Phrase mehr benötigt – Deine Tangem-Karte oder Dein Tangem-Ring wird zu Deinem sicheren Backup. + Backup mit Tangem + Ein Upgrade ist nicht möglich. Auf diesem Gerät ist bereits eine Wallet vorhanden. + Wähle ein anderes Gerät. Dieses Gerät kann für das Upgrade nicht verwendet werden. + Bei der Operation ist ein Fehler aufgetreten. Dein Geld bleibt während des gesamten Prozesses sicher und vollständig zugänglich Zugang zu Geldern - Alle privaten Wallet-Daten werden aus der mobilen App entfernt und nur sicher auf Deinem Tangem-Gerät gespeichert + Die Daten Deiner Wallet werden aus der App gelöscht und auf Deinem Tangem-Gerät gespeichert Allgemeine Sicherheit - Private Schlüssel werden von der App auf Deine Tangem-Karte oder Deinen Tangem-Ring verschoben + Die privaten Schlüssel werden von der App auf Ihr Tangem-Gerät übertragen. Schlüsselmigration Gerät scannen Upgrade starten - Du bist dabei, Deine Wallet auf Tangem Wallet zu aktualisieren. Dies wird Dein Vermögen mit cold-storage sicher verwahren. + Du stehst kurz vor dem Upgrade auf unsere Hardware-Wallet. Damit werden Deine Vermögenswerte sicher in Offline-Speichern aufbewahrt. Tangem Wallet Upgrade auf Hardware Wallet - Bewahren Deine Kryptowährungen sicher auf - mit der erstklassigen Hardware-Wallet von Tangem. - Wallet mit einem Hardware-Backup aufrüsten - Diese Informationen wurden mithilfe von KI generiert.\nTippe hier, wenn Du Fehler finden. + Schütze Deine Kryptowährungen mit Tangems erstklassiger Hardware-Wallet. + Rüste Deine Wallet auf Hardware-Sicherheit auf. + Diese Informationen wurden mithilfe von KI generiert.\nTippe hier, wenn Du Fehler findest. Um den Zugangscode zu ändern, halte die Karte oder Ring wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs. Um den Passcode zu ändern, halte die Karte oder Ring wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs. Um die Wallet zu erstellen, halte die Karte oder Ring wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs. @@ -599,6 +661,7 @@ Der ausgewählte Token ist derzeit nicht für Aktionen innerhalb der Krypto-Wallet verfügbar. Aber keine Sorge, du kannst dein Interesse bekunden, indem du den Token hochstufen. Hochstimmen Das Wallet unterstützt nicht mehr als ein Netzwerk + Über diesen Coin Um dieses Asset zu kaufen, zu tauschen oder zu erhalten, füge diesen Deinem Portfolio hinzu Dieses Asset wird derzeit in der Wallet nicht unterstützt. Dieses Asset ist für dieses Wallet nicht verfügbar @@ -634,6 +697,7 @@ Beliebt Staking ist der einfachste Weg, um Belohnungen für Deine Kryptowährung zu erhalten. %s Verdiene bis zu %s APY + Token hinzugefügt Über %s %d börse @@ -779,7 +843,7 @@ Token hinzufügen Du hast eine Backup-Karte oder einen Backup-Ring hinzugefügt. Nach Abschluss des Backup-Vorgangs kannst Du keine weiteren Backup-Geräte hinzufügen. Wenn Du eine weitere Karte oder einen weiteren Ring hast, fügen diesen dem Backup jetzt hinzu. Möchtest Du den Backup-Vorgang fortsetzen? Der Sicherungsvorgang ist teilweise abgeschlossen. Du kannst ihn jetzt nicht beenden. - Die Passphrase ist eine fortschrittliche Sicherheitsfunktion, die von Krypto-Wallets verwendet wird. Sie fügt ein zusätzliches Wort oder eine Phrase deiner Wahl zu der bereits bestehenden Wiederherstellungsphrase hinzu, um einen brandneuen Satz von Adressen zu erzeugen. + Eine Passphrase ist eine optionale Sicherheitsfunktion, die Deiner Wiederherstellungsphrase ein Wort oder eine Phrase hinzufügt und so einen neuen Satz von Wallet-Adressen für zusätzlichen Schutz erstellt. Hinzufügen einer Sicherungskarte oder Ring Karte oder Ring scannen Scanne die Karte oder Ring Nr. %d @@ -807,6 +871,7 @@ Für die Karte oder Ring, die du hinzufügen möchtest, wurde bereits eine andere Wallets erstellt. Wenn du Guthaben auf dieser Wallets hast, hebe es bitte ab, setze diese Karte oder Ring zurück und füge sie als Backup hinzu. Sicher deine Wallet Backups anlegen + Biometrische Daten Lese mehr über die Seed-Phrase @@ -868,6 +933,7 @@ Andere Währungen Beliebte Fiats Suche nach Währung + Die besten Preise erzielen... Sofort Durch die Nutzung der Onramp-Funktionalität stimmst Du den %1$s und %2$s des Anbieters zu. Der Service wird von einem externen Anbieter bereitgestellt. \n Tangem übernimmt keine Verantwortung. @@ -894,7 +960,12 @@ Bitte wähle das richtige Land aus, um korrekte Zahlungsoptionen und Dienstleistungen zu gewährleisten. Einstellungen Du kannst diesen Bildschirm schließen und den Transaktionsstatus auf dem Bildschirm mit den Token-Details überprüfen. + + bis zu %d Tag + bis zu %d Tagen + %s min + Erhältlich ab Du erhältst Der Service wird von einem externen Anbieter bereitgestellt. \n Tangem übernimmt keine Verantwortung. Du kannst diesen Bildschirm schließen und den Transaktionsstatus auf dem Bildschirm mit den Token-Details überprüfen. @@ -959,6 +1030,10 @@ Mir ist klar, dass ich diese Karte oder Ring nicht verwenden kann, um meinen Zugangscode auf den anderen Karten oder Ringe der aktuellen Wallet wiederherzustellen Durch das Zurücksetzen auf Werkseinstellungen wird die Wallet vollständig von der ausgewählten Karte oder Ring gelöscht. Du kannst die aktuelle Wallet nicht wiederherstellen oder die Karte oder Ring verwenden, um den Zugangscode wiederherzustellen. Beim Zurücksetzen auf die Werkseinstellungen wird die Wallet der ausgewählten Karte oder Ring vollständig gelöscht und aus der App entfernt. Es ist nicht möglich, die aktuelle Wallet wiederherzustellen. + Alle Tangem-Geräte wurden zurückgesetzt. + Bei der Aktivierung ist ein Fehler aufgetreten. Bitte setze die Karten nacheinander zurück. + Kartenprüfung fehlgeschlagen + Bitte setze das nächste Gerät zurück, um fortzufahren. Ringbesitzer erhalten bis zum 15.11. 3 provisionsfreie Swaps auf Changelly! Jetzt mit 0 % Gebühren tauschen! Melde dich bei der App an und überprüfe dein Guthaben, ohne die Karte oder Ring zu scannen @@ -1083,11 +1158,11 @@ Bist Du sicher, dass Du den Empfangstoken ändern möchtest? Dadurch werden Dein zuvor eingegebenen Daten zurückgesetzt. Token ändern Tauschen und senden - Mit der Konvertierung fortfahren? Dadurch werden Deine vorherigen Daten gelöscht. + Mit dem Datentausch fortfahren? Dadurch werden Deine vorherigen Daten gelöscht. Konvertierung bestätigen Das Senden einer anderen Währung führt zu deren unwiderruflichem Verlust. Wähle das richtige Empfängernetzwerk - Sende uns ein Token, und wir konvertieren es unterwegs. Dein Empfänger erhält genau das, was er braucht – nahtlos. + Wähle einen beliebige Token aus, den Du bekommen möchtest. Dein Empfänger erhält genau das, was Du ausgewählt hast – nahtlos. Der Empfänger erhält An den Empfänger Zu erhaltender Betrag @@ -1102,11 +1177,12 @@ Name Lass deinen Token für dich arbeiten Eine Netzwerkgebühr ist eine kleine Zahlung, die erforderlich ist, um Deine Transaktion auf der Blockchain zu verarbeiten und zu bestätigen. - Um mit dem Staking zu beginnen, muss Dein TON-Konto mit einer Selbsttransaktion von 1 TON aktiviert werden. Das Guthaben verbleibt in Deiner Wallet – dieser Schritt aktiviert Dein Konto lediglich für das Staking. + Um mit dem Staking zu beginnen, muss Dein TON-Konto mit einer Transaktion von 1 TON aktiviert werden. Das Guthaben verbleibt auf Deinem Konto, dieser Schritt dient lediglichder aktivierung für das Staking. Kontoaktivierung Die Anzahl der zu stakenden Krypros muss mindesten %s betragen Der Stakingbetrag wird aufgrund der Netzwerkregeln auf %1$s TRX aufgerundet. Der Betrag der unstaked wird, wird aufgrund der Netzwerkregeln auf %1$s TRX gerundet. + Effektiver Jahreszins %1$s%% Nicht gestakte beanspruche Gebühr für das Staking-Konto Ein Staking-Konto ist ein spezielles Konto, auf dem eingesetzte SOL-Token gespeichert werden. Es wird erstellt, indem Sie Ihre Token an einen Validator delegieren, um an der Transaktionsvalidierung teilzunehmen und Belohnungen zu erhalten. Für die Einrichtung des Staking-Kontos wird eine geringe Gebühr erhoben, die nach Abschluss des Stakings zurückerstattet wird. @@ -1167,10 +1243,10 @@ Wenn Du im %1$s -Netzwerk mit einem neuen Validator stakest, werden alle zuvor eingesetzten Gelder automatisch an diesen Validator übertragen. Reinvestiert Deine verdienten Prämien in Deinen Einsatzbetrag und erhöht so den potenziellen Gewinn. Mit Restake kannst Du Dein Guthaben von einem Validator zu einem anderen verschieben, ohne dass Du den Stake aufheben musst. - Du bist dabei, Dein gesamtes Guthaben zu staken. Wir empfehlen, einen kleinen Betrag übrig zu lassen, um die Netzwerkgebühren für die Aufhebung des Stakes oder das Einfordern von Prämien abzudecken. - Um mit dem Staking zu beginnen, muss Dein TON-Konto mit einer Selbsttransaktion von 1 TON aktiviert werden. Das Guthaben verbleibt in Deiner Wallet – dieser Schritt aktiviert Dein Konto lediglich für das Staking. + Wenn Du Dein gesamtes Guthaben einsetzt, musst Du beim Aufheben der Einsätze eine Netzwerkgebühr entrichten. Wir empfehlen, einen kleinen Betrag in Deiner Wallet zu belassen, um die Netzwerkgebühren zu decken. + Um mit dem Staking zu beginnen, musst Du zuerst Dein TON-Konto aktivieren. Kontoaktivierung - Um mit dem Staking bei TON zu beginnen, führe zunächst eine ausgehende Transaktion in beliebiger Höhe durch – dadurch wird Deine Wallet aktiviert. + Um mit dem Staking in TON zu beginnen, sende zunächst eine kleine Transaktion an Deine eigene Adresse – dadurch wird Deine Wallet aktiviert. Für den Abschluss der Transaktion können zusätzlich zur Netzwerkgebühr bis zu 0,2 TON erforderlich sein. Nicht genutzte Beträge werden zurückerstattet. Für diesen Vorgang sind zusätzlich zur Netzwerkgebühr 0,2 TON erforderlich. Bitte lade Dein Guthaben auf. TON-Reserve erforderlich @@ -1275,10 +1351,48 @@ Du erhältst Token auswählen Nicht verfügbar + 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. + Karte sperren lassen? + Einfrieren + Hilfe erhalten + Abgeschlossen + Abgelehnt + Ausstehend + Die Bank hat diese Transaktionsanfrage abgelehnt. + Diese Gebühr dient zur Deckung der Kosten für die Abwicklung Deiner Überweisung. + Abhebung + Guthaben hinzufügen + Aufladeoptionen + PIN ändern + Die Karte ist vollständig für Zahlungen bereit. + PIN-Code erstellt Daten konnten nicht geladen werden. Versuche es später noch einmal. + Karte einfrieren Verstecken + Google Wallet öffnen + Richte Tangem Pay mit wenigen Klicks ein und bezahlen mit Google Pay. + Karte zu Google Pay hinzufügen + Google Wallet öffnen + Tippe auf „Karte hinzufügen“. + 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. + Karte zu Google Pay hinzufügen + Teile Deine Adresse mit oder zeig den QR-Code. + Es wurden technische Probleme festgestellt. Bitte versuche es später erneut oder kontaktiere den Support. + Empfangen ist jetzt nicht verfügbar Aufdecken + Tausche beliebige Vermögenswerte in Deinem Portfolio gegen eine Karte. Kartendetails + Konto erstellen und Karte ausstellen + Tangem Pay + Status anzeigen + KYC für Tangem Pay in Arbeit Karte erhalten Füge es Deiner Wallet hinzu und bezahle überall mit Deinem Smartphone. Apple Pay & Google Pay @@ -1304,6 +1418,7 @@ Die Geldsendung wird verfügbar, sobald die ausstehende(n) Transaktion(en) im Netzwerk %s abgeschlossen ist/sind. Der Verkauf von %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen. Staking %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen. + Die Genehmigung wurde widerrufen. Dein Guthaben befindet sich weiterhin im Ertragsmodus. Um Aktionen durchzuführen, wechsel bitte in den Ertragsmodus und erteilen die Berechtigung erneut. Generiere XPUB Ausblenden Du bist dabei, dieses Token vom Hauptbildschirm auszublenden. Du kannst es jederzeit über die Seite „Token verwalten“ wieder hinzufügen. @@ -1367,6 +1482,7 @@ Aktiviere Push-Benachrichtigungen und wir benachrichtigen Dich sofort, wenn Gelder eintreffen Verpasse keine Transaktion Neues Wallet hinzufügen + Wenn Du diese Wallet ohne vorheriges Backup löschst, verlierst Du dauerhaft den Zugriff auf Dein Guthaben. Möchtest Du diese Wallet wirklich entfernen? Es ist ein Fehler aufgetreten, bitte scanne deine Karte oder Ring, um sich anzumelden Dieses Wallet wurde bereits gespeichert, du kannst ein weiteres hinzufügen @@ -1382,15 +1498,19 @@ Guthaben und Limits Zugangscode + Aktivierung des Kontos Bitte wähle die Wallet aus, mit der Du den Registrierungsprozess gestartet hast, um die Transaktion zur Erstellung Deines Kontos auf der Blockchain zu signieren Möchtest Du den Vorgang wirklich beenden? Du kannst später dort fortfahren, wo Du aufgehört hast. Das wird nicht lange dauern. Wir richten gerade Dein Konto ein. Das wird nicht lange dauern. Wir schließen die Aktivierung ab. Alles wird vorbereitet! + Andere Wallet PIN-Code erstellen PIN wurde nicht akzeptiert. Versuche es erneut oder verwende einen anderen Code. Ungültige PIN: Sequenzen oder Wiederholungen vermeiden Schon kann es losgehen! + Halte die Tangem-Karte bereit und tippe diese um zum Genehmigen. + Tangem Wallet vorbereiten Du kannst Deine Verbindung \n auf der Website des Drittanbieters \n herstellen und zurück zur Tangem-App gelangen Zur Website gehen Wallet wählen @@ -1402,6 +1522,13 @@ Freischalten Scanne Deine Karte, um den Zugriff freizuschalten Benötigte Freischaltung + Wähle aus, wie Du Deine Wallet hinzufügen möchtest. + Scanne Deine Tangem-Karte oder Deinen Tangem-Ring, um sie wiederherzustellen oder aus einer anderen Wallet zu importieren. + Hardware-Wallet erstellen + Möchtest Du eine Tangem-Wallet kaufen? + Seed-Phrase importieren + Stell Deine Wallet auf Deinem Smartphone wieder her oder importieren diese aus einer anderen App – praktisch, aber weniger sicher als eine Tangem-Karte. + Was soll man wählen? Blockchain ist nicht erreichbar. Versuche es später nochmal Karte oder Ring scannen Dieses Wallet wurde bereits früher aktiviert. \n Wenn dies nicht von Dir durchgeführt wurde, wende Dich bitte an den Support. \n Tangem verkauft niemals Wallets zusammen mit vorgenerierten Zugangscodes. @@ -1422,6 +1549,8 @@ Falsche Karte oder Ring in der Tangem App ausgewählt Transaktion aus Dapp-Daten konnte nicht erstellt werden. Code: %s Es ist ein unbekannter Fehler aufgetreten. Fehlercode: %d. Wenn das Problem weiterhin besteht, wende dich bitte an unseren Support + Mehrteilige Transaktion + Zur erfolgreichen Abwicklung wird Deine Transaktion in mehrere Teile aufgeteilt. Du musst Deine Karte mehrmals ans smartphone halten, um sie abzuschließen. Keine geöffneten WalletConnect-Sitzungen Hoppla. Keine Sitzungen. WalletConnect-Sitzung konnte nicht gekoppelt werden: %1$s @@ -1433,29 +1562,32 @@ Mit dieser Karte oder Ring kann keine WalletConnect-Sitzung hergestellt werden Dieses Netzwerk wird nicht unterstützt. Bitte wähle ein anderes Netzwerk. Netzwerk auswählen + Die Transaktion wird verarbeitet. Bitte halte Deine Karte mehrmals an, um sie abzuschließen. + Transaktion läuft WalletConnect-Sitzungen Mit dApps verbinden WalletConnect Das Herstellen der Verbindung kann einige Sekunden dauern + Tangem-Wallet erstellen Von %s - Kaufe eine Tangem Wallet – physische Karten, die Deine Kryptowährungen sicher offline speichern. + Kaufe eine Tangem Wallet – ein physisches Gerät, das Deine privaten Schlüssel sicher offline speichert. Hardware-Wallet - Eine sichere Wallet wird in Sekundenschnelle auf Deinem Telefon erstellt. + Erstelle in Sekundenschnelle eine sichere Wallet auf Deinem Smartphone. Mobile Wallet - Was soll ich wählen? - Besitzt Du bereits eine Tangem Wallet? + Was auswählen? + Verwendest Du bereits eine Tangem-Wallet? Jetzt scannen - Wähle, wie Du Deine Wallet erstellen möchten + Wähle eine Methode zur Einrichtung Deiner Wallet. Möchtest Du eine Tangem-Wallet kaufen? Jetzt kaufen - Eine vorhandene Wallet wiederherstellen, die Deinem Google Drive-Backup gespeichert ist + Stelle Deine bestehende Wallet über ein Google Drive-Backup wieder her. Importieren aus Google Drive Vorhandenes Wallet hinzufügen Physische Karten, die Deine Kryptowährung sicher offline speichern. Scanne eine Tangem-Wallet - Deine Seed-Phrase, um Deine Wallet wiederherzustellen + Importiere eine bestehende Wallet mit Deiner Wiederherstellungsphrase. Wallet importieren - Importieren einer Wiederherstellungsphrase + Wiederherstellungsphrase eingeben Du hast Deine Wallet erfolgreich gesichert. Wallet importieren Import abgeschlossen @@ -1468,7 +1600,7 @@ Jetzt mit 10 % Rabatt kaufen Greife auf 13.000+ Kryptowährungen zu. Kaufe, verkaufe, tausche und stake Sie mit einem einzigen Fingertipp.\nVerbinde bis zu drei Karten für ein Backup. Entdecke die Tangem Wallet - Geheimcode zum Schutz dieser Wallet. Wird für Anmeldung und Signaturen verwendet. + Dieser geheime Code schützt Deine Wallet und wird zum Einloggen und Signieren von Transaktionen verwendet. Zugangscode festlegen/ändern Zugangscode ändern Bleib stets über eingehende Wallet-Transaktionen und Tangem-Updates auf dem Laufenden. @@ -1648,7 +1780,7 @@ Keine Wallet-Änderungen erkannt Es wurden potenzielle Risiken oder bösartiges Verhalten erkannt. Das Verbinden oder Signieren von Transaktionen kann zum Verlust von Geldern führen. Bekanntes Sicherheitsrisiko - Öffne die Web3-App und wähle die Option WalletConnect + Öffne die Web3-App und wähle die Option WalletConnect. Anfrage von Trotzdem signieren Art der Signatur @@ -1663,6 +1795,20 @@ URI bereits verwendet WalletConnect Verdächtige Transaktion + Besitzt Du bereits eine Tangem Wallet? + Tausende von Vermögenswerten + Erstklassige Hardware-Wallet + Schnelle Lieferung + Mit einem Fingertipp starten + Nahtlos und sicher + Einfach zu bedienen + Erstelle eine Hardware-Wallet mit Tangem. Schlank wie eine Bankkarte, sicher wie ein Banktresor. + Erstelle oder importiere eine Software-Wallet + Erstelle oder importiere eine Software-Wallet auf Deinem Telefon + Beginne mit Mobile Wallet + Andere Methode + Tangem Hardware Wallet verwenden + Mehr erfahren & kaufen Verwerfen Du hast eine unterbrochene Sicherung. Möchtest du diese fortsetzen? Ja, fortsetzen @@ -1679,7 +1825,7 @@ Damit Sie beim nächsten Aufladen Ihrer Brieftasche keine erhöhte Provision zahlen, soll der Betrag um %s XTZ reduziert werden Wenn der Yield-Modus aktiviert ist, gehen alle zukünftigen Einzahlungen an diese Adresse an Aave. Du kannst über Dein Guthaben weiterhin frei verfügen. Deine %s wird an Aave übermittelt - Die Bereitstellung von %1$s %2$s für Aave steht aus + Die Lieferung von %1$s %2$s an Aave steht noch aus. Genehmigen Die Genehmigung Deines Tokens wurde widerrufen. Erteilen diese erneut, um die Servicefunktionalität fortzusetzen. Genehmigung erforderlich @@ -1689,14 +1835,14 @@ Deine Gelder werden derzeit dem Aave-Protokoll bereitgestellt, Du kannst sie jedoch jederzeit verwalten. Deine%s ist in Aave hinterlegt Chart konnte nicht geladen werden... - Der erhaltene Betrag %1$s %2$s wurde nicht auf Aave eingezahlt. + Das Bereitstellen von %1$s %2$s bei Aave steht aus. APY %1$s%% Verfügbar Aktueller effektiver Jahreszins Beim Aufladen zum Ausleihen wird eine Netzwerkgebühr von maximal %1$s vom Guthaben abgezogen. - Die Netzwerkgebühr ist derzeit zu hoch, um die Kreditvergabe durchzuführen. Die Mittel werden bereitgestellt, sobald sie auf %1$s oder darunter fallen. + Die Netzwerkgebühr ist derzeit zu hoch für die Kreditvergabe. Die Auszahlung erfolgt, sobald sie auf %1$s oder darunter sinkt. Meine Mittel - Dein %1$s ist nun bei Aave hinterlegt und bringt Zinsen ein. Du besitzt einen %2$s-Token, der Dein Guthaben repräsentiert und mit der Zeit wächst. Wenn Du mehr Geld einzahlst, wird dieses an Aave weitergeleitet, um Zinsen abzüglich einer Transaktionsgebühr zu erwirtschaften. + Deine %1$s sind nun bei Aave angelegt und erwirtschaften Rendite. Du besitzt %2$s -Token, die Dein Guthaben repräsentieren und automatisch Rendite generieren. Bei jeder Aufladung wird Dein Aave-Konto zusätzliches Guthaben gutgeschrieben, um weitere Rendite zu erzielen (abzüglich Gebühren). Ertragsmodus Gesamtverdienst Übertragungen zu Aave @@ -1704,10 +1850,10 @@ Dies ist die aktuelle Liefergebühr auf %s. Die tatsächlichen Kosten werden auf der Registerkarte \"Aktivierung\" angezeigt. Aktuelle Gebühr Alle zukünftigen %s-Einzahlungen werden automatisch an Aave geliefert, wobei die Transaktionsgebühr abgezogen wird. - Bei jeder zukünftigen Aufladung wird eine Netzwerkgebühr von ungefähr %1$s (%2$s) abgezogen, die Ihr Limit von %3$s (%4$s) nicht überschreitet. + Von jeder zukünftigen Aufladung wird eine ungefähre Netzwerkgebühr von %1$s ( %2$s ) abgezogen, die Dein Limit von %3$s ( %4$s ) nicht überschreiten wird. Wenn die Netzwerkgebühren über die maximale Gebühr steigen, wird die Transaktion erst durchgeführt, wenn diese sinken. Du kannst dieses Limit später ändern. Maximale Gebühr - Der Mindestbetrag wird aus der aktuellen Netzwerkgebühr berechnet, um sicherzustellen, dass er 4%% nicht überschreitet, was den Mindestbetrag %1$s (%2$s) ergibt. + Der Mindestbetrag wird auf Grundlage der aktuellen Netzwerkgebühr berechnet, sodass er 4%% des Aufladebetrags nicht überschreitet, was einen Mindestbetrag von %1$s (%2$s) ergibt. Mindestaufladung Gebührenpolitik Tangem erhebt außerdem eine Servicegebühr von 15% auf den erzielten Ertrag. @@ -1722,7 +1868,7 @@ Senden, tauschen oder verkaufen Deine Gelder sofort, wann immer Du willst. Jederzeit Zugriff auf Dein Geld Wie funktioniert das? - Aave ist ein dezentrales Protokoll, das einen Gesamtwert von über 61 Milliarden US-Dollar verwaltet. + Aave ist ein dezentrales Protokoll, das ein Gesamtvermögen von über 61 Milliarden Dollar verwaltet. Dezentral und selbstverwahrend Durch die Nutzung dieses Dienstes erklärst Du Dich mit provider\n%1$s und %2$s einverstanden Mit Aave verbinden @@ -1734,25 +1880,28 @@ Unterstützt durch Der Zinssatz ist variabel Wenn Du dein Guthaben auflädst, wird es automatisch an Aave weitergeleitet, um Zinsen zu verdienen. zur Deckung der Transaktionsgebühr wird ein Betrag von %s abgezogen. - Beginn des Verdienstes + Vermögenswerte liefern Dein %s wird an Aave übermittelt, bleibt aber verwaltbar. Siehe Gebührenrichtlinie Deine nächste Aufladung wird automatisch an Aave weitergeleitet. Aktiv Pausiert Deaktiviere den Yield-Modus - Wenn Du diese Option deaktivierst, wird Dein Geld von Aave auf %s in Deiner Wallet abgehoben und es werden keine Belohnungen mehr verdient. + Wenn Du diese Option deaktivierst, werden Deine Vermögenswerte von Aave abgezogen, in Deiner Wallet wieder in %s umgewandelt und die Zinsgutschrift gestoppt. Die Netzwerkgebühr wird von dem Betrag, den Du abhebst, abgezogen. + Deaktiviere den Yield-Modus + Angebot Effektiver Jahreszins für Versorgung Effektiver Jahreszins - Zinsen fallen automatisch an + Die Zinsen fallen automatisch an. Zinsen fallen automatisch an Ertragsmodus Bearbeitung Deiner Einzahlung + Ertragsmodus Automatisch Einzahlung von %1$s %2$s zur Deckung der Netzwerkgebühr für Transaktionen Die Gebühr %s kann nicht gedeckt werden - Der Stakingservice ist derzeit nicht verfügbar. Bitte versuche es später erneut. - Einnahmen nicht verfügbar + Der Yield-Modus ist momentan nicht verfügbar. Bitte versuche es später erneut. + Ausweichmodus nicht verfügbar Chart konnte nicht geladen werden... diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index fc32c4eedf..a84c13660c 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -194,6 +194,7 @@ Velocidad y tarifa Finalizar Sincronizar direcciones + Comenzar Ir al proveedor Ir al token Entendido @@ -396,6 +397,7 @@ Proveedor Mejor tarifa Lista de advertencias de la FCA + Mejor opción Proveedor en la lista de advertencias de la FCA Disponible hasta %s Disponible desde %s @@ -780,6 +782,7 @@ Por saldo Organizar tokens Desagrupar + Soporte de %s Más información Puede activar las Notificaciones para Tangem en Ajustes. Activar más tarde @@ -973,6 +976,7 @@ Esto eliminará la wallet de la aplicación. La wallet en sí puede\nañadirse de nuevo. Nombre Haga que su token trabaje para Ud. + Para comenzar a hacer staking, debe activar su cuenta TON con una auto-transacción de 1 TON. Los fondos permanecerán en su billetera; este paso solo es necesario para habilitar su cuenta para staking. La cantidad del staking debe ser al menos %s La cantidad de staking se redondeará a %1$s TRX debido a las reglas de la red. La cantidad de unstaking se redondeará a %1$s TRX debido a las reglas de la red. @@ -981,6 +985,7 @@ Una cuenta de staking es una cuenta especial donde se almacenan los tokens SOL de staking. Se crea cuando delegas sus tokens a un validador para participar en la validación de transacciones y ganar recompensas. Se cobra una pequeña tarifa por crear la cuenta de staking, que se devuelve una vez que se completa el staking. Porcentaje de rendimiento anual El porcentaje de rendimiento anual que podrá ganar como participante en el staking. + APY muestra el porcentaje de rendimiento anual del validador en función de su rendimiento APR APY Las recompensas se acumulan automáticamente en su saldo de staking diariamente. @@ -1037,6 +1042,7 @@ Reinvierta las recompensas obtenidas en el monto apostado, aumentando las ganancias potenciales. La opción de volver a staking le permite mover sus fondos de un validador a otro sin necesidad de retirarlos. Está a punto de hacer staking con todo su saldo. Recomendamos dejar una pequeña cantidad para cubrir las tarifas de red para hacer unstaking o reclamar recompensas. + Para empezar a hacer staking, primero debes activar tu cuenta de TON. Para empezar a hacer staking en TON, realice primero una transacción de salida de cualquier importe - esto activará su billetera. Es posible que se requieran hasta 0.2 TON además de la tarifa de red para completar la transacción. Cualquier cantidad no utilizada será reembolsada. Se requieren 0.2 TON para realizar esta operación, además de la tarifa de red. Por favor, recargue su saldo. @@ -1156,6 +1162,7 @@ El envío de fondos estará disponible una vez que se complete(n) la(s) transacción(es) pendiente(s) en la red %s Vender %s no es compatible con los proveedores actuales, pero estamos trabajando para agregar más opciones. El staking de %s no es compatible con los proveedores actuales, pero estamos trabajando para agregar más opciones. + Se ha revocado la aprobación. Sus fondos permanecen en modo de rendimiento. Para realizar acciones, vuelva al modo de rendimiento y otorgue el permiso nuevamente. Generar XPUB Ocultar Está a punto de ocultar este token de la pantalla principal. Puede volver a agregarlo en cualquier momento a través de la página de gestión de tokens. @@ -1490,7 +1497,7 @@ Sus fondos se suministran actualmente al protocolo Aave, pero puede gestionarlos en cualquier momento. Su %s está depositado en Aave No se puede cargar el gráfico... - La cantidad recibida, %1$s %2$s no fue suministrada a Aave. + El suministro de %1$s %2$s a Aave está pendiente. APY %1$s%% Disponible APY actual @@ -1508,7 +1515,7 @@ Se deducirá una comisión de red aproximada de %1$s (%2$s) de cada recarga futura, y no superará su límite de %3$s (%4$s). Si las tarifas de red superan la tarifa máxima, la transacción no se realizará hasta que disminuyan. Puede cambiar este límite más adelante. Tarifa máxima - El monto mínimo se calcula a partir de la tarifa de red actual para garantizar que no exceda 4%%, lo que hace que el mínimo sea %1$s (%2$s). + El monto mínimo se calcula en función de la comisión de la red actual para que no supere el 4%% del importe de la recarga, lo que hace que el mínimo sea %1$s (%2$s). Recarga mínima Política de tarifas Tangem también cobra una comisión de servicio del 15% sobre el rendimiento obtenido. @@ -1541,9 +1548,11 @@ Sus próximas recargas se suministrarán automáticamente a Aave. Activo En pausa - Desactivar el modo de rendimiento + Desactivación del modo de rendimiento Al desactivar esto, retirará sus fondos de Aave a %s en su billetera y dejará de ganar recompensas. La comisión de red se deducirá del importe que retire. + Desactivar el modo de rendimiento + Suministrar Suministro APY APY Los intereses se devengan automáticamente @@ -1555,5 +1564,6 @@ 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. - Ganancias no disponibles + 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 ef9f1f5500..cb8fba86f1 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -172,6 +172,7 @@ Vitesse et frais Terminer Synchroniser les adresses + Commencer Aller au fournisseur Aller au jeton Compris @@ -372,6 +373,7 @@ Fournisseur Meilleur taux Liste d’avertissement de la FCA + Meilleur choix Disponible jusqu\'à %s Disponible à partir de %s Indisponible pour cette paire @@ -753,6 +755,7 @@ Par solde Organiser les jetons Dégrouper + Assistance %s Plus d\'infos Vous pouvez activer les notifications pour Tangem dans les paramètres. Activer plus tard @@ -947,6 +950,7 @@ Cela supprimera le portefeuille de l\'application. Le portefeuille lui-même peut être ajouté à nouveau. Nom Faites travailler votre jeton pour vous + Pour commencer à staker, votre compte TON doit être activé avec une transaction de 1 TON. Les fonds restent sur votre compte, car cette étape sert uniquement à l\'activer pour le staking. Le montant à staker doit être au moins %s Le montant du staking sera arrondi à %1$s TRX en raison des règles du réseau. Le montant d\'annulation du staking sera arrondi à %1$s TRX en raison des règles du réseau. @@ -1011,6 +1015,7 @@ Réinvestissez vos récompenses gagnées dans le montant que vous avez staké, augmentant ainsi vos gains potentiels. L\'option de restaker vous permet de déplacer vos fonds d\'un validateur à un autre sans avoir besoin de les déstaker. Vous êtes sur le point de staker l\'intégralité de votre solde. Nous vous recommandons de laisser un petit montant pour couvrir les frais de réseau pour unstaking ou la réclamation des récompenses. + Pour commencer le staking, vous devez d’abord activer votre compte TON Pour commencer à staker TON, effectuez d\'abord une transaction sortante de n\'importe quel montant — cela activera votre portefeuille. Jusqu\'à 0,2 TON peuvent être requis en plus des frais de réseau pour terminer la transaction. Tout montant non utilisé sera remboursé. 0,2 TON requis pour effectuer cette opération, en plus des frais de réseau. Veuillez recharger votre solde. @@ -1469,7 +1474,7 @@ Vos fonds sont actuellement affectés au protocole Aave, mais vous pouvez les gérer à tout moment. Vos %s sont déposés dans Aave. Impossible de charger le graphique... - Le montant reçu, %1$s %2$s, n\'a pas été fourni à Aave. + L’approvisionnement de %1$s %2$s sur Aave est en attente. APY %1$s%% Disponible APY actuel @@ -1487,7 +1492,7 @@ Des frais de réseau d\'environ %1$s (%2$s) seront déduits de chaque recharge future, sans dépasser votre limite de %3$s (%4$s). Si les frais de réseau dépassent le montant maximal, la transaction ne sera pas effectuée tant qu\'ils n\'auront pas diminué. Vous pouvez modifier cette limite ultérieurement. Frais maximaux - Le montant minimum est calculé à partir des frais de réseau actuels afin de garantir qu\'il ne dépasse pas 4 %%, ce qui donne un minimum de %1$s (%2$s). + Le montant minimum est calculé en fonction des frais réseau actuels afin de ne pas dépasser 4%% du montant du rechargement, ce qui donne un minimum de %1$s (%2$s). Recharge minimale Politique tarifaire Tangem prélève également des frais de service de 15% sur les revenus générés. @@ -1520,9 +1525,10 @@ Vos prochains dépôts seront automatiquement transférés vers Aave. Actif En pause - Désactiver le mode rendement + Désactivation du mode de rendement En désactivant cela, vos fonds seront retirés d\'Aave vers %s dans votre portefeuille et vous ne gagnerez plus de récompenses. Les frais de réseau seront déduits du montant que vous retirez. + Désactiver le mode rendement Rendement annuel brut (APY) APY Les intérêts sont cumulés automatiquement. diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index d7457f6c77..74b96d49b9 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -488,7 +488,7 @@ プロバイダー ベストレート FCA警告リスト - ベストチョイス + 最適な選択 FCA警告リストに掲載されたプロバイダー 最大 %s まで使用可能 %s 以上で利用可能 @@ -531,6 +531,13 @@ 続行するには、%1sスマートコントラクトに%2sを使用する権限を付与してください 許可を与える 無制限 + バックアップは2枚または3枚のTangemカードを使って作成されます。紛失や破損に備えて、それぞれ別々の安全な場所に保管してください—シードフレーズは不要です。 + 複数のカードでバックアップ + Tangemウォレットを追加 + あなたの秘密鍵はTangemカード内部で直接生成され、決してカードの外に出ることはありません。 + 鍵生成 + すべての暗号処理は、複製や物理的な改ざんに対する認証を受けたセキュアチップ内で行われます。 + ハードウェアレベルのセキュリティ 既存のウォレットを追加 新しいウォレットを作成する Tangemを注文 @@ -569,6 +576,21 @@ 既存のウォレットをインポートする このリカバリーフレーズはすでにインポートされています。 モバイルウォレット + ウォレット削除 + このウォレットはデバイスから完全に削除されます。 + 本当にこれを実行しますか? + ウォレット削除 + バックアップへ移動 + バックアップを表示 + ウォレット削除 + とにかく削除する + このウォレットにはバックアップが存在します。後で復元できるように、削除する前に内容を確認してください。 + バックアップなしでこのウォレットを削除すると、資金へのアクセスを永久に失います。 + このウォレットを削除しますか? + ウォレットの削除前にバックアップをしていなければ、アクセスを失う可能性があることを理解しています。 + ウォレットを削除しても中身自体が消えるわけではなく、単にこのデバイスから表示が消えるだけであることを理解しています。 + もはやシードフレーズは不要——Tangemカードやリングがそのままあなたの安全なバックアップになります。 + Tangemでバックアップ アップグレードできません。このデバイスにはすでにウォレットが存在します。 別のデバイスを選択してください。このデバイスはアップグレードに使用できません。 操作中にエラーが発生しました。 @@ -1203,7 +1225,7 @@ 獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。 再ステーキングを使うと、ステーキングを解除することなく、あるバリデータから別のバリデータに資金を移動できます。 ウォレットの残高をすべてステーキングすると、ステーキング解除時にネットワーク手数料の支払いが必要になります。そのため、少額をウォレットに残しておくことをおすすめします。 - ステーキングを始めるには、TONアカウントを1 TONの自己取引で有効化する必要があります。資金はウォレット内にそのまま残り、この手順でステーキング用にアカウントを有効化できます。 + ステーキングを開始するには、まずTONアカウントを有効化してください。 アカウントの有効化 TONのステーキングを開始するには、まず自分のアドレスに少額の取引を送信します。これによりウォレットが有効になります。 取引を完了するには、ネットワーク手数料に加えて最大0.2TONが必要になる場合があります。未使用分は返金されます。 @@ -1315,6 +1337,9 @@ 取引を表示 サービス手数料 手数料 + カードを紛失したり盗まれたりしても、資金を安全に保てます。一時停止はいつでも解除できます。 + カードを一時停止しますか? + 一時停止 サポートを受ける 完了 拒否 @@ -1322,6 +1347,8 @@ 銀行がこの取引リクエストを拒否しました。 この手数料は、送金処理にかかるコストをカバーするためのものです。 出金 + 資金を追加 + 入金オプション PINを変更する カードは支払いの準備が整いました。 PINコードを作成しました @@ -1337,9 +1364,11 @@ お使いの端末に送信されたワンタイムパスワード(OTP)でカードを認証してください。 準備完了!カードはすぐに使用できます。 Google Payにカードを追加する + アドレスを共有するか、QRコードを表示してください。 技術的な問題が検出されました。しばらくしてからもう一度お試しいただくか、サポートにお問い合わせください。 現在、受け取りは利用できません 表示 + ポートフォリオ内のあらゆる資産をカードと交換 カードの詳細 アカウントを作成してカードを発行 Tangem Pay @@ -1776,7 +1805,7 @@ あなたの資金は現在Aaveプロトコルに預けられていますが、いつでも自由に管理できます。 あなたの%sはAaveに預けられています チャートを読み込めません・・ - 受け取った金額%1$s %2$sはAaveに入金されませんでした。 + Aaveへの%1$s %2$sの供給は保留中です。 年利%1$s%% 利用可能 現在のAPY @@ -1794,7 +1823,7 @@ 今後のチャージごとに、おおよそ%1$s(%2$s)のネットワーク手数料が差し引かれますが、上限の%3$s(%4$s)を超えることはありません。 ネットワーク手数料が上限手数料を超えた場合、手数料が下がるまで取引は成立しません。この制限は後で変更できます。 最大手数料 - 最小金額は現在のネットワーク手数料に基づいて計算されており、手数料が4%を超えないように設定されています。そのため、最小金額は%1$s(%2$s)となります。 + 最小金額は、ネットワーク手数料がチャージ額の4%を超えないように現在の手数料に基づいて計算されます。その結果、最小金額は%1$s(%2$s)となります。 最低入金額 手数料ポリシー Tangemはまた、得られた利回りに対して15%のサービス手数料を差し引きます。 @@ -1830,10 +1859,11 @@ 利回りモードを無効にする これをオフにすると、Aaveから資産が引き出され、ウォレット内の%sに変換され、利回りの発生が停止します。 出金金額からネットワーク手数料が差し引かれます。 + 利回りモードを無効にする 供給 供給APY APY - 利息は自動的に発生します + 利息は自動的に発生します。 利息は自動的に発生します 利回りモード 入金の処理中 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 8d29bcc1a7..bcbee9cd62 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -5,6 +5,7 @@ Невозможно восстановить аккаунт Архив Вы архивируете свой аккаунт, но в любое время можете вернуть его обратно + Архивация... Имя аккаунта уже существует Аккаунт Новый аккаунт @@ -96,6 +97,9 @@ %d устройств %d устройств + Пожалуйста, сбросьте следующее устройство, чтобы продолжить. + Сброс кошелька + Сброс завершен Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте. Использовать эту карту для сброса кода доступа на других картах в этом кошельке Восстановление доступа @@ -121,6 +125,7 @@ Недостаточно ADA Принять Доступ запрещен + Аккаунт Активировать Добавить Добавить в портфель @@ -232,6 +237,7 @@ Отклонить Перезагрузить Переименовать + Сброс Сохранить Сохранить изменения Поиск @@ -856,6 +862,7 @@ По балансу Упорядочить токены Список + Поддержка %s Подробнее Вы можете включить нотификации в настройках Включить позже @@ -1117,7 +1124,7 @@ Реинвестируйте свои заработанные награды в вашу застейканную сумму, увеличивая потенциальный доход Рестейк позволяет вам переместить средства из одного валидатора в другого без необходимости выхода из стейкинга. Вы собираетесь застейкать весь баланс, рекомендуем оставить небольшую сумму для оплаты комиссии сети при выходе из стейкинга или получении награды. - Чтобы начать стейкинг, ваш TON-аккаунт должен быть активирован транзакцией самому себе на 1 TON. Средства остаются в вашем кошельке — этот шаг лишь активирует ваш аккаунт для участия в стейкинге. + Чтобы начать стейкинг, сначала активируйте свой TON-аккаунт. Активация аккаунта Чтобы начать стейкинг в TON, сначала отправьте небольшую транзакцию на свой же адрес — это активирует ваш кошелёк. До 0.2 TON может потребоваться сверх сетевой комиссии для завершения транзакции. Неиспользованная часть будет возвращена. @@ -1311,7 +1318,9 @@ Переименование кошелька Разблокировать все Разблокировать все с %s + Активация аккаунта ПИН не принят. Попробуйте ещё раз или введите другой код. + Что выбрать? Блокчейн недоступен. Попробуйте позже. Отсканируйте карту или кольцо Этот кошелек уже был активирован ранее.\nЕсли это сделали не вы, свяжитесь со службой поддержки.\nTangem никогда не продает кошелек вместе с предустановленным кодом доступа. @@ -1577,6 +1586,7 @@ Чтобы не платить повышенную комиссию при следующем пополнении кошелька, уменьшите сумму на %s XTZ При активном режиме доходности все будущие депозиты на этот адрес будут направляться в Aave. Вы по-прежнему можете свободно управлять своими средствами. Ваш %s внесён в Aave + Отправка %1$s %2$s в Aave находится в ожидании Выдать разрешение Разрешение для вашего токена было отозвано. Выдайте его снова, чтобы продолжить работу сервиса. Необходимо разрешение @@ -1586,21 +1596,25 @@ Ваши средства в данный момент размещены в протоколе Aave, но вы можете воспользоваться ими в любое время. Ваш %s внесён в Aave Невозможно загрузить график - Полученная сумма, %1$s %2$s, не была зачислена на Aave. + Отправка %1$s %2$s на Aave ожидает выполнения. APY %1$s%% Доступно Текущий APY + При пополнении в режиме доходности с баланса будет удержана комиссия сети, не превышающая %1$s. + Сейчас комиссия сети слишком высока для выполнения операции. Средства будут отправлены, как только она снизится до %1$s или ниже. Мои средства Ваши %1$s теперь внесён в Aave и приносит проценты. У вас есть токен %2$s, который отражает ваш баланс и со временем увеличивается. При пополнении средства автоматически направляются в Aave для получения процентов за вычетом комиссии за транзакцию. + Режим доходности Итоговый доход Переводы в Aave Изучите Aave Это текущая комиссия в сети %s. Текущая комиссия Все следующие пополнения %s автоматически поступят в Aave с удержанием комиссии за транзакцию. + Примерная комиссия сети в размере %1$s (%2$s) будет удерживаться с каждого будущего пополнения и не превысит ваш лимит %3$s (%4$s). Если комиссия сети превысит максимальный лимит, транзакция не будет выполнена до тех пор, пока комиссия не снизится. Вы сможете изменить этот лимит позже. Максимальная комиссия - Это минимальный депозит, который можно отправить в Aave. Чтобы депозиты оставались прибыльными, мы не обрабатываем их, когда комиссия сети превышает %1$s%2$sот суммы. + Минимальная сумма рассчитывается исходя из текущей комиссии сети, чтобы она не превышала 4%% от суммы пополнения, что составляет минимум %1$s (%2$s). Минимальный депозит Политика комиссий Tangem взимает комиссию за обслуживание в размере 15% от полученного дохода. @@ -1615,11 +1629,11 @@ Отправляйте, обменивайте или продавайте свои средства мгновенно, когда захотите. Мгновенный вывод средств Как это работает? - Aave — это децентрализованный протокол, управляющий активами на сумму более 61 миллиарда долларов США. + Aave — децентрализованный протокол, управляющий активами на сумму более $61 млрд. Децентрализованный и некастодиальный Используя сервис, вы соглашаетесь с условиями провайдера %1$s и %2$s Подключить Aave - Aave %1$s%%• Ставка с плавающим процентом + Aave %1$s%% • Плавающая ставка Aave Сред. %s Доходность за прошлый год @@ -1636,11 +1650,15 @@ Завершение режима доходности Выключив эту функцию, вы выведёте средства из Aave, получите их обратно в %s в своем кошельке и перестанете зарабатывать награды. Комиссия сети будет вычтена из суммы вашего вывода. + Завершить режим доходности + Внесение Годовая доходность (APY) APY + Проценты начисляются автоматически. Проценты начисляются автоматически. Режим доходности Отправка ваших средств + Режим доходности Автоматически Внесите немного %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 936f193cce..25532a4207 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -172,6 +172,7 @@ Швидкість та комісія Завершити Синхронізувати адреси + Почати Перейти до провайдера Перейти до токену Зrozуміло @@ -370,6 +371,7 @@ Провайдер Найкращий курс Список попереджень FCA + Найкращий вибір Доступно до %s Доступно від %s Недоступно для цієї пари @@ -760,6 +762,7 @@ За балансом Сортування токенів Список + Підтримка %s Більше Ви можете ввімкнути сповіщення у налаштуваннях Увімкнути пізніше @@ -1017,6 +1020,7 @@ Реінвестуйте зароблені винагороди у суму стейкінгу, щоб збільшити потенційний прибуток. Рестейк дозволяє вам перемістити ваші кошти від одного валідатора до іншого без необхідності виводити кошти зі стейкінгу Ви збираєтеся застейкати весь свій баланс. Ми рекомендуємо залишити невелику суму, щоб покрити комісію мережі за зняття коштів або отримання винагороди. + Щоб розпочати стейкінг, спочатку активуйте свій TON-акаунт. Щоб почати стейкінг в TON, спочатку здійсніть вихідну транзакцію на будь-яку суму — це активує ваш гаманець. До 0.2 TON може знадобитися додатково мережевої комісії для завершення транзакції. Невикористана частина буде повернута. Для виконання операції потребується додатково 0.2 TON, крім мережевої комісії. Будь ласка, поповніть баланс. @@ -1446,7 +1450,7 @@ Наразі ваші кошти розміщені у протоколі Aave, але ви можете керувати ними в будь-який час. Ваш %s внесений у Aave Неможливо завантажити графік - Отримана сума, %1$s %2$s не була зарахована на Aave. + Поповнення %1$s %2$s на Aave очікує виконання. APY %1$s%% Доступно Поточний APY @@ -1464,7 +1468,7 @@ Приблизна мережева плата в розмірі %1$s (%2$s) буде вираховуватися з кожного наступного поповнення, і вона не перевищуватиме ваш ліміт %3$s (%4$s). Якщо мережеві комісії перевищують максимальну комісію, транзакція не пройде, поки вони не знизяться. Ви можете змінити цей ліміт пізніше. Максимальна комісія - Це найменший депозит, який можна надіслати в Aave. Щоб депозити залишалися прибутковими, ми не обробляємо їх, коли комісія мережі перевищує %1$s%2$s від суми. + Мінімальна сума розраховується з урахуванням поточного збору мережі, щоб вона не перевищувала 4%%, що становить мінімум %1$s (%2$s). Мінімальне поповнення Політика комісій Tangem комісію за обслуговування у розмірі 15% від отриманого доходу. @@ -1479,11 +1483,11 @@ Відправляйте, обмінюйте або продавайте свої кошти миттєво, в будь-який час. Миттєвий вивід коштів Як це працює? - Aave — це децентралізований протокол, який управляє активами загальною вартістю понад 61 мільярд доларів. + Aave — децентралізований протокол, що керує активами на понад $61 млрд. Децентралізований та некастодіальний Використовуючи сервіс, ви погоджуєтесь з умовами провайдера %1$s та %2$s Підключити Aave - Aave %1$s%% • Ставка з плаваючим відсотком + Aave %1$s%% • Плаваюча ставка Аave Серед. %s Дохідність за минулий рік @@ -1497,14 +1501,17 @@ Ваші наступні поповнення рахунку автоматично надходитимуть до Aave. Активний На паузі - Завершити заробіток + Вимкнення режиму прибутковості Вимкнення цієї опції виведе ваші активи з Aave, отримаєте їх назад у %s у вашому гаманці і зупинить накопичення нагород. Комісія мережі буде вирахувана з суми, яку ви знімаєте. + Завершити заробіток Річна прибутковість (APY) APY + Відсотки нараховуються автоматично. Відсотки нараховуються автоматично Режим дохідності Відправка ваших коштів + Режим прибутковості Автоматично Внесіть трохи %1$s %2$s, щоб покрити комісію мережі за транзакції. Неможливо покрити комісію в %s diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6591e77f82..2dcc96ef01 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -160,7 +160,7 @@ %d token %d tokens - Please reset the next device to continue. + Please reset the next device to continue. Wallet reset All Tangem devices have been reset. You can now continue upgrading your wallet. Upgrade again @@ -539,6 +539,13 @@ To continue, grant %1s smart contracts permission to use your %2s Give Permission Unlimited + Your backup is created using 2 or 3 Tangem cards. Keep them in separate safe places to protect against loss or damage — no seed phrase needed. + Backup with Multiple Cards + Add Tangem Wallet + Your private key is generated directly inside the Tangem card and never leaves it. + Key Generation + All cryptographic operations happen inside the secure chip, certified against cloning and physical tampering. + Hardware-Level Security Add Existing Wallet Create New Wallet Order Tangem @@ -580,6 +587,21 @@ Import existing wallet This recovery phrase has already been imported Mobile Wallet + Forget wallet + This wallet will be permanently removed from your device + Are you sure you want to do this? + Forget wallet + Go to backup + View backup + Forget wallet + Forget Anyway + A backup for this wallet exists. Review it before forgetting to make sure you can recover later. + If you forget this wallet without a backup, you’ll permanently lose access to your funds. + Forget this wallet? + I understand that if I haven\'t backed up my wallet before removing it, I may lose access to it. + I understand that removing my wallet does not delete it—but simply removes it from my device. + No seed phrase is needed anymore — your Tangem card or ring becomes your secure backup. + Backup with Tangem Can’t upgrade. A wallet already exists on this device. Pick another device. This one can’t be used for the upgrade. An error occurred during the operation. @@ -1159,11 +1181,12 @@ Name Put your token to work A network fee is a small payment required to process and confirm your transaction on the blockchain. - To start staking, your TON account must be activated with a self-transaction of 1 TON. The funds stay in your wallet — this step only enables your account for staking. + 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 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. + APR %1$s%% Claim unstaked Stake account fee A staking account is a special account where staked SOL tokens are stored. It is created when you delegate your tokens to a validator to participate in transaction validation and receive rewards. A small fee is charged for creating the staking account, which is returned after the staking is completed. @@ -1227,7 +1250,7 @@ Reinvests your earned rewards in your staked amount, increasing potential earnings. Restake lets you move your funds from one validator to another without the need to unstake If you stake your entire balance, you’ll need to pay a network fee when you unstake. We recommend leaving a small amount in your wallet to cover network fees. - To start staking, your TON account must be activated with a self-transaction of 1 TON. The funds stay in your wallet — this step only enables your account for staking. + To begin staking, you need to activate your TON account first. Account activation To start staking in TON, first send a small transaction to your own address — this will activate your wallet. Up to 0.2 TON may be required in addition to the network fee to complete the transaction. Any unused amount will be refunded. @@ -1348,6 +1371,8 @@ Pending The bank rejected this transaction request. This fee goes to cover the cost of handling your transfer. + Keep using your money. You can freeze anytime. + Unfreeze your card? Withdrawal Add funds Top-up options @@ -1372,6 +1397,7 @@ Reveal Swap any asset in your portfolio for card Card details + Unfreeze Card Create account and issue a card Tangem Pay View Status @@ -1856,12 +1882,12 @@ Your funds are currently supplied to the Aave protocol, but you can manage them at any time. Your %s is deposited in Aave Unable to load chart... - The amount received, %1$s %2$s was not supplied to Aave. + Supplying %1$s %2$s to Aave is pending. APY %1$s%% Available Current APY When topping up for lending, a network fee not exceeding %1$s will be deducted from the balance. - The network fee is currently too high to execute lending. Funds will be supplied once it drops to %1$s or below. + The network fee is currently too high to execute lending. Funds will be supplied once it drops to %1$s or below. My funds Your %1$s is now deployed to Aave and earning yield. You hold %2$s tokens, which represent your balance and accrue yield automatically. When you top up, funds are added to Aave to earn more yield, minus fees. Yield mode @@ -1874,7 +1900,7 @@ An approximate network fee of %1$s (%2$s) will be deducted from each future top-up, and it won’t exceed your %3$s (%4$s) limit. If network fees rise above maximum fee, the transaction won’t go through until they decrease. You can change this limit later. Maximum fee - The minimum amount is calculated from the current network fee to ensure it does not exceed 4%%, which makes the minimum %1$s (%2$s). + The minimum amount is calculated based on the current network fee so that it does not exceed 4%% of the top-up amount, which makes the minimum %1$s (%2$s). Minimum top-up Fee policy Tangem also takes a 15% service fee on yield earned. @@ -1907,13 +1933,14 @@ Your next top-ups will be automatically supplied to Aave. Active Paused - Disable yield mode + Disabling Yield Mode Turning this off will withdraw your assets from Aave, convert them back to %s in your wallet, and stop yield accrual. The network fee will be deducted from the amount you withdraw. + Disable yield mode Supply Supply APY APY - Interest accrues automatically + Interest accrues automatically. Interest accrues automatically Yield mode Processing your deposit diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt b/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt index 860dba2c51..951e6a5042 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt @@ -75,6 +75,7 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) { .padding(horizontal = 8.dp, vertical = 4.dp), ) { Text( + modifier = Modifier.weight(1.0f, fill = false), text = text.resolveReference(), style = TangemTheme.typography.caption1, color = textColor, @@ -125,7 +126,9 @@ private fun LabelPreview() { ) Label( state = LabelUM( - text = TextReference.Str("Regular Label"), + text = TextReference.Str( + "Regular long long long long long long long long long long long long Label", + ), style = LabelStyle.REGULAR, icon = R.drawable.ic_information_24, ), diff --git a/core/ui/src/main/res/drawable/img_visa_waitlist_promo.webp b/core/ui/src/main/res/drawable/img_visa_waitlist_promo.webp new file mode 100644 index 0000000000..6da4590058 Binary files /dev/null and b/core/ui/src/main/res/drawable/img_visa_waitlist_promo.webp differ diff --git a/domain/card/src/test/java/com/tangem/domain/card/TwinsHelperTest.kt b/data/account/src/test/java/com/tangem/domain/card/common/TwinsHelperTest.kt similarity index 86% rename from domain/card/src/test/java/com/tangem/domain/card/TwinsHelperTest.kt rename to data/account/src/test/java/com/tangem/domain/card/common/TwinsHelperTest.kt index 76e2291eac..a45be46828 100644 --- a/domain/card/src/test/java/com/tangem/domain/card/TwinsHelperTest.kt +++ b/data/account/src/test/java/com/tangem/domain/card/common/TwinsHelperTest.kt @@ -1,9 +1,8 @@ -package com.tangem.domain.card +package com.tangem.domain.card.common -import com.tangem.domain.common.TwinsHelper -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue +import org.junit.Assert import org.junit.Test +import org.junit.jupiter.api.Assertions.* class TwinsHelperTest { @@ -19,7 +18,7 @@ class TwinsHelperTest { @Test fun `twins compatibility pack 1 success`() { - assertTrue(TwinsHelper.isTwinsCompatible(pack1Twins[0], pack1Twins[1])) + Assert.assertTrue(TwinsHelper.isTwinsCompatible(pack1Twins[0], pack1Twins[1])) assertTrue(TwinsHelper.isTwinsCompatible(pack1Twins[1], pack1Twins[0])) } 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 09b1d25af8..a0dada9a61 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 @@ -18,13 +18,13 @@ import com.tangem.domain.promo.models.PromoId import com.tangem.domain.promo.models.StoryContent import com.tangem.feature.referral.domain.ReferralRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.runCatching import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull -import com.tangem.utils.coroutines.runCatching internal class DefaultPromoRepository( private val tangemApi: TangemTechApi, @@ -49,6 +49,11 @@ internal class DefaultPromoRepository( PromoId.Sepa -> { val isActive = getSepaPromoBanner()?.isActive ?: false + isActive && shouldShow + } + PromoId.VisaPresale -> { + val isActive = getVisaPromoBanner()?.isActive ?: false + isActive && shouldShow } } @@ -59,6 +64,7 @@ internal class DefaultPromoRepository( return when (promoId) { PromoId.Referral -> flowOf(false) PromoId.Sepa -> flowOf(false) + PromoId.VisaPresale -> flowOf(false) } } @@ -134,8 +140,17 @@ internal class DefaultPromoRepository( }.getOrNull() } + private suspend fun getVisaPromoBanner(): PromoBanner? { + return runCatching(dispatchers.io) { + promoBannerConverter.convert( + tangemApi.getPromoBanner(VISA_NAME).getOrThrow(), + ) + }.getOrNull() + } + private companion object { const val SEPA_NAME = "sepa" + const val VISA_NAME = "visa-waitlist" const val STORIES_LOAD_DELAY = 1000L } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index e41afb0cdc..58bc11680b 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -67,20 +67,6 @@ internal object TokensDataModule { ) } - @Provides - @Singleton - fun providePolkadotAccountHealthCheckRepository( - walletManagersFacade: WalletManagersFacade, - appPreferencesStore: AppPreferencesStore, - dispatchers: CoroutineDispatcherProvider, - ): PolkadotAccountHealthCheckRepository { - return DefaultPolkadotAccountHealthCheckRepository( - walletManagersFacade = walletManagersFacade, - appPreferencesStore = appPreferencesStore, - dispatchers = dispatchers, - ) - } - @Provides @Singleton fun provideTokenReceiveWarningsViewedRepository( diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultPolkadotAccountHealthCheckRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultPolkadotAccountHealthCheckRepository.kt deleted file mode 100644 index 2e2a8ae876..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultPolkadotAccountHealthCheckRepository.kt +++ /dev/null @@ -1,179 +0,0 @@ -package com.tangem.data.tokens.repository - -import androidx.datastore.preferences.core.Preferences -import com.tangem.blockchain.blockchains.polkadot.AccountCheckProvider -import com.tangem.blockchain.blockchains.polkadot.network.accounthealthcheck.ExtrinsicListItemResponse -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchainsdk.utils.toBlockchain -import com.tangem.datasource.local.preferences.AppPreferencesStore -import com.tangem.datasource.local.preferences.PreferencesKeys.POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY -import com.tangem.datasource.local.preferences.PreferencesKeys.POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY -import com.tangem.datasource.local.preferences.PreferencesKeys.POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY -import com.tangem.datasource.local.preferences.utils.getObjectMapSync -import com.tangem.datasource.local.preferences.utils.getObjectSetSync -import com.tangem.domain.models.network.Network -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository -import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext -import timber.log.Timber -import java.util.concurrent.ConcurrentHashMap - -internal class DefaultPolkadotAccountHealthCheckRepository( - private val walletManagersFacade: WalletManagersFacade, - private val appPreferencesStore: AppPreferencesStore, - private val dispatchers: CoroutineDispatcherProvider, -) : PolkadotAccountHealthCheckRepository { - - private val hasImmortalTransaction = MutableSharedFlow>() - private val hasResetTransaction = MutableSharedFlow>() - - private val mutex = Mutex() - private val mutexes = ConcurrentHashMap() - - override suspend fun runCheck(userWalletId: UserWalletId, network: Network) { - // Run Polkadot account health check - if (network.toBlockchain() != Blockchain.Polkadot) return - - val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network) - val address = requireNotNull(walletManager?.wallet?.address) { - Timber.e("Address is null") - return - } - val accountCheckProvider = requireNotNull(walletManager as? AccountCheckProvider) { - Timber.e("Unable to cast wallet manager to AccountCheckProvider") - return - } - - // use a separate mutexForKey for each key to avoid multiple calls block() to the same key - // also used mutex to safe create mutexForKey, otherwise it can lead to multiple calls for the same key - val mutexForKey = mutex.withLock { mutexes.getOrPut(address) { Mutex() } } - mutexForKey.withLock { - withContext(dispatchers.io) { - checkHasReset(accountCheckProvider, address) - checkHasImmortal(accountCheckProvider, address) - } - } - } - - override fun subscribeToHasImmortalResults() = hasImmortalTransaction.asSharedFlow() - - override fun subscribeToHasResetResults() = hasResetTransaction.asSharedFlow() - - private suspend fun checkHasReset(polkadotManager: AccountCheckProvider, address: String) { - val checkedAddresses = appPreferencesStore.getObjectSetSync(POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY) - if (checkedAddresses.contains(address)) return - runCatching { - val accountInfo = requireNotNull(polkadotManager.getAccountInfo().account) { - Timber.e("Account info is null") - } - val nonce = accountInfo.nonce - val extrinsicCount = accountInfo.countExtrinsic - - // Account was reset - if (nonce != null && extrinsicCount != null) { - val hasReset = nonce < extrinsicCount - updateChecked(address, POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY) - hasResetTransaction.emit(address to hasReset) - } - }.onFailure { - if ((it as? BlockchainSdkError.CustomError)?.customMessage == ACCOUNT_NOT_FOUND) { - updateChecked(address, POLKADOT_HEALTH_CHECKED_RESET_ACCOUNTS_KEY) - } - } - } - - private suspend fun checkHasImmortal(accountCheckerProvider: AccountCheckProvider, address: String) { - val checkedAddresses = - appPreferencesStore.getObjectSetSync(POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY) - if (checkedAddresses.contains(address)) return - - runCatching { - do { - // Getting batch of extrinsics to check - val lastChecked = appPreferencesStore.getObjectMapSync(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY) - val lastExtrinsic = lastChecked[address] - val extrinsicListResult = accountCheckerProvider.getExtrinsicList(afterExtrinsicId = lastExtrinsic) - extrinsicListResult.extrinsic - ?.forEach { tx -> - // Checking extrinsic one by one - if (checkTx(tx, address, accountCheckerProvider)) return - } - } while (!extrinsicListResult.extrinsic.isNullOrEmpty()) - - // We checked all transactions up to current moment and did not found an `immortal` transaction - hasImmortalTransaction.emit(address to false) - updateChecked(address, POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY) - clearLastCheckedTransaction(address) - } - } - - private suspend fun checkTx( - tx: ExtrinsicListItemResponse, - address: String, - accountCheckerProvider: AccountCheckProvider, - ): Boolean { - val hash = tx.hash - val id = tx.id - if (hash != null && id != null) { - val details = accountCheckerProvider.getExtrinsicDetail(hash) - - // We found an `immortal` transaction - if (details.lifetime == null) { - hasImmortalTransaction.emit(address to true) - updateChecked(address, POLKADOT_HEALTH_CHECKED_IMMUTABLE_ACCOUNTS_KEY) - clearLastCheckedTransaction(address) - return true - } - - // Saving last checked transaction - updateLastCheckedTransaction(address, id) - } - return false - } - - private suspend fun updateLastCheckedTransaction(address: String, txId: Long) { - appPreferencesStore.editData { - val savedList = it.getObjectMap(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY) - val updatedList = savedList.toMutableMap() - updatedList[address] = txId - it.setObjectMap(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY, updatedList) - } - } - - private suspend fun clearLastCheckedTransaction(address: String) { - appPreferencesStore.editData { mutablePreferences -> - val savedList = mutablePreferences.getObjectMap(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY) - val updatedList = savedList.toMutableMap() - updatedList.remove(address) - mutablePreferences.setObjectMap(POLKADOT_HEALTH_CHECK_LAST_INDEXED_TX_KEY, updatedList) - } - } - - private suspend fun updateChecked(address: String, key: Preferences.Key) { - appPreferencesStore.editData { mutablePreferences -> - val savedList = mutablePreferences.getObjectSet(key) - val updatedList = savedList?.toMutableSet() ?: mutableSetOf() - updatedList.add(address) - - if (updatedList.isNotEmpty()) { - mutablePreferences.setObjectSet( - key, - updatedList.toSet(), - ) - } else { - mutablePreferences.remove(key) - } - } - } - - private companion object { - const val ACCOUNT_NOT_FOUND = "Record Not Found" - } -} \ No newline at end of file diff --git a/data/yield-supply/src/main/java/com/tangem/data/yield/supply/converters/YieldMarketTokenConverter.kt b/data/yield-supply/src/main/java/com/tangem/data/yield/supply/converters/YieldMarketTokenConverter.kt index c66fb73edd..61d15fb639 100644 --- a/data/yield-supply/src/main/java/com/tangem/data/yield/supply/converters/YieldMarketTokenConverter.kt +++ b/data/yield-supply/src/main/java/com/tangem/data/yield/supply/converters/YieldMarketTokenConverter.kt @@ -12,8 +12,8 @@ internal object YieldMarketTokenConverter : Converter cb62 - * cb64 <-> cb65 - * - */ - private const val FIRST_CARD_FIRST_SERIES = "CB61" - private const val SECOND_CARD_FIRST_SERIES = "CB62" - private const val FIRST_CARD_SECOND_SERIES = "CB64" - private const val SECOND_CARD_SECOND_SERIES = "CB65" - - @Suppress("MagicNumber") - fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { - if (issuerData.size < 65 || cardWalletPublicKey == null) return false - - val publicKey = issuerData.sliceArray(0 until 65) - val signedKey = issuerData.sliceArray(65 until issuerData.size) - return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey) - } - - fun getTwinCardNumber(cardId: String): TwinCardNumber? { - val isFirstCard = cardId.startsWith(FIRST_CARD_FIRST_SERIES) || - cardId.startsWith(FIRST_CARD_SECOND_SERIES) - if (isFirstCard) return TwinCardNumber.First - - val isSecondCard = cardId.startsWith(SECOND_CARD_FIRST_SERIES) || - cardId.startsWith(SECOND_CARD_SECOND_SERIES) - if (isSecondCard) return TwinCardNumber.Second - return null - } - - @Suppress("MagicNumber") - fun getTwinCardIdForUser(cardId: String): String { - if (cardId.length < 16) return cardId - - val twinCardId = cardId.substring(11..14) - val twinCardNumber = getTwinCardNumber(cardId)?.number ?: 1 - return "$twinCardId #$twinCardNumber" - } - - /** - * Twins compatibility - * cb61 <-> cb62 - * cb64 <-> cb65 - */ - fun isTwinsCompatible(firstCardId: String, secondCardId: String): Boolean { - return secondCardId.startsWith(getCompatibleTwinCardId(firstCardId)) - } - - private fun getCompatibleTwinCardId(cardId: String): String { - return when { - cardId.startsWith(FIRST_CARD_FIRST_SERIES) -> SECOND_CARD_FIRST_SERIES - cardId.startsWith(FIRST_CARD_SECOND_SERIES) -> SECOND_CARD_SECOND_SERIES - cardId.startsWith(SECOND_CARD_FIRST_SERIES) -> FIRST_CARD_FIRST_SERIES - cardId.startsWith(SECOND_CARD_SECOND_SERIES) -> FIRST_CARD_SECOND_SERIES - else -> { - "unknown" - } - } - } -} - -enum class TwinCardNumber(val number: Int) { - First(1), Second(2); - - fun pairNumber(): TwinCardNumber = when (this) { - First -> Second - Second -> First - } -} - -@Deprecated("Use ScanResponse.isTangemTwin") -fun CardDTO.isTangemTwin(): Boolean { - return TwinsHelper.getTwinCardNumber(cardId) != null -} - -fun CardDTO.getTwinCardNumber(): TwinCardNumber? { - return TwinsHelper.getTwinCardNumber(this.cardId) -} - -fun CardDTO.getTwinCardIdForUser(): String { - return TwinsHelper.getTwinCardIdForUser(this.cardId) -} \ No newline at end of file diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index fa98ae01b7..34e368afbc 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -14,6 +14,10 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.derivations.DerivationsRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @Deprecated("Use SaveCryptoCurrenciesUseCase") @Suppress("LongParameterList") @@ -26,6 +30,7 @@ class SaveManagedTokensUseCase( private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, private val stakingIdFactory: StakingIdFactory, + private val parallelUpdatingScope: CoroutineScope, ) { suspend operator fun invoke( @@ -54,11 +59,23 @@ class SaveManagedTokensUseCase( currencies = addingCurrencies, ) - refreshUpdatedNetworks(userWalletId = userWalletId, addedCurrencies = savedCurrencies) - - refreshUpdatedYieldBalances(userWalletId = userWalletId, addedCurrencies = savedCurrencies) - - refreshUpdatedQuotes(addedCurrencies = savedCurrencies) + parallelUpdatingScope.launch { + withContext(NonCancellable) { + launch { + refreshUpdatedNetworks( + userWalletId = userWalletId, + addedCurrencies = savedCurrencies, + ) + } + launch { + refreshUpdatedYieldBalances( + userWalletId = userWalletId, + addedCurrencies = savedCurrencies, + ) + } + launch { refreshUpdatedQuotes(addedCurrencies = savedCurrencies) } + } + } } } 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 4e5c8aa6d6..70da7009cd 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 @@ -11,6 +11,10 @@ import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.derivations.DerivationsRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** * Use case for saving tokens from Markets @@ -31,6 +35,7 @@ class SaveMarketTokensUseCase( private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, private val stakingIdFactory: StakingIdFactory, + private val parallelUpdatingScope: CoroutineScope, ) { suspend operator fun invoke( @@ -70,11 +75,13 @@ class SaveMarketTokensUseCase( currencies = addedCurrencies, ) - refreshUpdatedNetworks(userWalletId, savedCurrencies) - - refreshUpdatedYieldBalances(userWalletId, savedCurrencies) - - refreshUpdatedQuotes(savedCurrencies) + parallelUpdatingScope.launch { + withContext(NonCancellable) { + launch { refreshUpdatedNetworks(userWalletId, savedCurrencies) } + launch { refreshUpdatedYieldBalances(userWalletId, savedCurrencies) } + launch { refreshUpdatedQuotes(savedCurrencies) } + } + } } } 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 7605016350..452445b991 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 @@ -28,4 +28,5 @@ data class PromoBanner( enum class PromoId { Referral, Sepa, + VisaPresale, } \ 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 0fd7070d88..396e1d8e90 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 @@ -29,7 +29,9 @@ class ShouldShowPromoWalletUseCase( if (!isReady) return false return when (promoId) { - PromoId.Referral -> true + PromoId.Referral, + PromoId.VisaPresale, + -> true PromoId.Sepa -> { val walletFirstUsageDate = settingsRepository.getWalletFirstUsageDate() if (walletFirstUsageDate == 0L) return false @@ -39,6 +41,7 @@ class ShouldShowPromoWalletUseCase( } } } + suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowWalletPromo(promoId) private companion object { diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt b/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt index ff38b75b71..43e2488895 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt @@ -1,8 +1,10 @@ package com.tangem.domain.staking.model import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.blockchainsdk.utils.toMigratedCoinId import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network /** * Represents a staking integration identifier. @@ -143,6 +145,9 @@ sealed interface StakingIntegrationID { } } +val Network.isStakingSupported: Boolean + get() = this.toBlockchain().isStakingSupported + /** * Extension property to check if staking is supported for a blockchain. * diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt index 5b277e5aed..3b50f3a63c 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt @@ -1,8 +1,5 @@ package com.tangem.domain.staking.usecase -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.repositories.StakingRepository import kotlinx.coroutines.flow.Flow @@ -21,22 +18,10 @@ class StakingApyFlowUseCase(private val stakingRepository: StakingRepository) { return stakingRepository.getEnabledYields() .map { yields -> yields.associate { yield -> - val key = composeKey(yield) + val key = "${yield.token.coinGeckoId}_${yield.token.symbol}" val apy = yield.validators key to apy } } } - - // so that cause for api.stakek.it coinGeckoId on BNB Smart chain different how we receive our backendId - // coinGeckoId - binance - // our backendId BNB Smart Chain - binance-smart-chain - // our backendId BNB Beacon Chain - binance - private fun composeKey(yield: Yield): String { - return if (yield.token.symbol == StakingIntegrationID.Coin.BSC.blockchain.currency) { - "${Blockchain.BSC.toNetworkId()}_${yield.token.symbol}" - } else { - "${yield.token.coinGeckoId}_${yield.token.symbol}" - } - } } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt similarity index 68% rename from domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt rename to domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt index f460d229ff..1dcd4490f4 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt @@ -3,14 +3,14 @@ package com.tangem.domain.tokens.model.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam -sealed class TokenSwapPromoAnalyticsEvent( +sealed class PromoAnalyticsEvent( event: String, params: Map = mapOf(), ) : AnalyticsEvent(category = "Promotion", event = event, params = params) { class NoticePromotionBanner( source: AnalyticsParam.ScreensSources, program: Program, - ) : TokenSwapPromoAnalyticsEvent( + ) : PromoAnalyticsEvent( event = "Notice - Promotion Banner", params = mapOf( AnalyticsParam.SOURCE to source.value, @@ -22,7 +22,7 @@ sealed class TokenSwapPromoAnalyticsEvent( source: AnalyticsParam.ScreensSources, program: Program, action: BannerAction, - ) : TokenSwapPromoAnalyticsEvent( + ) : PromoAnalyticsEvent( event = "Promo Banner Clicked", params = mapOf( AnalyticsParam.SOURCE to source.value, @@ -36,6 +36,24 @@ sealed class TokenSwapPromoAnalyticsEvent( } } + // region visa waitlist promo + data object VisaWaitlistPromo : PromoAnalyticsEvent(event = "Visa Waitlist") + + data object VisaWaitlistPromoJoin : PromoAnalyticsEvent( + event = "Button - Join Now", + params = mapOf( + "Program Name" to "Visa Waitlist", + ), + ) + + data object VisaWaitlistPromoDismiss : PromoAnalyticsEvent( + event = "Button - Close", + params = mapOf( + "Program Name" to "Visa Waitlist", + ), + ) + //endregion + // Use it on new promo action enum class Program(val programName: String) { Empty("Empty"), diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt index 3365ce6d87..5c0c30da0d 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt @@ -184,6 +184,7 @@ sealed class TokenScreenAnalyticsEvent( private const val LOADING = "Loading" private const val ASSET_REQUIREMENT = "AssetRequirement" private const val TRUSTLINE_REQUIREMENT = "TrustlineRequirement" + private const val APPROVAL_REQUIRED = "Approval Required" @Suppress("CyclomaticComplexMethod") fun ScenarioUnavailabilityReason.toReasonAnalyticsText(): String { @@ -192,8 +193,8 @@ sealed class TokenScreenAnalyticsEvent( is ScenarioUnavailabilityReason.NotExchangeable, is ScenarioUnavailabilityReason.NotSupportedBySellService, is ScenarioUnavailabilityReason.StakingUnavailable, - ScenarioUnavailabilityReason.YieldSupplyApprovalRequired, -> UNAVAILABLE + is ScenarioUnavailabilityReason.YieldSupplyApprovalRequired -> APPROVAL_REQUIRED ScenarioUnavailabilityReason.UnassociatedAsset -> ASSET_REQUIREMENT ScenarioUnavailabilityReason.TrustlineRequired -> TRUSTLINE_REQUIREMENT is ScenarioUnavailabilityReason.EmptyBalance -> EMPTY diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPolkadotCheckHasImmortalUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPolkadotCheckHasImmortalUseCase.kt deleted file mode 100644 index 527f6e1703..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPolkadotCheckHasImmortalUseCase.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.domain.tokens - -import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository -import kotlinx.coroutines.flow.Flow - -class GetPolkadotCheckHasImmortalUseCase( - private val polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository, -) { - operator fun invoke(): Flow> = - polkadotAccountHealthCheckRepository.subscribeToHasImmortalResults() -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPolkadotCheckHasResetUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPolkadotCheckHasResetUseCase.kt deleted file mode 100644 index 33ce7e960e..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPolkadotCheckHasResetUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.domain.tokens - -import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository -import kotlinx.coroutines.flow.Flow - -class GetPolkadotCheckHasResetUseCase( - private val polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository, -) { - - operator fun invoke(): Flow> = - polkadotAccountHealthCheckRepository.subscribeToHasResetResults() -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RunPolkadotAccountHealthCheckUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RunPolkadotAccountHealthCheckUseCase.kt deleted file mode 100644 index 9f665e442e..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RunPolkadotAccountHealthCheckUseCase.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.domain.tokens - -import arrow.core.Either -import com.tangem.domain.models.network.Network -import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository -import com.tangem.domain.models.wallet.UserWalletId - -class RunPolkadotAccountHealthCheckUseCase( - private val polkadotAccountHealthCheckRepository: PolkadotAccountHealthCheckRepository, -) { - - suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either = Either.catch { - polkadotAccountHealthCheckRepository.runCheck(userWalletId, network) - } -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PolkadotAccountHealthCheckRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PolkadotAccountHealthCheckRepository.kt deleted file mode 100644 index 88ca5927ae..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PolkadotAccountHealthCheckRepository.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.tangem.domain.tokens.repository - -import com.tangem.domain.models.network.Network -import com.tangem.domain.models.wallet.UserWalletId -import kotlinx.coroutines.flow.Flow - -interface PolkadotAccountHealthCheckRepository { - - suspend fun runCheck(userWalletId: UserWalletId, network: Network) - - fun subscribeToHasImmortalResults(): Flow> - - fun subscribeToHasResetResults(): Flow> -} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt index dbee9b5cfb..5e7d46dafe 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt @@ -2,8 +2,8 @@ package com.tangem.domain.wallets.usecase import com.tangem.common.card.FirmwareVersion import com.tangem.common.services.Result -import com.tangem.domain.common.TwinCardNumber -import com.tangem.domain.common.TwinsHelper +import com.tangem.domain.card.common.TwinCardNumber +import com.tangem.domain.card.common.TwinsHelper import com.tangem.domain.models.ArtworkModel import com.tangem.domain.wallets.models.Artwork import com.tangem.operations.attestation.ArtworkSize diff --git a/domain/yield-supply/models/src/main/java/com/tangem/domain/yield/supply/models/YieldMarketToken.kt b/domain/yield-supply/models/src/main/java/com/tangem/domain/yield/supply/models/YieldMarketToken.kt index 7d379d4af6..26ec2d1ac9 100644 --- a/domain/yield-supply/models/src/main/java/com/tangem/domain/yield/supply/models/YieldMarketToken.kt +++ b/domain/yield-supply/models/src/main/java/com/tangem/domain/yield/supply/models/YieldMarketToken.kt @@ -12,8 +12,8 @@ data class YieldMarketToken( val chainId: Int, val apy: SerializedBigDecimal, val isActive: Boolean, - val maxFeeNative: String, - val maxFeeUSD: String, + val maxFeeNative: SerializedBigDecimal, + val maxFeeUSD: SerializedBigDecimal, val backendId: String? = null, ) { diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/FeeExtensions.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/FeeExtensions.kt index 4032d39149..799506d8f2 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/FeeExtensions.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/FeeExtensions.kt @@ -3,6 +3,10 @@ package com.tangem.domain.yield.supply import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.models.currency.CryptoCurrency import java.math.BigInteger +import java.math.RoundingMode + +private val HUNDRED_PERCENT = 100.toBigInteger() // base 100% +val INCREASE_GAS_LIMIT_FOR_SUPPLY = 112.toBigInteger() // 12% increase fun Fee.fixFee(cryptoCurrency: CryptoCurrency, gasLimit: BigInteger): Fee = when (this) { is Fee.Ethereum.Legacy -> copy( @@ -20,4 +24,24 @@ fun Fee.fixFee(cryptoCurrency: CryptoCurrency, gasLimit: BigInteger): Fee = when ), ) else -> this +} + +/** + * Increase gasLimit for Fee.Ethereum + */ +fun Fee.increaseGasLimitBy(percent: BigInteger): Fee { + if (this !is Fee.Ethereum) return this + val gasLimit = this.gasLimit + val increasedGasPrice = this.amount.value?.movePointRight(this.amount.decimals) + ?.divide(gasLimit.toBigDecimal(), RoundingMode.HALF_UP) + val increasedGasLimit = gasLimit + .multiply(percent) + .divide(HUNDRED_PERCENT) + val increasedAmount = this.amount.copy( + value = increasedGasLimit.toBigDecimal().multiply(increasedGasPrice).movePointLeft(this.amount.decimals), + ) + return when (this) { + is Fee.Ethereum.EIP1559 -> copy(amount = increasedAmount, gasLimit = increasedGasLimit) + is Fee.Ethereum.Legacy -> copy(amount = increasedAmount, gasLimit = increasedGasLimit) + } } \ No newline at end of file diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt index 58f1a8476f..e3b24f7f96 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt @@ -6,11 +6,13 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplyEnterCallData import com.tangem.domain.blockaid.BlockAidGasEstimate import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.yield.supply.fixFee import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.transaction.FeeRepository import com.tangem.domain.transaction.error.FeeErrorResolver import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.yield.supply.INCREASE_GAS_LIMIT_FOR_SUPPLY +import com.tangem.domain.yield.supply.fixFee +import com.tangem.domain.yield.supply.increaseGasLimitBy import com.tangem.utils.extensions.isSingleItem import timber.log.Timber @@ -73,7 +75,10 @@ class YieldSupplyEstimateEnterFeeUseCase( ) return transactionDataList.zip(estimatedFees.estimatedGasList) { transaction, estimatedGas -> - transaction.copy(fee = fee.fixFee(cryptoCurrency, estimatedGas)) + transaction.copy( + fee = fee.fixFee(cryptoCurrency, estimatedGas) + .increaseGasLimitBy(INCREASE_GAS_LIMIT_FOR_SUPPLY), + ) } } diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt index 3c8c7aa671..60a316b9f7 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt @@ -14,11 +14,15 @@ import java.math.BigDecimal import java.math.RoundingMode /** - * Calculates max allowed network fee for Yield Supply enter transaction expressed in token units. + * Calculates the max allowed network fee for a Yield Supply transaction. * - * Uses YieldMarketToken.maxFeeNative (native coin units) and converts it to token units with the - * same conversion logic as [YieldSupplyGetCurrentFeeUseCase]: based on fiat rate ratio - * (nativeFiatRate / tokenFiatRate). + * Result is a Pair where: + * - first: fee in native coin units (maxFeeNative) + * - second: the same fee converted to token units using the fiat-rate ratio + * (nativeFiatRate / tokenFiatRate). + * + * Uses YieldMarketToken.maxFeeNative and the same conversion logic as + * [YieldSupplyGetCurrentFeeUseCase]. */ class YieldSupplyGetMaxFeeUseCase( private val yieldSupplyRepository: YieldSupplyRepository, @@ -29,7 +33,7 @@ class YieldSupplyGetMaxFeeUseCase( suspend operator fun invoke( userWallet: UserWallet, cryptoCurrencyStatus: CryptoCurrencyStatus, - ): Either = catch { + ): Either> = catch { val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: error("CryptoCurrency must be token for max fee calculation") @@ -55,7 +59,7 @@ class YieldSupplyGetMaxFeeUseCase( val cachedMarketToken = yieldSupplyRepository.getCachedMarkets().orEmpty() .firstOrNull { it.yieldSupplyKey == token.yieldSupplyKey() } val marketToken = cachedMarketToken ?: yieldSupplyRepository.getTokenStatus(token) - val maxFeeNative = marketToken.maxFeeNative.toBigDecimal() + val maxFeeNative = marketToken.maxFeeNative val rateRatio = nativeFiatRate.divide( fiatRate, @@ -65,6 +69,6 @@ class YieldSupplyGetMaxFeeUseCase( val tokenValue = rateRatio.multiply(maxFeeNative) - tokenValue.stripTrailingZeros() + maxFeeNative to tokenValue.stripTrailingZeros() } } \ No newline at end of file diff --git a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt index 8a2cc75aae..20c7619b9e 100644 --- a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt +++ b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt @@ -314,9 +314,9 @@ class YieldSupplyEstimateEnterFeeUseCaseTest { val deployFee = txs.first().fee as Fee.Ethereum.EIP1559 val approveFee = txs[1].fee as Fee.Ethereum.EIP1559 val enterFee = txs.last().fee as Fee.Ethereum.EIP1559 - Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(1_000)) - Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(2_000)) - Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(3_000)) + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(1_120)) + Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(2_240)) + Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(3_360)) } @Test @@ -353,9 +353,9 @@ class YieldSupplyEstimateEnterFeeUseCaseTest { val deployFee = txs.first().fee as Fee.Ethereum.Legacy val approveFee = txs[1].fee as Fee.Ethereum.Legacy val enterFee = txs.last().fee as Fee.Ethereum.Legacy - Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(1_000)) - Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(2_000)) - Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(3_000)) + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(1_120)) + Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(2_240)) + Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(3_360)) } private fun getDeployTx() = uncompiled( diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt index ab86ceed99..f1559c092d 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt @@ -16,9 +16,9 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.toWrappedList import com.tangem.datasource.local.config.issuers.IssuersConfigStorage +import com.tangem.domain.card.common.TwinCardNumber +import com.tangem.domain.card.common.getTwinCardNumber import com.tangem.domain.card.repository.CardRepository -import com.tangem.domain.common.TwinCardNumber -import com.tangem.domain.common.getTwinCardNumber import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.scan.ScanResponse @@ -68,6 +68,7 @@ internal class OnboardingTwinModel @Inject constructor( private val params = paramsContainer.require() private val firstCardTwinNumber = params.scanResponse.card.getTwinCardNumber() ?: error("Not twin") private val cryptoCurrencyStatusJobHolder = JobHolder() + private var twinCardsIds = Pair(null, null) private val _uiState = MutableStateFlow( when (params.mode) { @@ -159,6 +160,7 @@ internal class OnboardingTwinModel @Inject constructor( } is CompletionResult.Success -> { if (params.mode == Mode.CreateWallet) { + twinCardsIds = twinCardsIds.copy(first = result.data.cardId) cardRepository.startCardActivation(result.data.cardId) } @@ -216,6 +218,7 @@ internal class OnboardingTwinModel @Inject constructor( } is CompletionResult.Success -> { if (params.mode == Mode.CreateWallet) { + twinCardsIds = twinCardsIds.copy(second = result.data.cardId) cardRepository.startCardActivation(result.data.cardId) } @@ -299,7 +302,12 @@ internal class OnboardingTwinModel @Inject constructor( return@coroutineScope } - cardRepository.finishCardActivation(params.scanResponse.card.cardId) + twinCardsIds.first?.let { + cardRepository.finishCardActivation(it) + } + twinCardsIds.second?.let { + cardRepository.finishCardActivation(it) + } params.modelCallbacks.onDone() } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index ca65afd419..aa4707f395 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -24,11 +24,10 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.VibratorHapticManager +import com.tangem.core.ui.message.DialogMessage import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase -import com.tangem.core.ui.message.DialogMessage -import com.tangem.features.staking.impl.R import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -60,6 +59,7 @@ import com.tangem.domain.transaction.usecase.* import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.staking.api.StakingComponent +import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender import com.tangem.features.staking.impl.navigation.InnerStakingRouter @@ -171,6 +171,7 @@ internal class StakingModel @Inject constructor( } private var isAccountInitialized: Boolean = true + private var isTonHeatupCase: Boolean = false private lateinit var cryptoCurrencyStatus: CryptoCurrencyStatus private var stakingActions: List = emptyList() private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null @@ -1063,7 +1064,7 @@ internal class StakingModel @Inject constructor( currency = params.cryptoCurrency, ).conflate().distinctUntilChanged() .filter { - value.currentStep == StakingStep.InitialInfo || isCaseWithUnitializedTonAccount() + value.currentStep == StakingStep.InitialInfo || isTopHeatupCase() }.onEach { (maybeAccount, maybeStatus) -> isAccountsModeEnabled = isAccountsModeEnabledUseCase.invokeSync() account = maybeAccount @@ -1077,7 +1078,7 @@ internal class StakingModel @Inject constructor( isSingleWalletWithTokens = false, ).conflate().distinctUntilChanged() .filter { - value.currentStep == StakingStep.InitialInfo || isCaseWithUnitializedTonAccount() + value.currentStep == StakingStep.InitialInfo || isTopHeatupCase() } .onEach { maybeStatus -> maybeStatus.fold( @@ -1119,19 +1120,7 @@ internal class StakingModel @Inject constructor( } cryptoCurrencyStatus = status - val isAccountInitializedNew = checkAccountInitializedUseCase.invoke( - userWalletId = userWalletId, - network = cryptoCurrencyStatus.currency.network, - ).getOrElse { - Timber.e(it) - false - } - if (isAccountInitializedNew && !isAccountInitialized) { - isAccountInitialized = true - updateNotifications() - } - isAccountInitialized = isAccountInitializedNew - + checkForTonHeatupCase() setupApprovalNeeded() setupIsAnyTokenStaked() checkIfSubtractAvailable() @@ -1277,6 +1266,26 @@ internal class StakingModel @Inject constructor( ) } + private suspend fun checkForTonHeatupCase() { + val isAccountInitializedNewValue = checkAccountInitializedUseCase.invoke( + userWalletId = userWalletId, + network = cryptoCurrencyStatus.currency.network, + ).getOrElse { + Timber.e(it) + false + } + + if (!isAccountInitializedNewValue && isAccountInitialized) { + isTonHeatupCase = true + } + + isAccountInitialized = isAccountInitializedNewValue + + if (isTopHeatupCase()) { + updateNotifications() + } + } + private fun isExplicitExit(balanceType: BalanceType, pendingAction: PendingAction?): Boolean { return balanceType == BalanceType.STAKED && pendingAction?.type?.isRestake == false } @@ -1296,11 +1305,11 @@ internal class StakingModel @Inject constructor( .getOrElse { false } } - private fun isCaseWithUnitializedTonAccount(): Boolean { + private fun isTopHeatupCase(): Boolean { if (!::cryptoCurrencyStatus.isInitialized) return false return value.currentStep == StakingStep.Confirmation && isTon(cryptoCurrencyStatus.currency.network.rawId) && - !isAccountInitialized + isTonHeatupCase } private companion object { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt index ad3fcd0c4e..3bef7418c8 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsNotificationsAnalyticsSender.kt @@ -4,7 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics @@ -38,8 +38,8 @@ internal class TokenDetailsNotificationsAnalyticsSender( -> TokenDetailsAnalyticsEvent.Notice.NotEnoughFee( currency = cryptoCurrency, ) - is TokenDetailsNotification.SwapPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner( - program = TokenSwapPromoAnalyticsEvent.Program.Empty, // Use it on new promo action + is TokenDetailsNotification.SwapPromo -> PromoAnalyticsEvent.NoticePromotionBanner( + program = PromoAnalyticsEvent.Program.Empty, // Use it on new promo action source = AnalyticsParam.ScreensSources.Token, ) is TokenDetailsNotification.KaspaIncompleteTransactionWarning -> TokenDetailsAnalyticsEvent.Notice.Reveal( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 3137a72bf3..29fb7b3937 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -847,10 +847,10 @@ internal class TokenDetailsModel @Inject constructor( modelScope.launch(dispatchers.main) { shouldShowPromoTokenUseCase.neverToShow(promoId) analyticsEventsHandler.send( - TokenSwapPromoAnalyticsEvent.PromotionBannerClicked( + PromoAnalyticsEvent.PromotionBannerClicked( source = AnalyticsParam.ScreensSources.Token, - program = TokenSwapPromoAnalyticsEvent.Program.Empty, // Use it on new promo action - action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed, + program = PromoAnalyticsEvent.Program.Empty, // Use it on new promo action + action = PromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed, ), ) } @@ -860,10 +860,10 @@ internal class TokenDetailsModel @Inject constructor( modelScope.launch(dispatchers.main) { shouldShowPromoTokenUseCase.neverToShow(promoId) analyticsEventsHandler.send( - TokenSwapPromoAnalyticsEvent.PromotionBannerClicked( + PromoAnalyticsEvent.PromotionBannerClicked( source = AnalyticsParam.ScreensSources.Token, - program = TokenSwapPromoAnalyticsEvent.Program.Empty, // Use it on new promo action - action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked, + program = PromoAnalyticsEvent.Program.Empty, // Use it on new promo action + action = PromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt index 739be22276..945d0dff0c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt @@ -33,7 +33,9 @@ import com.tangem.domain.settings.NeverToSuggestRateAppUseCase import com.tangem.domain.settings.RemindToRateAppLaterUseCase import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher -import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program +import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType import com.tangem.domain.wallets.models.UnlockWalletsError import com.tangem.domain.wallets.usecase.* @@ -327,11 +329,12 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( analyticsEventHandler.send( when (promoId) { PromoId.Referral -> MainScreen.ReferralPromoButtonDismiss - PromoId.Sepa -> TokenSwapPromoAnalyticsEvent.PromotionBannerClicked( + PromoId.Sepa -> PromotionBannerClicked( source = AnalyticsParam.ScreensSources.Main, - program = TokenSwapPromoAnalyticsEvent.Program.Sepa, - action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed, + program = Program.Sepa, + action = PromotionBannerClicked.BannerAction.Closed, ) + PromoId.VisaPresale -> PromoAnalyticsEvent.VisaWaitlistPromoDismiss }, ) @@ -349,10 +352,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( } PromoId.Sepa -> { analyticsEventHandler.send( - TokenSwapPromoAnalyticsEvent.PromotionBannerClicked( + PromotionBannerClicked( source = AnalyticsParam.ScreensSources.Main, - program = TokenSwapPromoAnalyticsEvent.Program.Sepa, - action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked, + program = Program.Sepa, + action = PromotionBannerClicked.BannerAction.Clicked, ), ) cryptoCurrency ?: return @@ -365,6 +368,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( ), ) } + PromoId.VisaPresale -> { + analyticsEventHandler.send(PromoAnalyticsEvent.VisaWaitlistPromoJoin) + urlOpener.openUrl(VISA_PROMO_LINK) + } } } @@ -575,4 +582,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( } } } + + private companion object { + const val VISA_PROMO_LINK = "https://tangem.com/en/cardwaitlist/?utm_source=tangem-app-banner" + + "&utm_medium=banner" + + "&utm_campaign=tangempaywaitlist" + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt index 135b2c2f6c..54d3a4cca2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt @@ -46,26 +46,6 @@ sealed class WalletScreenAnalyticsEvent { ) } - sealed class Token( - event: String, - params: Map = mapOf(), - ) : AnalyticsEvent(category = "Token", event = event, params = params) { - - class PolkadotAccountReset(hasReset: Boolean) : Token( - event = "Polkadot Account Reset", - params = mapOf( - AnalyticsParam.STATE to if (hasReset) "Yes" else "No", - ), - ) - - class PolkadotImmortalTransactions(hasImmortalTransaction: Boolean) : Token( - event = "Polkadot Immortal Transactions", - params = mapOf( - AnalyticsParam.STATE to if (hasImmortalTransaction) "Yes" else "No", - ), - ) - } - sealed class MainScreen( event: String, params: Map = mapOf(), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt index 9eaaa739ba..1f136f0dc3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt @@ -4,8 +4,8 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped -import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent.* -import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent.Program +import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification @@ -50,15 +50,16 @@ internal class WalletWarningsAnalyticsSender @Inject constructor( is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem is WalletNotification.Critical.BackupError -> MainScreen.BackupError is WalletNotification.NoteMigration -> MainScreen.NotePromo - is WalletNotification.SwapPromo -> NoticePromotionBanner( + is WalletNotification.SwapPromo -> PromoAnalyticsEvent.NoticePromotionBanner( source = AnalyticsParam.ScreensSources.Main, program = Program.Empty, // Use it on new promo action ) - is WalletNotification.Sepa -> NoticePromotionBanner( + is WalletNotification.Sepa -> PromoAnalyticsEvent.NoticePromotionBanner( source = AnalyticsParam.ScreensSources.Main, program = Program.Sepa, ) is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo + is WalletNotification.VisaPresalePromo -> PromoAnalyticsEvent.VisaWaitlistPromo is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender] is WalletNotification.Informational.NoAccount, is WalletNotification.Warning.LowSignatures, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index be35057603..108d706b1d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -97,7 +97,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( isReadyToShowRateAppUseCase(), isNeedToBackupUseCase(userWallet.walletId), seedPhraseNotificationUseCase(userWalletId = userWallet.walletId), - shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Referral), + shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.VisaPresale), shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa), notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key), ) { array -> array } @@ -109,7 +109,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( val isReadyToShowRating = array[1] as Boolean val isNeedToBackup = array[2] as Boolean val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus - val shouldShowReferralPromo = array[4] as Boolean + val shouldShowVisaPromo = array[4] as Boolean val shouldShowSepaBanner = array[5] as Boolean val shouldShowEnablePushesReminderNotification = array[6] as Boolean @@ -120,7 +120,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( addFinishWalletActivationNotification(userWallet, totalFiatBalance, clickIntents) - addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo) + addVisaPresalePromoNotification(clickIntents, shouldShowVisaPromo) addSepaPromoNotification(userWallet, clickIntents, shouldShowSepaBanner) @@ -274,17 +274,16 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( .map(CryptoCurrencyStatus::currency) } - private fun MutableList.addReferralPromoNotification( - cardTypesResolver: CardTypesResolver?, + private fun MutableList.addVisaPresalePromoNotification( clickIntents: WalletClickIntents, shouldShowPromo: Boolean, ) { addIf( - element = WalletNotification.ReferralPromo( - onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.Referral) }, - onClick = { clickIntents.onPromoClick(promoId = PromoId.Referral) }, + element = WalletNotification.VisaPresalePromo( + onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.VisaPresale) }, + onClick = { clickIntents.onPromoClick(promoId = PromoId.VisaPresale) }, ), - condition = shouldShowPromo && (cardTypesResolver == null || cardTypesResolver.isTangemWallet()), + condition = shouldShowPromo, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt index 5eb6975247..b08d9fb996 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt @@ -2,17 +2,17 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.core.decompose.di.ModelScoped import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.nft.GetNFTCollectionsUseCase import com.tangem.domain.promo.GetStoryContentUseCase -import com.tangem.domain.tokens.ApplyTokenListSortingUseCase -import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase -import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.usecase.StakingApyFlowUseCase +import com.tangem.domain.tokens.ApplyTokenListSortingUseCase +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents +import com.tangem.feature.wallet.presentation.account.AccountDependencies import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender @@ -21,7 +21,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.subscribers.* -import com.tangem.feature.wallet.presentation.account.AccountDependencies @Suppress("LongParameterList") @ModelScoped @@ -38,7 +37,6 @@ internal class MultiWalletContentLoader( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, - private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, private val getStoryContentUseCase: GetStoryContentUseCase, private val walletsRepository: WalletsRepository, @@ -59,7 +57,6 @@ internal class MultiWalletContentLoader( tokenListStore = tokenListStore, getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, applyTokenListSortingUseCase = applyTokenListSortingUseCase, - runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, accountDependencies = accountDependencies, yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase, stakingApyFlowUseCase = stakingApyFlowUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt index 63d8447f57..e2dae7da64 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt @@ -5,7 +5,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.nft.GetNFTCollectionsUseCase import com.tangem.domain.promo.GetStoryContentUseCase import com.tangem.domain.tokens.ApplyTokenListSortingUseCase -import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.usecase.StakingApyFlowUseCase @@ -35,7 +34,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, - private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, private val getStoryContentUseCase: GetStoryContentUseCase, private val walletsRepository: WalletsRepository, @@ -59,7 +57,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, walletWarningsSingleEventSender = walletWarningsSingleEventSender, applyTokenListSortingUseCase = applyTokenListSortingUseCase, - runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, getStoryContentUseCase = getStoryContentUseCase, shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase, walletsRepository = walletsRepository, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt index a70aae4f9a..c2fbf4fa35 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt @@ -4,7 +4,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.promo.GetStoryContentUseCase import com.tangem.domain.staking.usecase.StakingApyFlowUseCase -import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents @@ -31,7 +30,6 @@ internal class SingleWalletWithTokenContentLoader( private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, private val tokenListStore: MultiWalletTokenListStore, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, - private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, private val getStoryContentUseCase: GetStoryContentUseCase, private val accountDependencies: AccountDependencies, @@ -49,7 +47,6 @@ internal class SingleWalletWithTokenContentLoader( walletWithFundsChecker = walletWithFundsChecker, tokenListStore = tokenListStore, getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, - runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, accountDependencies = accountDependencies, yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase, stakingApyFlowUseCase = stakingApyFlowUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt index ccaa76798e..7fe9f9188e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt @@ -5,7 +5,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.promo.GetStoryContentUseCase import com.tangem.domain.staking.usecase.StakingApyFlowUseCase -import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents @@ -32,7 +31,6 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, - private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, private val getStoryContentUseCase: GetStoryContentUseCase, private val accountDependencies: AccountDependencies, @@ -52,7 +50,6 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor( getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, walletWarningsSingleEventSender = walletWarningsSingleEventSender, - runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, getStoryContentUseCase = getStoryContentUseCase, shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase, accountDependencies = accountDependencies, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt index 1479c155dd..62e29d2cac 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt @@ -298,6 +298,23 @@ sealed class WalletNotification(val config: NotificationConfig) { ), ) + data class VisaPresalePromo( + val onCloseClick: () -> Unit, + val onClick: () -> Unit, + ) : WalletNotification( + config = NotificationConfig( + title = resourceReference(R.string.notification_visa_waitlist_promo_title), + subtitle = resourceReference(R.string.notification_visa_waitlist_promo_text), + iconResId = R.drawable.img_visa_waitlist_promo, + onCloseClick = onCloseClick, + buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( + text = resourceReference(R.string.notification_referral_promo_button), + onClick = onClick, + ), + iconSize = 54.dp, + ), + ) + data class Sepa( val onCloseClick: () -> Unit, val onClick: () -> Unit, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt index 79601cf7d4..514d33f97c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt @@ -15,7 +15,6 @@ import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.usecase.StakingApyFlowUseCase -import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents @@ -43,7 +42,6 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() { protected abstract val tokenListAnalyticsSender: TokenListAnalyticsSender protected abstract val walletWithFundsChecker: WalletWithFundsChecker protected abstract val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase - protected abstract val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase protected abstract val accountDependencies: AccountDependencies protected abstract val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase protected abstract val stakingApyFlowUseCase: StakingApyFlowUseCase @@ -80,8 +78,6 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() { coroutineScope.launch { onTokenListReceived(maybeTokenList) }.saveIn(onTokenListReceivedJobHolder) - - coroutineScope.launch { startCheck(maybeTokenList) } }, flow2 = appCurrencyFlow(), flow3 = yieldSupplyApyFlow(), @@ -137,20 +133,6 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() { walletWithFundsChecker.check(tokenList) } - private suspend fun startCheck(maybeTokenList: Lce) { - // Run Polkadot account health check - maybeTokenList.getOrNull()?.let { tokenList -> - tokenList - .flattenCurrencies() - .forEach { - runPolkadotAccountHealthCheckUseCase( - userWalletId = userWallet.walletId, - network = it.currency.network, - ) - } - } - } - private fun createAccountListFlow(coroutineScope: CoroutineScope): Flow<*> = combine6( flow1 = accountListFlow(coroutineScope) .onEach { accountStatusList -> @@ -161,14 +143,7 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() { ) }.saveIn(sendAnalyticsJobHolder) } - .distinctUntilChanged() - .onEach { accountList -> - // todo account see[onAccountListReceived] - // coroutineScope.launch { onAccountListReceived() }.saveIn(onTokenListReceivedJobHolder) - - accountList.flattenTokens() - .forEach { tokenList -> startCheck(Lce.Content(tokenList)) } - }, + .distinctUntilChanged(), flow2 = appCurrencyFlow(), flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet), flow4 = accountDependencies.isAccountsModeEnabledUseCase(), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt index f64dafc766..8cea37e832 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt @@ -12,7 +12,6 @@ import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.usecase.StakingApyFlowUseCase import com.tangem.domain.tokens.ApplyTokenListSortingUseCase -import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents @@ -34,7 +33,6 @@ internal class MultiWalletTokenListSubscriber( override val tokenListAnalyticsSender: TokenListAnalyticsSender, override val walletWithFundsChecker: WalletWithFundsChecker, override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, - override val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, override val accountDependencies: AccountDependencies, override val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase, override val stakingApyFlowUseCase: StakingApyFlowUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt index 3143cec16b..cb4085687e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt @@ -8,7 +8,6 @@ import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.usecase.StakingApyFlowUseCase -import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents @@ -30,7 +29,6 @@ internal class SingleWalletWithTokenListSubscriber( override val tokenListAnalyticsSender: TokenListAnalyticsSender, override val walletWithFundsChecker: WalletWithFundsChecker, override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, - override val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, override val accountDependencies: AccountDependencies, override val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase, override val stakingApyFlowUseCase: StakingApyFlowUseCase, diff --git a/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/analytics/YieldSupplyAnalytics.kt b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/analytics/YieldSupplyAnalytics.kt index 596a7bb892..32c5975417 100644 --- a/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/analytics/YieldSupplyAnalytics.kt +++ b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/analytics/YieldSupplyAnalytics.kt @@ -77,12 +77,26 @@ sealed class YieldSupplyAnalytics( ), ) - data object ButtonFeePolicy : YieldSupplyAnalytics( + data class ButtonFeePolicy( + val token: String, + val blockchain: String, + ) : YieldSupplyAnalytics( event = "Button - Fee Policy", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchain, + ), ) - data object EarnInProgressScreen : YieldSupplyAnalytics( + data class EarnInProgressScreen( + val token: String, + val blockchain: String, + ) : YieldSupplyAnalytics( event = "Earn In Progress Screen", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchain, + ), ) data object FundsEarned : YieldSupplyAnalytics( @@ -162,10 +176,10 @@ sealed class YieldSupplyAnalytics( val errorDescription: String?, ) : YieldSupplyAnalytics( event = "Earn Errors", - params = buildMap { - ACTION to action - ERROR_DESCRIPTION to errorDescription.orEmpty() - }, + params = mapOf( + ACTION to action.value, + ERROR_DESCRIPTION to errorDescription.orEmpty(), + ), ) data object ApyChartViewed : YieldSupplyAnalytics( @@ -184,6 +198,7 @@ sealed class YieldSupplyAnalytics( ) enum class Action(val value: String) { + Start("Start"), Approve("Approve"), Stop("Stop"), } diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt index 7b3d8e94ed..ccc975f7ff 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt @@ -5,7 +5,6 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.extensions.TextReference import kotlinx.collections.immutable.ImmutableList -import java.math.BigDecimal @Immutable internal sealed class YieldSupplyFeeUM { @@ -31,5 +30,4 @@ internal data class YieldSupplyActionUM( val yieldSupplyFeeUM: YieldSupplyFeeUM, val isPrimaryButtonEnabled: Boolean, val isTransactionSending: Boolean, - val maxFee: BigDecimal = BigDecimal.ZERO, ) \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt index af19fd4a55..1cbf8f1cfe 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -40,6 +41,7 @@ import com.tangem.utils.StringsSigns internal fun YieldSupplyBlockContent(yieldSupplyUM: YieldSupplyUM, modifier: Modifier = Modifier) { AnimatedContent( targetState = yieldSupplyUM, + contentKey = { it::class }, ) { supplyUM -> when (supplyUM) { is YieldSupplyUM.Available -> SupplyAvailable(supplyUM, modifier) @@ -90,6 +92,7 @@ private fun SupplyUnavailable(modifier: Modifier = Modifier) { ) } +@Suppress("LongMethod") @Composable private fun SupplyContent(supplyUM: YieldSupplyUM.Content, modifier: Modifier = Modifier) { Row( @@ -114,21 +117,30 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, modifier: Modifier = horizontalArrangement = Arrangement.spacedBy(4.dp), ) { Text( + modifier = Modifier.weight(1.0f, fill = false), text = supplyUM.title.resolveReference(), style = TangemTheme.typography.subtitle1, + maxLines = 1, + overflow = TextOverflow.Ellipsis, color = TangemTheme.colors.text.primary1, ) - Text( - text = StringsSigns.DOT, - style = TangemTheme.typography.subtitle1, - color = TangemTheme.colors.text.tertiary, - ) - Text( - text = supplyUM.rewardsApy.resolveReference(), - style = TangemTheme.typography.subtitle1, - maxLines = 1, - color = TangemTheme.colors.text.accent, - ) + AnimatedVisibility(supplyUM.rewardsApy != TextReference.EMPTY) { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = StringsSigns.DOT, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.tertiary, + ) + Text( + text = supplyUM.rewardsApy.resolveReference(), + style = TangemTheme.typography.subtitle1, + maxLines = 1, + color = TangemTheme.colors.text.accent, + ) + } + } } Text( text = supplyUM.subtitle.resolveReference(), @@ -337,17 +349,17 @@ private class PreviewProvider : PreviewParameterProvider { onClick = {}, ), YieldSupplyUM.Content( - title = stringReference("Aave lending is active"), + title = stringReference("Aave l"), subtitle = stringReference("Interest accrues automatically"), - rewardsApy = stringReference("5.1 % APY"), + rewardsApy = stringReference("APY 5.1%"), onClick = {}, apy = "5.1", showWarningIcon = false, ), YieldSupplyUM.Content( - title = stringReference("Aave lending is active"), + title = stringReference("Aave lending is active "), subtitle = stringReference("Interest accrues automatically"), - rewardsApy = stringReference("5.1 % APY"), + rewardsApy = stringReference("APY 5.1%"), onClick = {}, apy = "5.1", showWarningIcon = true, diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/ui/YieldSupplyPromoContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/ui/YieldSupplyPromoContent.kt index e036e90dd0..12359bae82 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/ui/YieldSupplyPromoContent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/ui/YieldSupplyPromoContent.kt @@ -6,6 +6,8 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -29,9 +31,7 @@ import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.yield.supply.impl.R import com.tangem.features.yield.supply.impl.promo.entity.YieldSupplyPromoUM import com.tangem.features.yield.supply.impl.promo.model.YieldSupplyPromoClickIntents -import com.tangem.utils.StringsSigns -@Suppress("MagicNumber") @Composable internal fun YieldSupplyPromoContent( yieldSupplyPromoUM: YieldSupplyPromoUM, @@ -39,50 +39,18 @@ internal fun YieldSupplyPromoContent( modifier: Modifier = Modifier, ) { Column( - horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier .background(color = TangemTheme.colors.background.tertiary) - .fillMaxWidth() + .fillMaxSize() .imePadding() - .systemBarsPadding() - .padding(horizontal = 16.dp), + .systemBarsPadding(), + horizontalAlignment = Alignment.CenterHorizontally, ) { YieldStatusAppBar( onBackClick = clickIntents::onBackClick, onHowItWorksClick = clickIntents::onHowItWorksClick, ) - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(horizontal = 20.dp), - ) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.ic_analytics_up_24), - tint = TangemTheme.colors.icon.accent, - contentDescription = null, - modifier = Modifier - .background(TangemTheme.colors.icon.accent.copy(0.1f), CircleShape) - .padding(12.dp) - .size(32.dp), - ) - SpacerH(20.dp) - Text( - text = yieldSupplyPromoUM.title.resolveReference(), - style = TangemTheme.typography.h2, - color = TangemTheme.colors.text.primary1, - ) - SpacerH8() - Label( - state = LabelUM( - text = yieldSupplyPromoUM.subtitle, - style = LabelStyle.REGULAR, - icon = R.drawable.ic_information_24, - onIconClick = clickIntents::onApyInfoClick, - ), - ) - SpacerH32() - PromoItems() - } - SpacerHMax() + Content(yieldSupplyPromoUM = yieldSupplyPromoUM, clickIntents = clickIntents) YieldSupplyTosText( tosLink = yieldSupplyPromoUM.tosLink, policyLink = yieldSupplyPromoUM.policyLink, @@ -91,16 +59,72 @@ internal fun YieldSupplyPromoContent( PrimaryButton( text = stringResourceSafe(R.string.yield_module_start_earning), onClick = clickIntents::onStartEarningClick, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .padding( + start = 16.dp, + end = 16.dp, + bottom = 8.dp, + ) + .fillMaxWidth(), + ) + } +} + +@Suppress("MagicNumber") +@Composable +private fun ColumnScope.Content(yieldSupplyPromoUM: YieldSupplyPromoUM, clickIntents: YieldSupplyPromoClickIntents) { + Box(modifier = Modifier.weight(1f)) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 20.dp), + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_analytics_up_24), + tint = TangemTheme.colors.icon.accent, + contentDescription = null, + modifier = Modifier + .background(TangemTheme.colors.icon.accent.copy(0.1f), CircleShape) + .padding(12.dp) + .size(32.dp), + ) + SpacerH(20.dp) + Text( + text = yieldSupplyPromoUM.title.resolveReference(), + style = TangemTheme.typography.h2, + color = TangemTheme.colors.text.primary1, + ) + SpacerH8() + Label( + state = LabelUM( + text = yieldSupplyPromoUM.subtitle, + style = LabelStyle.REGULAR, + icon = R.drawable.ic_information_24, + onIconClick = clickIntents::onApyInfoClick, + ), + ) + SpacerH32() + PromoItems() + } + SpacerH32() + } + Fade( + backgroundColor = TangemTheme.colors.background.tertiary, + modifier = Modifier.align(Alignment.BottomCenter), ) - SpacerH8() } } @Composable private fun YieldStatusAppBar(onBackClick: () -> Unit, onHowItWorksClick: () -> Unit) { Row( - modifier = Modifier.padding(vertical = 16.dp), + modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically, ) { Icon( @@ -166,7 +190,12 @@ private fun PromoItem(@DrawableRes icon: Int, title: TextReference, subtitle: Te } @Composable -private fun YieldSupplyTosText(tosLink: String, policyLink: String, onClick: (String) -> Unit) { +private fun YieldSupplyTosText( + tosLink: String, + policyLink: String, + onClick: (String) -> Unit, + modifier: Modifier = Modifier, +) { val tosTitle = stringResourceSafe(R.string.common_terms_of_use) val policyTitle = stringResourceSafe(R.string.common_privacy_policy) val fullString = stringResourceSafe(id = R.string.yield_module_promo_screen_terms_disclaimer, tosTitle, policyTitle) @@ -175,8 +204,6 @@ private fun YieldSupplyTosText(tosLink: String, policyLink: String, onClick: (St Text( text = buildAnnotatedString { - append(StringsSigns.POINT_SIGN) - appendSpace() append(fullString.substring(0, tosIndex)) withLink( link = LinkAnnotation.Clickable( @@ -207,7 +234,7 @@ private fun YieldSupplyTosText(tosLink: String, policyLink: String, onClick: (St textAlign = TextAlign.Center, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(16.dp), ) diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveModel.kt index 8d8bd96071..bfb21a8ec9 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveModel.kt @@ -45,7 +45,7 @@ import javax.inject.Inject internal class YieldSupplyActiveModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, - analyticsHandler: AnalyticsEventHandler, + private val analyticsHandler: AnalyticsEventHandler, private val yieldSupplyGetProtocolBalanceUseCase: YieldSupplyGetProtocolBalanceUseCase, private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase, private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase, @@ -82,7 +82,7 @@ internal class YieldSupplyActiveModel @Inject constructor( init { analyticsHandler.send( - YieldSupplyAnalytics.EarningScreenInfoOpened( + YieldSupplyAnalytics.EarnInProgressScreen( token = cryptoCurrency.symbol, blockchain = cryptoCurrency.network.name, ), @@ -189,6 +189,12 @@ internal class YieldSupplyActiveModel @Inject constructor( return if (notDepositedAmount > BigDecimal.ZERO) { val formattedAmount = notDepositedAmount.format { crypto(symbol = "", decimals = cryptoCurrencyStatus.currency.decimals) } + analyticsHandler.send( + YieldSupplyAnalytics.NoticeAmountNotDeposited( + token = cryptoCurrency.symbol, + blockchain = cryptoCurrency.network.name, + ), + ) NotificationUM.Warning.YieldSupplyNotAllAmountSupplied( formattedAmount = formattedAmount, symbol = cryptoCurrency.symbol, @@ -242,7 +248,8 @@ internal class YieldSupplyActiveModel @Inject constructor( cryptoCurrencyStatus = cryptoStatus, appCurrency = appCurrency, feeValue = currentFee, - maxNetworkFee = maxFee, + maxNetworkFee = maxFee.second, + analyticsHandler = analyticsHandler, ), ) } else { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt index bcd2e22076..cc7f66e762 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt @@ -1,5 +1,6 @@ package com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList @@ -9,6 +10,7 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics import com.tangem.features.yield.supply.impl.R import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM import com.tangem.utils.transformer.Transformer @@ -22,6 +24,7 @@ internal class YieldSupplyActiveFeeContentTransformer( private val appCurrency: AppCurrency, private val feeValue: BigDecimal, private val maxNetworkFee: BigDecimal, + private val analyticsHandler: AnalyticsEventHandler, ) : Transformer { override fun transform(prevState: YieldSupplyActiveContentUM): YieldSupplyActiveContentUM { @@ -48,6 +51,15 @@ internal class YieldSupplyActiveFeeContentTransformer( val isHighFee = feeValue > maxNetworkFee + if (isHighFee) { + analyticsHandler.send( + YieldSupplyAnalytics.NoticeHighNetworkFee( + token = cryptoCurrency.symbol, + blockchain = cryptoCurrency.network.name, + ), + ) + } + return prevState.copy( currentFee = stringReference(tokenFiatFeeValueText), feeDescription = feeNoteValue, diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt index 0be362037a..3e43bf02a5 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt @@ -4,6 +4,8 @@ import arrow.core.getOrElse import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -132,6 +134,12 @@ internal class YieldSupplyApproveModel @Inject constructor( ifLeft = { error -> Timber.e(error.toString()) uiState.update(YieldSupplyTransactionReadyTransformer) + analyticsEventHandler.send( + YieldSupplyAnalytics.EarnErrors( + action = YieldSupplyAnalytics.Action.Approve, + errorDescription = error.getAnalyticsDescription(), + ), + ) yieldSupplyAlertFactory.getSendTransactionErrorState( error = error, popBack = params.callback::onBackClick, @@ -147,6 +155,17 @@ internal class YieldSupplyApproveModel @Inject constructor( ) }, ifRight = { + val event = AnalyticsParam.TxSentFrom.Earning( + blockchain = cryptoCurrency.network.name, + token = cryptoCurrency.symbol, + feeType = AnalyticsParam.FeeType.Normal, + ) + analyticsEventHandler.send( + Basic.TransactionSent( + sentFrom = event, + memoType = Basic.TransactionSent.MemoType.Null, + ), + ) analyticsEventHandler.send(YieldSupplyAnalytics.ApprovalAction( token = cryptoCurrency.symbol, blockchain = cryptoCurrency.network.name, diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt index d6de1559ac..2037750020 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt @@ -59,7 +59,10 @@ internal class YieldSupplyStartEarningEntryModel @Inject constructor( override fun onFeePolicyClick() { analyticsEventHandler.send( - YieldSupplyAnalytics.ButtonFeePolicy, + YieldSupplyAnalytics.ButtonFeePolicy( + token = params.cryptoCurrency.symbol, + blockchain = params.cryptoCurrency.network.name, + ), ) router.push(YieldSupplyStartEarningRoute.FeePolicy) } diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt index fdb9493169..c9dc6d5b65 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt @@ -3,16 +3,16 @@ package com.tangem.features.yield.supply.impl.subcomponents.startearning.model import arrow.core.getOrElse import com.tangem.blockchain.common.TransactionSender import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList -import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase @@ -61,8 +61,8 @@ internal class YieldSupplyStartEarningModel @Inject constructor( private val yieldSupplyNotificationsUpdateTrigger: YieldSupplyNotificationsUpdateTrigger, private val yieldSupplyAlertFactory: YieldSupplyAlertFactory, private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase, - private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase, private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase, + private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase, private val yieldSupplyRepository: YieldSupplyRepository, ) : Model(), YieldSupplyNotificationsComponent.ModelCallback { @@ -130,11 +130,8 @@ internal class YieldSupplyStartEarningModel @Inject constructor( } } - private suspend fun getMaxFee(): BigDecimal? { - if (uiState.value.maxFee != BigDecimal.ZERO) return uiState.value.maxFee - val yieldTokenStatus = yieldSupplyGetTokenStatusUseCase(cryptoCurrency as CryptoCurrency.Token) - .getOrNull() - return yieldTokenStatus?.maxFeeNative?.parseToBigDecimal(cryptoCurrency.decimals) + private suspend fun getMaxFeePair(): Pair? { + return yieldSupplyGetMaxFeeUseCase(userWallet, cryptoCurrencyStatus).getOrNull() } private suspend fun onLoadFee() { @@ -144,11 +141,10 @@ internal class YieldSupplyStartEarningModel @Inject constructor( it.copy(yieldSupplyFeeUM = YieldSupplyFeeUM.Loading) } - val maxFee = if (uiState.value.maxFee == BigDecimal.ZERO) { - getMaxFee() - } else { - uiState.value.maxFee - } ?: return + val maxFeePair = getMaxFeePair() ?: return + + val maxFee = maxFeePair.first + val maxFeeFiat = maxFeePair.second val transactionListData = yieldSupplyStartEarningUseCase( userWalletId = userWallet.walletId, @@ -189,6 +185,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( val feeSum = updatedTransactionList.sumOf { it.fee?.amount?.value.orZero() } + uiState.update( YieldSupplyStartEarningFeeContentTransformer( cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value, @@ -197,6 +194,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( updatedTransactionList = updatedTransactionList, feeValue = feeSum, maxNetworkFee = maxFee, + maxNetworkFeeFiat = maxFeeFiat, minAmount = minAmount, ), ) @@ -232,7 +230,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( uiState.update(YieldSupplyTransactionReadyTransformer) analytics.send( YieldSupplyAnalytics.EarnErrors( - action = YieldSupplyAnalytics.Action.Approve, + action = YieldSupplyAnalytics.Action.Start, errorDescription = error.getAnalyticsDescription(), ), ) @@ -252,7 +250,20 @@ internal class YieldSupplyStartEarningModel @Inject constructor( }, ifRight = { yieldSupplyRepository.saveTokenProtocolStatus(cryptoCurrency, YieldSupplyEnterStatus.Enter) + val event = AnalyticsParam.TxSentFrom.Earning( + blockchain = cryptoCurrency.network.name, + token = cryptoCurrency.symbol, + feeType = AnalyticsParam.FeeType.Normal, + ) analytics.send(YieldSupplyAnalytics.FundsEarned) + yieldSupplyFeeUM.transactionDataList.forEach { + analytics.send( + Basic.TransactionSent( + sentFrom = event, + memoType = Basic.TransactionSent.MemoType.Null, + ), + ) + } val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value if (address != null) { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/transformers/YieldSupplyStartEarningFeeContentTransformer.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/transformers/YieldSupplyStartEarningFeeContentTransformer.kt index b76fd630a4..189ffa8a23 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/transformers/YieldSupplyStartEarningFeeContentTransformer.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/transformers/YieldSupplyStartEarningFeeContentTransformer.kt @@ -25,6 +25,7 @@ internal class YieldSupplyStartEarningFeeContentTransformer( private val updatedTransactionList: List, private val feeValue: BigDecimal, private val maxNetworkFee: BigDecimal, + private val maxNetworkFeeFiat: BigDecimal, private val minAmount: BigDecimal, ) : Transformer { override fun transform(prevState: YieldSupplyActionUM): YieldSupplyActionUM { @@ -42,14 +43,8 @@ internal class YieldSupplyStartEarningFeeContentTransformer( val tokenFiatFee = feeFiat // same fiat amount val tokenFiatFeeValueText = tokenFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) } - val maxFiatFee = feeFiatRate?.let { rate -> - maxNetworkFee.multiply(rate) - } - val maxFeeCryptoValueText = tokenFiatRate?.let { rate -> - maxFiatFee?.divide(rate, cryptoCurrency.decimals, RoundingMode.HALF_UP) - }.format { crypto(cryptoCurrency) } - - val maxFiatFeeValueText = maxFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) } + val maxFeeCryptoValueText = maxNetworkFee.format { crypto(cryptoCurrency) } + val maxFiatFeeValueText = maxNetworkFeeFiat.format { fiat(appCurrency.code, appCurrency.symbol) } val minAmountCryptoText = minAmount.format { crypto(cryptoCurrency) } val minAmountFiat = tokenFiatRate?.let(minAmount::multiply) diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt index 73cfd703d0..658dd165bb 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt @@ -2,6 +2,8 @@ package com.tangem.features.yield.supply.impl.subcomponents.stopearning.model import arrow.core.getOrElse import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -16,7 +18,9 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase +import com.tangem.domain.yield.supply.INCREASE_GAS_LIMIT_FOR_SUPPLY import com.tangem.domain.yield.supply.YieldSupplyRepository +import com.tangem.domain.yield.supply.increaseGasLimitBy import com.tangem.domain.yield.supply.models.YieldSupplyEnterStatus import com.tangem.domain.yield.supply.usecase.YieldSupplyDeactivateUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyStopEarningUseCase @@ -82,7 +86,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor( val uiState: StateFlow field: MutableStateFlow = MutableStateFlow( YieldSupplyActionUM( - title = resourceReference(R.string.yield_module_stop_earning), + title = resourceReference(R.string.yield_module_stop_earning_sheet_title), subtitle = resourceReference( id = R.string.yield_module_stop_earning_sheet_description, formatArgs = wrappedList(cryptoCurrency.symbol), @@ -168,6 +172,17 @@ internal class YieldSupplyStopEarningModel @Inject constructor( blockchain = cryptoCurrency.network.name, ), ) + val event = AnalyticsParam.TxSentFrom.Earning( + blockchain = cryptoCurrency.network.name, + token = cryptoCurrency.symbol, + feeType = AnalyticsParam.FeeType.Normal, + ) + analytics.send( + Basic.TransactionSent( + sentFrom = event, + memoType = Basic.TransactionSent.MemoType.Null, + ), + ) val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value if (address != null) { yieldSupplyDeactivateUseCase(cryptoCurrency, address) @@ -223,7 +238,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor( } }, ifRight = { fee -> - val fee = fee.normal + val fee = fee.normal.increaseGasLimitBy(INCREASE_GAS_LIMIT_FOR_SUPPLY) val feeCryptoValue = fee.amount.value.orZero() uiState.update( diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index b7a7456028..48711f2377 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,7 +5,7 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "develop-1294" +tangemBlockchainSdk = "develop-1301" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-564" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/providers/ProviderTypeIdMapping.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/providers/ProviderTypeIdMapping.kt index cef42efc7c..3c509abd97 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/providers/ProviderTypeIdMapping.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/providers/ProviderTypeIdMapping.kt @@ -26,5 +26,5 @@ internal enum class ProviderTypeIdMapping(val id: String, val providerType: Prov KoinosPro(id = "koinospro", providerType = ProviderType.Koinos.KoinosPro), AlephiumTangem(id = "tangemAlephium", providerType = ProviderType.Alephium.Tangem), Blink(id = "blink", providerType = ProviderType.Blink), - Tatum(id = "tatum", providerType = ProviderType.Kusama.Tatum), + Tatum(id = "tatum", providerType = ProviderType.PolkadotLike.Tatum), } \ No newline at end of file