diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e6d30a7cc6..c939548392 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -228,8 +228,8 @@ dependencies { implementation(deps.armadillo) implementation(deps.mviCore.watcher) implementation(deps.kotlin.serialization) - implementation(deps.walletConnectCore) - implementation(deps.walletConnectWeb3) + implementation(deps.reownCore) + implementation(deps.reownWeb3) implementation(deps.prettyLogger) /** Testing libraries */ diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index cce49feacb..f1aa2f2cbf 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit cce49feacb18dc9ad7cc5da62d58f1b3338a1057 +Subproject commit f1aa2f2cbf374ecba5da753d9f51c9df81749ef2 diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 81b845f7bc..22e43ff3d2 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -3,6 +3,7 @@ package com.tangem.tap import com.tangem.TangemSdkLogger import com.tangem.blockchainsdk.BlockchainSDKFactory import com.tangem.blockchainsdk.signer.TransactionSignerFactory +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager @@ -119,4 +120,6 @@ interface ApplicationEntryPoint { fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles fun getCoroutineDispatcherProvider(): CoroutineDispatcherProvider + + fun getExcludedBlockchains(): ExcludedBlockchains } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 699817b550..61af04c644 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -92,6 +92,7 @@ import kotlinx.coroutines.flow.* import timber.log.Timber import javax.inject.Inject import kotlin.coroutines.CoroutineContext +import kotlin.time.Duration.Companion.seconds lateinit var tangemSdkManager: TangemSdkManager lateinit var backupService: BackupService @@ -330,7 +331,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac private fun installAppTheme() { appThemeModeFlow = createAppThemeModeFlow() - val mode = runBlocking { appThemeModeFlow.first() } + val mode = runBlocking { + withTimeoutOrNull(APP_THEME_LOAD_TIMEOUT.seconds) { + appThemeModeFlow.first() + } ?: AppThemeMode.DEFAULT + } updateAppTheme(mode) } @@ -599,4 +604,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac .onRight { Timber.d("Submitting hashes succeeded") } } } + + companion object { + private const val APP_THEME_LOAD_TIMEOUT = 2 + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index ddb2c9f1bc..0cb1183a0c 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -9,6 +9,7 @@ import com.tangem.TangemSdkLogger import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.blockchainsdk.BlockchainSDKFactory import com.tangem.blockchainsdk.signer.TransactionSignerFactory +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.api.ParamsInterceptor @@ -189,6 +190,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val dispatchers: CoroutineDispatcherProvider get() = entryPoint.getCoroutineDispatcherProvider() + + private val excludedBlockchains: ExcludedBlockchains + get() = entryPoint.getExcludedBlockchains() // endregion override fun onCreate() { @@ -269,6 +273,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { onrampFeatureToggles = onrampFeatureToggles, environmentConfigStorage = environmentConfigStorage, onboardingV2FeatureToggles = onboardingV2FeatureToggles, + excludedBlockchains = excludedBlockchains, ), ), ) 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 646c003c7f..f6252946fb 100644 --- a/app/src/main/java/com/tangem/tap/di/ActivityModule.kt +++ b/app/src/main/java/com/tangem/tap/di/ActivityModule.kt @@ -1,5 +1,6 @@ package com.tangem.tap.di +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.datasource.exchangeservice.swap.SwapServiceLoader import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository @@ -49,6 +50,7 @@ internal object ActivityModule { swapServiceLoader: SwapServiceLoader, currenciesRepository: CurrenciesRepository, getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, + excludedBlockchains: ExcludedBlockchains, dispatchers: CoroutineDispatcherProvider, ): RampStateManager { return DefaultRampManager( @@ -58,6 +60,7 @@ internal object ActivityModule { swapServiceLoader = swapServiceLoader, currenciesRepository = currenciesRepository, getNetworkCoinStatusUseCase = getNetworkCoinStatusUseCase, + excludedBlockchains = excludedBlockchains, dispatchers = dispatchers, ) } diff --git a/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt b/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt index 66d63389c9..34411415bf 100644 --- a/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt +++ b/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt @@ -1,5 +1,6 @@ package com.tangem.tap.di.data +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.sdk.api.TangemSdkManager @@ -20,8 +21,9 @@ internal object CardDataModule { fun providesDerivationsRepository( tangemSdkManager: TangemSdkManager, userWalletsStore: UserWalletsStore, + excludedBlockchains: ExcludedBlockchains, dispatchers: CoroutineDispatcherProvider, ): DerivationsRepository { - return DefaultDerivationsRepository(tangemSdkManager, userWalletsStore, dispatchers) + return DefaultDerivationsRepository(tangemSdkManager, userWalletsStore, excludedBlockchains, dispatchers) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt index 45cc75e9da..f9e14a3a11 100644 --- a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt @@ -1,6 +1,7 @@ package com.tangem.tap.domain.card import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve @@ -32,6 +33,7 @@ private typealias DerivedKeys = Map internal class DefaultDerivationsRepository( private val tangemSdkManager: TangemSdkManager, private val userWalletsStore: UserWalletsStore, + private val excludedBlockchains: ExcludedBlockchains, private val dispatchers: CoroutineDispatcherProvider, ) : DerivationsRepository { @@ -49,6 +51,7 @@ internal class DefaultDerivationsRepository( blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null, extraDerivationPath = null, scanResponse = userWallet.scanResponse, + excludedBlockchains = excludedBlockchains, ) }, ) @@ -85,6 +88,7 @@ internal class DefaultDerivationsRepository( blockchain = Blockchain.fromNetworkId(backendId) ?: return@mapNotNull null, extraDerivationPath = extraDerivationPath, scanResponse = userWallet.scanResponse, + excludedBlockchains = excludedBlockchains, ) }, ) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt index bcf6ba147c..776836a294 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt @@ -2,6 +2,11 @@ package com.tangem.tap.domain.walletconnect2.data import android.app.Application import arrow.core.flatten +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.android.relay.ConnectionType +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.tap.common.analytics.events.WalletConnect import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository @@ -9,11 +14,6 @@ import com.tangem.tap.domain.walletconnect2.domain.WcJrpcMethods import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer import com.tangem.tap.domain.walletconnect2.domain.WcRequest import com.tangem.tap.domain.walletconnect2.domain.models.* -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -72,7 +72,7 @@ internal class DefaultLegacyWalletConnectRepository( } } - Web3Wallet.initialize(Wallet.Params.Init(core = CoreClient)) { error -> + WalletKit.initialize(Wallet.Params.Init(core = CoreClient)) { error -> Timber.e("Error while initializing Web3Wallet: $error") scope.launch { _events.emit( @@ -84,11 +84,11 @@ internal class DefaultLegacyWalletConnectRepository( } val walletDelegate = defineWalletDelegate() - Web3Wallet.setWalletDelegate(walletDelegate) + WalletKit.setWalletDelegate(walletDelegate) } - private fun defineWalletDelegate(): Web3Wallet.WalletDelegate { - return object : Web3Wallet.WalletDelegate { + private fun defineWalletDelegate(): WalletKit.WalletDelegate { + return object : WalletKit.WalletDelegate { override fun onSessionProposal( sessionProposal: Wallet.Model.SessionProposal, verifyContext: Wallet.Model.VerifyContext, @@ -188,14 +188,6 @@ internal class DefaultLegacyWalletConnectRepository( } } - override fun onAuthRequest( - authRequest: Wallet.Model.AuthRequest, - verifyContext: Wallet.Model.VerifyContext, - ) { - // Triggered when Dapp / Requester makes an authorization request - Timber.i("onAuthRequest: $authRequest") - } - override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) { // Triggered when the session is deleted by the peer if (sessionDelete is Wallet.Model.SessionDelete.Success) { @@ -251,7 +243,7 @@ internal class DefaultLegacyWalletConnectRepository( } override fun pair(uri: String) { - Web3Wallet.pair( + WalletKit.pair( params = Wallet.Params.Pair(uri), onSuccess = { Timber.i("Paired successfully: $it") @@ -303,7 +295,7 @@ internal class DefaultLegacyWalletConnectRepository( Timber.i("Session approval is prepared for sending: $sessionApproval") - Web3Wallet.approveSession( + WalletKit.approveSession( params = sessionApproval, onSuccess = { Timber.i("Approved successfully: $it") @@ -364,7 +356,7 @@ internal class DefaultLegacyWalletConnectRepository( ) } - Web3Wallet.respondSessionRequest( + WalletKit.respondSessionRequest( params = Wallet.Params.SessionRequestResponse( sessionTopic = requestData.topic, jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcResult( @@ -409,7 +401,7 @@ internal class DefaultLegacyWalletConnectRepository( } override fun cancelRequest(topic: String, id: Long, message: String) { - Web3Wallet.respondSessionRequest( + WalletKit.respondSessionRequest( params = Wallet.Params.SessionRequestResponse( sessionTopic = topic, jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcError( @@ -424,7 +416,7 @@ internal class DefaultLegacyWalletConnectRepository( } override fun reject() { - Web3Wallet.rejectSession( + WalletKit.rejectSession( params = Wallet.Params.SessionReject( proposerPublicKey = sessionProposal?.proposerPublicKey ?: "", reason = "", @@ -440,7 +432,7 @@ internal class DefaultLegacyWalletConnectRepository( override fun disconnect(topic: String) { val session = currentSessions.find { it.topic == topic } - Web3Wallet.disconnectSession( + WalletKit.disconnectSession( params = Wallet.Params.SessionDisconnect(topic), onSuccess = { analyticsHandler.send( @@ -459,7 +451,7 @@ internal class DefaultLegacyWalletConnectRepository( } fun send(topic: String, id: Long, data: String) { - Web3Wallet.respondSessionRequest( + WalletKit.respondSessionRequest( params = Wallet.Params.SessionRequestResponse( sessionTopic = topic, jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcResult( @@ -477,7 +469,7 @@ internal class DefaultLegacyWalletConnectRepository( } private fun updateSessionsInternal(): Job = scope.launch { - val availableSessions = Web3Wallet.getListOfActiveSessions() + val availableSessions = WalletKit.getListOfActiveSessions() .map { WalletConnectSession( topic = it.topic, diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt index 367d7b6a9e..04bab1fa69 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WcSessionRequestConverter.kt @@ -163,7 +163,9 @@ internal class WcSessionRequestConverter( return sessionsRepository.loadSessions(userWalletId) .firstOrNull { it.topic == sessionRequest.topic } ?.accounts?.firstOrNull { - it.chainId == sessionRequest.chainId && it.walletAddress.lowercase() == walletAddress?.lowercase() + // if walletAddress == null take first account + it.chainId == sessionRequest.chainId && + (walletAddress == null || it.walletAddress.lowercase() == walletAddress.lowercase()) }?.derivationPath } diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/mapper/SolanaTransactionMappings.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/mapper/SolanaTransactionMappings.kt deleted file mode 100644 index c97506bb7e..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/mapper/SolanaTransactionMappings.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.mapper - -import com.tangem.blockchain.blockchains.solana.solanaj.core.SolanaTransaction -import com.tangem.tap.domain.walletconnect2.domain.models.solana.SolanaTransactionRequest -import org.p2p.solanaj.core.AccountMeta -import org.p2p.solanaj.core.PublicKey -import org.p2p.solanaj.core.TransactionInstruction - -internal fun SolanaTransactionRequest.mapToTransaction(): SolanaTransaction { - val from = PublicKey(feePayer) - val instructions = instructions.map(SolanaTransactionRequest.Instruction::mapToInstruction) - - return SolanaTransaction(from) - .apply { - instructions.forEach(::addInstruction) - setRecentBlockHash(recentBlockhash) - } -} - -private fun SolanaTransactionRequest.Instruction.mapToInstruction() = TransactionInstruction( - /* programId = */ PublicKey(programId), - /* keys = */ keys.map(SolanaTransactionRequest.Key::mapToAccountMeta), - /* data = */ data.toByteArray(), -) - -private fun SolanaTransactionRequest.Key.mapToAccountMeta() = AccountMeta( - /* publicKey = */ PublicKey(publicKey), - /* isSigner = */ isSigner, - /* isWritable = */ isWritable, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaTransactionRequest.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaTransactionRequest.kt index b77ecffae0..bed9fd3897 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaTransactionRequest.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/solana/SolanaTransactionRequest.kt @@ -7,39 +7,8 @@ import com.tangem.tap.domain.walletconnect2.domain.WcRequestData @JsonClass(generateAdapter = true) data class SolanaTransactionRequest( @Json(name = "feePayer") - val feePayer: String, - - @Json(name = "recentBlockhash") - val recentBlockhash: String, - - @Json(name = "instructions") - val instructions: List, + val feePayer: String?, @Json(name = "transaction") val transaction: String, -) : WcRequestData { - - @JsonClass(generateAdapter = true) - data class Instruction( - @Json(name = "programId") - val programId: String, - - @Json(name = "data") - val data: String, - - @Json(name = "keys") - val keys: List, - ) - - @JsonClass(generateAdapter = true) - data class Key( - @Json(name = "isSigner") - val isSigner: Boolean, - - @Json(name = "isWritable") - val isWritable: Boolean, - - @Json(name = "pubkey") - val publicKey: String, - ) -} \ No newline at end of file +) : WcRequestData \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 1f56b4bf2f..e9b44c3240 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -53,6 +53,9 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen): DetailsSta store.inject(DaggerGraphState::balanceHidingRepository) .getBalanceHidingSettings().isHidingEnabledInSettings }, + needEnrollBiometrics = runBlocking { + runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() ?: false + }, ), ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt index a21b7a4d75..d985adc26b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/SignTransactionDialog.kt @@ -11,7 +11,8 @@ import com.tangem.wallet.R internal object SignTransactionDialog { fun create(preparedData: WcPreparedRequest.SignTransaction, context: Context): AlertDialog { - val message = context.getString(R.string.wallet_connect_alert_sign_message, "") + val signMessage = context.getString(R.string.wallet_connect_alert_sign_message, "") + val message = "${preparedData.preparedRequestData.dAppName}\n$signMessage" return AlertDialog.Builder(context).apply { setTitle(context.getString(R.string.wallet_connect_title)) setMessage(message) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index e99470b276..7ab791d0b4 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -24,8 +24,8 @@ import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource import com.tangem.tap.features.saveWallet.redux.SaveWalletAction import com.tangem.tap.mainScope @@ -224,7 +224,9 @@ object OnboardingHelper { fun handleTopUpAction(walletManager: WalletManager, scanResponse: ScanResponse, globalState: GlobalState) { val blockchain = walletManager.wallet.blockchain - val cryptoCurrency = CryptoCurrencyFactory().createCoin( + val excludedBlockchains = store.inject(DaggerGraphState::excludedBlockchains) + + val cryptoCurrency = CryptoCurrencyFactory(excludedBlockchains).createCoin( blockchain = blockchain, extraDerivationPath = null, scanResponse = scanResponse, diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CryptoCurrencyConverter.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CryptoCurrencyConverter.kt index 520d262847..8cf25d7f82 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CryptoCurrencyConverter.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CryptoCurrencyConverter.kt @@ -2,6 +2,7 @@ package com.tangem.tap.network.exchangeServices import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.tap.common.extensions.inject @@ -10,9 +11,11 @@ import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import com.tangem.utils.converter.TwoWayConverter -internal class CryptoCurrencyConverter : TwoWayConverter { +internal class CryptoCurrencyConverter( + private val excludedBlockchains: ExcludedBlockchains, +) : TwoWayConverter { - private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory() } + private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory(excludedBlockchains) } override fun convert(value: Currency): CryptoCurrency { return when (value) { diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt index 0186fe0f32..045c5f948e 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt @@ -1,5 +1,6 @@ package com.tangem.tap.network.exchangeServices +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE import com.tangem.datasource.exchangeservice.swap.SwapServiceLoader import com.tangem.domain.exchange.RampStateManager @@ -25,9 +26,10 @@ internal class DefaultRampManager( private val currenciesRepository: CurrenciesRepository, private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, private val dispatchers: CoroutineDispatcherProvider, + excludedBlockchains: ExcludedBlockchains, ) : RampStateManager { - private val cryptoCurrencyConverter = CryptoCurrencyConverter() + private val cryptoCurrencyConverter = CryptoCurrencyConverter(excludedBlockchains) override fun isSellSupportedByService(cryptoCurrency: CryptoCurrency): Boolean { return exchangeService?.availableForSell( diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 075686701f..6827cc546c 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux import com.tangem.blockchainsdk.BlockchainSDKFactory import com.tangem.blockchainsdk.signer.TransactionSignerFactory +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener @@ -71,4 +72,5 @@ data class DaggerGraphState( val onrampFeatureToggles: OnrampFeatureToggles? = null, val environmentConfigStorage: EnvironmentConfigStorage? = null, val onboardingV2FeatureToggles: OnboardingV2FeatureToggles? = null, + val excludedBlockchains: ExcludedBlockchains? = null, ) : StateType \ No newline at end of file diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt index b44074274b..3ff9b7bf0e 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt +++ b/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt @@ -3,6 +3,7 @@ package com.tangem.tap.domain.card import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.FeePaidCurrency import com.tangem.blockchain.common.Token +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.data.common.currency.getNetworkDerivationPath @@ -17,7 +18,7 @@ import com.tangem.domain.tokens.model.Network */ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) { - private val factory = CryptoCurrencyFactory() + private val factory = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains()) val cardano by lazy { listOf(createCoin(blockchain = Blockchain.Cardano)) } val chia by lazy { listOf(element = createCoin(Blockchain.Chia)) } diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt index 93a58d5ba5..095156e1c2 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt +++ b/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt @@ -2,6 +2,7 @@ package com.tangem.tap.domain.card import android.annotation.SuppressLint import com.google.common.truth.Truth +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.common.CompletionResult import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.ScanCardException @@ -29,6 +30,7 @@ internal class DefaultDerivationsRepositoryTest { tangemSdkManager = tangemSdkManager, userWalletsStore = userWalletsStore, dispatchers = TestingCoroutineDispatcherProvider(), + excludedBlockchains = ExcludedBlockchains(), ) private val defaultUserWalletId = UserWalletId("011") diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExpressErrorResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExpressErrorResponse.kt index d3e2fd0694..183965a431 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExpressErrorResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExpressErrorResponse.kt @@ -37,4 +37,10 @@ data class ExpressErrorValue( @Json(name = "expressFromDecimals") val expressFromDecimals: Int?, + + @Json(name = "fromAmount") + val fromAmount: String?, + + @Json(name = "fromAmountProvider") + val fromAmountProvider: String?, ) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt index 9e9b278b7e..9e326cc62c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt @@ -10,6 +10,8 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.emptyPreferences import androidx.datastore.preferences.preferencesDataStoreFile import com.tangem.datasource.local.preferences.PreferencesDataStore.INSTANCE +import com.tangem.datasource.local.preferences.PreferencesKeys.APP_LOGS_KEY +import com.tangem.datasource.local.preferences.utils.CleanupKeyMigration import com.tangem.datasource.local.preferences.utils.SharedPreferencesKeyMigration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob @@ -77,6 +79,7 @@ internal object PreferencesDataStore { legacyKeyName = LEGACY_DEFAULT_KEY_NAME, keyName = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY.name, ), + CleanupKeyMigration(key = APP_LOGS_KEY), ) } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/utils/CleanupKeyMigration.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/utils/CleanupKeyMigration.kt new file mode 100644 index 0000000000..8b5e3fa149 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/utils/CleanupKeyMigration.kt @@ -0,0 +1,18 @@ +package com.tangem.datasource.local.preferences.utils + +import androidx.datastore.core.DataMigration +import androidx.datastore.preferences.core.Preferences + +internal class CleanupKeyMigration(private val key: Preferences.Key) : DataMigration { + + override suspend fun cleanUp() { + // do nothing + } + + override suspend fun shouldMigrate(currentData: Preferences): Boolean = currentData.contains(key) + + override suspend fun migrate(currentData: Preferences): Preferences { + currentData.toMutablePreferences().remove(key) + return currentData + } +} \ No newline at end of file diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 4197e69d71..7d74f429b0 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -891,6 +891,7 @@ Nicht verfügbar zum Kauf Nicht zum Verkauf verfügbar Nicht verfügbar für Tausch von %s + Nicht zum Tausch verfügbar Vertrag: %s Du hast noch keine Transaktionen Der Transaktionsverlauf konnte nicht geladen werden.\nKlicke auf die Schaltfläche Neu laden, um die Informationen zu aktualisieren. diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 5849f7d083..1b346bf8cf 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -882,6 +882,7 @@ 買付できません 売却できません %sからのスワップは利用できません + スワップはできません コントラクト: %s まだ取引はありません 取引履歴の読み込みに失敗しました。\n情報を更新するには、リロードボタンをクリックしてください。 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 88c9836ebf..7b9a25c8cd 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -914,6 +914,7 @@ Недоступно для покупки Недоступно для продажи Недоступно для обмена с %s + Недоступно для обмена контракт: %s У вас еще нет транзакций Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index ef80d3a558..18cf4fecf0 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -898,6 +898,7 @@ Unavailable to purchase Unavailable to sell Unavailable for swap from %s + Unavailable for swap contract: %s You don\'t have any transactions yet Failed to load transaction history.\nClick on reload button to update the information. diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt index b76e11c37c..1a4d6d8dae 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt @@ -1,6 +1,7 @@ package com.tangem.core.ui.components.tokenlist import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -10,6 +11,7 @@ import com.tangem.core.ui.components.tokenlist.internal.GroupTitleItem import com.tangem.core.ui.components.tokenlist.internal.NetworkTitleItem import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme /** * Multi-currency content item @@ -39,5 +41,13 @@ fun TokenListItem(state: TokensListItemUM, isBalanceHidden: Boolean, modifier: M is TokensListItemUM.SearchBar -> { SearchBar(state = state.searchBarUM, modifier = modifier.padding(all = 12.dp)) } + is TokensListItemUM.Text -> { + Text( + text = state.text.resolveReference(), + modifier = modifier.padding(all = 12.dp), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + ) + } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt index 2e9417d5ed..23332310aa 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt @@ -48,4 +48,6 @@ sealed interface TokensListItemUM { data class Token(val state: TokenItemState) : TokensListItemUM { override val id: String = state.id } + + data class Text(override val id: Any, val text: TextReference) : TokensListItemUM } \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt index ff4455dc21..fb8608e78b 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt @@ -1,6 +1,7 @@ package com.tangem.data.common.currency import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.toCoinId import com.tangem.domain.models.scan.ScanResponse @@ -10,7 +11,9 @@ import timber.log.Timber import com.tangem.blockchain.common.Token as SdkToken // FIXME: Make internal -class CryptoCurrencyFactory { +class CryptoCurrencyFactory( + private val excludedBlockchains: ExcludedBlockchains, +) { @Suppress("LongParameterList") // Yep, it's long fun createToken( @@ -46,7 +49,7 @@ class CryptoCurrencyFactory { return null } - val network = getNetwork(blockchain, extraDerivationPath, scanResponse) ?: return null + val network = getNetwork(blockchain, extraDerivationPath, scanResponse, excludedBlockchains) ?: return null val id = getTokenId(network, sdkToken) return CryptoCurrency.Token( @@ -70,7 +73,7 @@ class CryptoCurrencyFactory { Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") return null } - val network = getNetwork(blockchain, extraDerivationPath, scanResponse) ?: return null + val network = getNetwork(blockchain, extraDerivationPath, scanResponse, excludedBlockchains) ?: return null return createCoin(network) } diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt index f181886484..9d2fd810d9 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt @@ -2,6 +2,7 @@ package com.tangem.data.common.currency import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.FeePaidCurrency +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.canHandleToken @@ -19,10 +20,10 @@ fun getNetwork( blockchain: Blockchain, extraDerivationPath: String?, derivationStyleProvider: DerivationStyleProvider?, + excludedBlockchains: ExcludedBlockchains, canHandleTokens: Boolean, ): Network? { - if (blockchain == Blockchain.Unknown) { - Timber.e("Unable to convert Unknown blockchain to the domain network model") + if (!isBlockchainSupported(blockchain, excludedBlockchains)) { return null } @@ -43,10 +44,14 @@ fun getNetwork( ) } -fun getNetwork(networkId: Network.ID, derivationPath: Network.DerivationPath, scanResponse: ScanResponse): Network? { +fun getNetwork( + networkId: Network.ID, + derivationPath: Network.DerivationPath, + scanResponse: ScanResponse, + excludedBlockchains: ExcludedBlockchains, +): Network? { val blockchain = getBlockchain(networkId) - if (blockchain == Blockchain.Unknown) { - Timber.e("Unable to convert Unknown blockchain to the domain network model") + if (!isBlockchainSupported(blockchain, excludedBlockchains)) { return null } @@ -59,16 +64,43 @@ fun getNetwork(networkId: Network.ID, derivationPath: Network.DerivationPath, sc currencySymbol = blockchain.currency, standardType = getNetworkStandardType(blockchain), hasFiatFeeRate = blockchain.feePaidCurrency() !is FeePaidCurrency.FeeResource, - canHandleTokens = scanResponse.card.canHandleToken(blockchain, scanResponse.cardTypesResolver), + canHandleTokens = scanResponse.card.canHandleToken( + blockchain, + scanResponse.cardTypesResolver, + excludedBlockchains, + ), ) } -fun getNetwork(blockchain: Blockchain, extraDerivationPath: String?, scanResponse: ScanResponse): Network? { +private fun isBlockchainSupported(blockchain: Blockchain, excludedBlockchains: ExcludedBlockchains): Boolean { + if (blockchain == Blockchain.Unknown) { + Timber.w("Unable to convert Unknown blockchain to the domain network model") + return false + } + if (blockchain in excludedBlockchains) { + Timber.w("Unable to convert excluded blockchain to the domain network model") + return false + } + + return true +} + +fun getNetwork( + blockchain: Blockchain, + extraDerivationPath: String?, + scanResponse: ScanResponse, + excludedBlockchains: ExcludedBlockchains, +): Network? { return getNetwork( blockchain = blockchain, extraDerivationPath = extraDerivationPath, derivationStyleProvider = scanResponse.derivationStyleProvider, - canHandleTokens = scanResponse.card.canHandleToken(blockchain, scanResponse.cardTypesResolver), + excludedBlockchains = excludedBlockchains, + canHandleTokens = scanResponse.card.canHandleToken( + blockchain, + scanResponse.cardTypesResolver, + excludedBlockchains, + ), ) } diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt index 047150c42e..7cda282ecb 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt @@ -3,6 +3,7 @@ package com.tangem.data.common.currency import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchainsdk.compatibility.l2BlockchainsList +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.toCoinId import com.tangem.datasource.api.tangemTech.models.UserTokensResponse @@ -12,7 +13,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency import timber.log.Timber import com.tangem.blockchain.common.Token as SdkToken -class ResponseCryptoCurrenciesFactory { +class ResponseCryptoCurrenciesFactory( + private val excludedBlockchains: ExcludedBlockchains, +) { fun createCurrency(currencyId: String, response: UserTokensResponse, scanResponse: ScanResponse): CryptoCurrency { return response.tokens @@ -65,7 +68,12 @@ class ResponseCryptoCurrenciesFactory { responseToken: UserTokensResponse.Token, scanResponse: ScanResponse, ): CryptoCurrency.Coin? { - val network = getNetwork(blockchain, responseToken.derivationPath, scanResponse) ?: return null + val network = getNetwork( + blockchain, + responseToken.derivationPath, + scanResponse, + excludedBlockchains, + ) ?: return null return CryptoCurrency.Coin( id = getCoinId(network, blockchain.toCoinId()), @@ -111,8 +119,13 @@ class ResponseCryptoCurrenciesFactory { responseDerivationPath: String?, scanResponse: ScanResponse, ): CryptoCurrency.Token? { - val network = getNetwork(blockchain, responseDerivationPath, scanResponse) - ?: return null + val network = getNetwork( + blockchain, + responseDerivationPath, + scanResponse, + excludedBlockchains, + ) ?: return null + val id = getTokenId(network, sdkToken) return CryptoCurrency.Token( diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt index 3c9aabf80e..ba48dfac71 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt @@ -1,6 +1,7 @@ package com.tangem.data.managetokens import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.data.common.api.safeApiCall @@ -36,10 +37,11 @@ internal class DefaultCustomTokensRepository( private val userWalletsStore: UserWalletsStore, private val appPreferencesStore: AppPreferencesStore, private val walletManagersFacade: WalletManagersFacade, + private val excludedBlockchains: ExcludedBlockchains, private val dispatchers: CoroutineDispatcherProvider, ) : CustomTokensRepository { - private val cryptoCurrencyFactory = CryptoCurrencyFactory() + private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains) private val userTokensResponseFactory = UserTokensResponseFactory() private val tokenAddressConverter = TokenAddressesConverter() private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility() @@ -87,7 +89,9 @@ internal class DefaultCustomTokensRepository( val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "User wallet [$userWalletId] not found while finding token" } - val network = requireNotNull(getNetwork(networkId, derivationPath, userWallet.scanResponse)) { + val network = requireNotNull( + getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains), + ) { "Network [$networkId] not found while finding token" } val tokenAddress = tokenAddressConverter.convertTokenAddress( @@ -97,7 +101,7 @@ internal class DefaultCustomTokensRepository( ) val supportedTokenNetworkIds = userWallet.scanResponse.card - .supportedBlockchains(userWallet.scanResponse.cardTypesResolver) + .supportedBlockchains(userWallet.scanResponse.cardTypesResolver, excludedBlockchains) .filter(Blockchain::canHandleTokens) .map(Blockchain::toNetworkId) @@ -137,7 +141,9 @@ internal class DefaultCustomTokensRepository( val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "User wallet [$userWalletId] not found while creating coin" } - val network = requireNotNull(getNetwork(networkId, derivationPath, userWallet.scanResponse)) { + val network = requireNotNull( + getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains), + ) { "Network [$networkId] not found while creating coin" } @@ -168,7 +174,9 @@ internal class DefaultCustomTokensRepository( val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "User wallet [$userWalletId] not found while creating custom token" } - val network = requireNotNull(getNetwork(networkId, derivationPath, userWallet.scanResponse)) { + val network = requireNotNull( + getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains), + ) { "Network [$networkId] not found while creating custom token" } val tokenAddress = tokenAddressConverter.convertTokenAddress( @@ -230,11 +238,18 @@ internal class DefaultCustomTokensRepository( Blockchain.entries .mapNotNull { blockchain -> - if (scanResponse.card.canHandleBlockchain(blockchain, scanResponse.cardTypesResolver)) { + val canHandleBlockchain = scanResponse.card.canHandleBlockchain( + blockchain, + scanResponse.cardTypesResolver, + excludedBlockchains, + ) + + if (canHandleBlockchain) { getNetwork( blockchain = blockchain, extraDerivationPath = null, scanResponse = scanResponse, + excludedBlockchains = excludedBlockchains, ) } else { null diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt index deb08005ef..6e2f02f44c 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt @@ -2,8 +2,8 @@ package com.tangem.data.managetokens import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.compatibility.l2BlockchainsCoinIds +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId -import com.tangem.blockchainsdk.utils.isSupportedInApp import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.data.common.api.safeApiCall import com.tangem.data.common.currency.UserTokensResponseFactory @@ -47,12 +47,13 @@ internal class DefaultManageTokensRepository( private val manageTokensUpdateFetcher: ManageTokensUpdateFetcher, private val appPreferencesStore: AppPreferencesStore, private val testnetTokensStorage: TestnetTokensStorage, + private val excludedBlockchains: ExcludedBlockchains, private val dispatchers: CoroutineDispatcherProvider, ) : ManageTokensRepository { - private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory() + private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(excludedBlockchains) private val userTokensResponseFactory = UserTokensResponseFactory() - private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(DemoConfig()) + private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(DemoConfig(), excludedBlockchains) // region getTokenListBatchFlow override fun getTokenListBatchFlow( @@ -202,9 +203,9 @@ internal class DefaultManageTokensRepository( private fun getSupportedBlockchains(userWallet: UserWallet?): List { return userWallet?.scanResponse?.let { - it.card.supportedBlockchains(it.cardTypesResolver) + it.card.supportedBlockchains(it.cardTypesResolver, excludedBlockchains) } ?: Blockchain.entries.filter { - !it.isTestnet() && it.isSupportedInApp() + !it.isTestnet() && it !in excludedBlockchains } } // endregion @@ -290,6 +291,7 @@ internal class DefaultManageTokensRepository( val canHandleBlockchain = userWallet.scanResponse.card.canHandleBlockchain( blockchain = blockchain, cardTypesResolver = userWallet.cardTypesResolver, + excludedBlockchains = excludedBlockchains, ) return if (!canHandleBlockchain) { @@ -304,7 +306,10 @@ internal class DefaultManageTokensRepository( blockchain: Blockchain, ): CurrencyUnsupportedState.Token? { val cardTypesResolver = userWallet.scanResponse.cardTypesResolver - val supportedTokens = userWallet.scanResponse.card.supportedTokens(cardTypesResolver) + val supportedTokens = userWallet.scanResponse.card.supportedTokens( + cardTypesResolver, + excludedBlockchains, + ) return when { // refactor this later by moving all this logic in card config diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt index a85db459dd..3f64ae5970 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt @@ -1,5 +1,6 @@ package com.tangem.data.managetokens.di +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.data.managetokens.DefaultCustomTokensRepository import com.tangem.data.managetokens.DefaultManageTokensRepository import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher @@ -30,6 +31,7 @@ internal object ManageTokensDataModule { appPreferencesStore: AppPreferencesStore, testnetTokensStorage: TestnetTokensStorage, dispatchers: CoroutineDispatcherProvider, + excludedBlockchains: ExcludedBlockchains, ): ManageTokensRepository { return DefaultManageTokensRepository( tangemTechApi, @@ -37,6 +39,7 @@ internal object ManageTokensDataModule { manageTokensUpdateFetcher, appPreferencesStore, testnetTokensStorage, + excludedBlockchains, dispatchers, ) } @@ -49,12 +52,14 @@ internal object ManageTokensDataModule { appPreferencesStore: AppPreferencesStore, walletManagersFacade: WalletManagersFacade, dispatchers: CoroutineDispatcherProvider, + excludedBlockchains: ExcludedBlockchains, ): CustomTokensRepository { return DefaultCustomTokensRepository( tangemTechApi, userWalletsStore, appPreferencesStore, walletManagersFacade, + excludedBlockchains, dispatchers, ) } diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt index a5eb111a0d..36475f8fbd 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt @@ -4,8 +4,8 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.compatibility.applyL2Compatibility import com.tangem.blockchainsdk.compatibility.getL2CompatibilityTokenComparison import com.tangem.blockchainsdk.compatibility.l2BlockchainsList +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId -import com.tangem.blockchainsdk.utils.isSupportedInApp import com.tangem.blockchainsdk.utils.toCoinId import com.tangem.data.common.currency.getCoinId import com.tangem.data.common.currency.getNetwork @@ -22,7 +22,9 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.model.Network -internal class ManagedCryptoCurrencyFactory { +internal class ManagedCryptoCurrencyFactory( + private val excludedBlockchains: ExcludedBlockchains, +) { fun create( coinsResponse: CoinsResponse, @@ -88,7 +90,7 @@ internal class ManagedCryptoCurrencyFactory { scanResponse: ScanResponse, ): ManagedCryptoCurrency? { val blockchain = Blockchain.fromNetworkId(token.networkId) - ?.takeIf { it.isSupportedInApp() } + ?.takeUnless { it in excludedBlockchains } ?: return null if (!checkIsCustomToken(token, blockchain, scanResponse.derivationStyleProvider)) { @@ -99,6 +101,7 @@ internal class ManagedCryptoCurrencyFactory { blockchain = blockchain, extraDerivationPath = token.derivationPath, scanResponse = scanResponse, + excludedBlockchains = excludedBlockchains, ) ?: return null val contractAddress = token.contractAddress @@ -161,15 +164,16 @@ internal class ManagedCryptoCurrencyFactory { extraDerivationPath: String? = null, ): SourceNetwork? { val blockchain = Blockchain.fromNetworkId(networkId) - ?.takeIf { it.isSupportedInApp() } + ?.takeUnless { it in excludedBlockchains } ?: return null val network = getNetwork( blockchain, extraDerivationPath, scanResponse?.derivationStyleProvider, + excludedBlockchains, canHandleTokens = scanResponse?.let { - it.card.canHandleToken(blockchain, it.cardTypesResolver) + it.card.canHandleToken(blockchain, it.cardTypesResolver, excludedBlockchains) } ?: true, ) ?: return null @@ -200,13 +204,14 @@ internal class ManagedCryptoCurrencyFactory { .mapNotNullTo(mutableSetOf()) { token -> val blockchain = Blockchain.fromNetworkId(token.networkId) - if (blockchain != null && blockchain.isSupportedInApp()) { + if (blockchain != null && blockchain !in excludedBlockchains) { getNetwork( blockchain, token.derivationPath, scanResponse?.derivationStyleProvider, + excludedBlockchains, canHandleTokens = scanResponse?.let { - it.card.canHandleToken(blockchain, it.cardTypesResolver) + it.card.canHandleToken(blockchain, it.cardTypesResolver, excludedBlockchains) } ?: true, ) } else { diff --git a/data/markets/build.gradle.kts b/data/markets/build.gradle.kts index ba16c78bf0..632a570f78 100644 --- a/data/markets/build.gradle.kts +++ b/data/markets/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation(projects.core.pagination) implementation(projects.core.analytics) implementation(projects.core.analytics.models) + implementation(projects.core.configToggles) implementation(projects.domain.legacy) implementation(projects.domain.markets) diff --git a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt index be0e41c584..61eb79b5e0 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt @@ -3,6 +3,7 @@ package com.tangem.data.markets import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.compatibility.applyL2Compatibility import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.data.common.cache.CacheRegistry @@ -37,10 +38,14 @@ internal class DefaultMarketsTokenRepository( private val userWalletsStore: UserWalletsStore, private val dispatcherProvider: CoroutineDispatcherProvider, private val analyticsEventHandler: AnalyticsEventHandler, + private val excludedBlockchains: ExcludedBlockchains, private val cacheRegistry: CacheRegistry, private val tokenExchangesStore: RuntimeStateStore>, ) : MarketsTokenRepository { + private val tokenMarketInfoConverter: TokenMarketInfoConverter = TokenMarketInfoConverter(excludedBlockchains) + private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains) + private fun createTokenMarketsFetcher(firstBatchSize: Int, nextBatchSize: Int) = LimitOffsetBatchFetcher( prefetchDistance = firstBatchSize, batchSize = nextBatchSize, @@ -204,7 +209,7 @@ internal class DefaultMarketsTokenRepository( } val resultResponse = result.applyL2Compatibility(tokenId) - return@withContext TokenMarketInfoConverter.convert(resultResponse) + return@withContext tokenMarketInfoConverter.convert(resultResponse) } override suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String, tokenSymbol: String) = @@ -234,7 +239,7 @@ internal class DefaultMarketsTokenRepository( val blockchain = Blockchain.fromNetworkId(network.networkId) ?: error("Unknown network [${network.networkId}]") return if (network.contractAddress == null) { - CryptoCurrencyFactory().createCoin( + cryptoCurrencyFactory.createCoin( blockchain = blockchain, extraDerivationPath = null, scanResponse = userWallet.scanResponse, @@ -244,9 +249,10 @@ internal class DefaultMarketsTokenRepository( blockchain = blockchain, extraDerivationPath = null, scanResponse = userWallet.scanResponse, + excludedBlockchains = excludedBlockchains, ) ?: return null - CryptoCurrencyFactory().createToken( + cryptoCurrencyFactory.createToken( network = currencyNetwork, rawId = token.id, name = token.name, diff --git a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt index 85afc94699..2e0146f681 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt @@ -2,8 +2,8 @@ package com.tangem.data.markets.converters import android.net.Uri import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId -import com.tangem.blockchainsdk.utils.isSupportedInApp import com.tangem.datasource.api.markets.models.response.TokenMarketInfoResponse import com.tangem.domain.markets.BuildConfig import com.tangem.domain.markets.TokenMarketInfo @@ -11,10 +11,9 @@ import com.tangem.domain.markets.TokenQuotes import com.tangem.utils.converter.Converter import java.math.BigDecimal -internal object TokenMarketInfoConverter : Converter { - - private const val PROD_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/security_provider/" - private const val DEV_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api.dev/security_provider/" +internal class TokenMarketInfoConverter( + private val excludedBlockchains: ExcludedBlockchains, +) : Converter { override fun convert(value: TokenMarketInfoResponse): TokenMarketInfo { return with(value) { @@ -68,7 +67,8 @@ internal object TokenMarketInfoConverter : Converter { + blockchain != null && blockchain !in excludedBlockchains && + blockchain.canHandleTokens() -> { TokenMarketInfo.Network( networkId = network.networkId, exchangeable = network.exchangeable, @@ -193,4 +193,10 @@ internal object TokenMarketInfoConverter : Converter { val card = scanResponse.card diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt index cf827af989..a5129bbb52 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt @@ -1,7 +1,7 @@ package com.tangem.domain.common.extensions import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchainsdk.utils.isSupportedInApp +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion import com.tangem.domain.common.CardTypesResolver @@ -15,7 +15,10 @@ import com.tangem.domain.models.scan.CardDTO val FirmwareVersion.Companion.SolanaTokensAvailable get() = FirmwareVersion(4, 52) -fun CardDTO.supportedBlockchains(cardTypesResolver: CardTypesResolver): List { +fun CardDTO.supportedBlockchains( + cardTypesResolver: CardTypesResolver, + excludedBlockchains: ExcludedBlockchains, +): List { val supportedBlockchains = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { Blockchain.fromCurve(EllipticCurve.Secp256k1).toMutableList() } else if (!cardTypesResolver.isWallet2() && !cardTypesResolver.isTangemWallet()) { @@ -27,11 +30,14 @@ fun CardDTO.supportedBlockchains(cardTypesResolver: CardTypesResolver): List { - val tokensSupportedByBlockchain = supportedBlockchains(cardTypesResolver) +fun CardDTO.supportedTokens( + cardTypesResolver: CardTypesResolver, + excludedBlockchains: ExcludedBlockchains, +): List { + val tokensSupportedByBlockchain = supportedBlockchains(cardTypesResolver, excludedBlockchains) .filter { it.canHandleTokens() } .toMutableList() val tokensSupportedByCard = when { @@ -68,10 +74,14 @@ fun CardDTO.canHandleToken( } } -fun CardDTO.canHandleToken(blockchain: Blockchain, cardTypesResolver: CardTypesResolver): Boolean { +fun CardDTO.canHandleToken( + blockchain: Blockchain, + cardTypesResolver: CardTypesResolver, + excludedBlockchains: ExcludedBlockchains, +): Boolean { val cardConfig = CardConfig.createConfig(this) val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain) - val isContainsBlockchain = this.supportedTokens(cardTypesResolver).contains(blockchain) + val isContainsBlockchain = blockchain in supportedTokens(cardTypesResolver, excludedBlockchains) val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain } // fixme: check for first wallets with 1 curve and remove condition return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) { @@ -82,10 +92,14 @@ fun CardDTO.canHandleToken(blockchain: Blockchain, cardTypesResolver: CardTypesR } } -fun CardDTO.canHandleBlockchain(blockchain: Blockchain, cardTypesResolver: CardTypesResolver): Boolean { +fun CardDTO.canHandleBlockchain( + blockchain: Blockchain, + cardTypesResolver: CardTypesResolver, + excludedBlockchains: ExcludedBlockchains, +): Boolean { val cardConfig = CardConfig.createConfig(this) val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain) - val isContainsBlockchain = this.supportedBlockchains(cardTypesResolver).contains(blockchain) + val isContainsBlockchain = blockchain in supportedBlockchains(cardTypesResolver, excludedBlockchains) val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain } // fixme: check for first wallets with 1 curve and remove condition return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) { diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt index b6d4827fda..5121d721b4 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt @@ -3,6 +3,7 @@ package com.tangem.features.markets.details.impl.model import androidx.compose.runtime.Stable import arrow.core.Either import arrow.core.getOrElse +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.common.ui.charts.state.MarketChartData import com.tangem.common.ui.charts.state.MarketChartDataProducer import com.tangem.common.ui.charts.state.sorted @@ -64,6 +65,7 @@ internal class MarketsTokenDetailsModel @Inject constructor( private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val urlOpener: UrlOpener, private val analyticsEventHandler: AnalyticsEventHandler, + private val excludedBlockchains: ExcludedBlockchains, ) : Model() { private var quotesJob = JobHolder() @@ -400,7 +402,7 @@ internal class MarketsTokenDetailsModel @Inject constructor( } val networks = newInfo.networks?.filter { - BlockchainUtils.isSupportedNetworkId(it.networkId) + BlockchainUtils.isSupportedNetworkId(it.networkId, excludedBlockchains) } networksState.value = if (networks.isNullOrEmpty()) { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt index 4d639a5c46..a84dd28b38 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt @@ -22,6 +22,7 @@ import com.tangem.features.onramp.swap.availablepairs.AvailableSwapPairsComponen import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetLoadingTokenItemsTransformer import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUMController +import com.tangem.features.onramp.tokenlist.entity.transformer.SetNothingToFoundStateTransformer import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateTokenItemsTransformer import com.tangem.features.onramp.utils.InputManager import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer @@ -80,24 +81,35 @@ internal class AvailableSwapPairsModel @Inject constructor( } else { val (appCurrency, isBalanceHidden) = appCurrencyAndBalanceHiding - val filterTokenList = currencies + val filterByQueryTokenList = currencies .filter { it.currency != selectedStatus?.currency } .filterByQuery(query = query) - .filterByAvailability(availablePairs = availablePairs) - UpdateTokenItemsTransformer( - appCurrency = appCurrency, - onItemClick = params.onTokenClick, - statuses = filterTokenList, - isBalanceHidden = isBalanceHidden, - hasSearchBar = currencies.isNotEmpty(), - unavailableTokensHeaderReference = resourceReference( - id = R.string.tokens_list_unavailable_to_swap_header, - wrappedList(selectedStatus?.currency?.name?.capitalize() ?: ""), - ), - onQueryChange = ::onSearchQueryChange, - onActiveChange = ::onSearchBarActiveChange, - ) + if (query.isNotEmpty() && filterByQueryTokenList.isEmpty()) { + SetNothingToFoundStateTransformer( + isBalanceHidden = isBalanceHidden, + hasSearchBar = currencies.isNotEmpty(), + emptySearchMessageReference = resourceReference( + id = R.string.action_buttons_swap_empty_search_message, + ), + onQueryChange = ::onSearchQueryChange, + onActiveChange = ::onSearchBarActiveChange, + ) + } else { + UpdateTokenItemsTransformer( + appCurrency = appCurrency, + onItemClick = params.onTokenClick, + statuses = filterByQueryTokenList.filterByAvailability(availablePairs = availablePairs), + isBalanceHidden = isBalanceHidden, + hasSearchBar = currencies.isNotEmpty(), + unavailableTokensHeaderReference = resourceReference( + id = R.string.tokens_list_unavailable_to_swap_header, + wrappedList(selectedStatus?.currency?.name?.capitalize() ?: ""), + ), + onQueryChange = ::onSearchQueryChange, + onActiveChange = ::onSearchBarActiveChange, + ) + } } } .onEach(tokenListUMController::update) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/SetNothingToFoundStateTransformer.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/SetNothingToFoundStateTransformer.kt new file mode 100644 index 0000000000..a3ec0c3e4e --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/SetNothingToFoundStateTransformer.kt @@ -0,0 +1,65 @@ +package com.tangem.features.onramp.tokenlist.entity.transformer + +import com.tangem.core.ui.components.fields.entity.SearchBarUM +import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.tokenlist.entity.TokenListUM +import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList + +internal class SetNothingToFoundStateTransformer( + private val isBalanceHidden: Boolean, + private val hasSearchBar: Boolean, + private val emptySearchMessageReference: TextReference, + private val onQueryChange: (String) -> Unit, + private val onActiveChange: (Boolean) -> Unit, +) : TokenListUMTransformer { + + override fun transform(prevState: TokenListUM): TokenListUM { + val searchBarItem = if (hasSearchBar) { + prevState.getSearchBar() ?: createSearchBarItem() + } else { + null + } + + return prevState.copy( + availableItems = buildList { + if (searchBarItem != null) { + add(searchBarItem) + } + + createGroupTitle( + textReference = resourceReference(id = R.string.exchange_tokens_available_tokens_header), + ) + .let(::add) + + TokensListItemUM.Text( + id = emptySearchMessageReference.hashCode(), + text = emptySearchMessageReference, + ).let(::add) + } + .toImmutableList(), + unavailableItems = persistentListOf(), + isBalanceHidden = isBalanceHidden, + ) + } + + private fun createSearchBarItem(): TokensListItemUM.SearchBar { + return TokensListItemUM.SearchBar( + searchBarUM = SearchBarUM( + placeholderText = resourceReference(id = R.string.common_search), + query = "", + onQueryChange = onQueryChange, + isActive = false, + onActiveChange = onActiveChange, + ), + ) + } + + private fun createGroupTitle(textReference: TextReference): TokensListItemUM.GroupTitle { + return TokensListItemUM.GroupTitle(id = textReference.hashCode(), text = textReference) + } +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt index c86d5ec70f..4df19b9727 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt @@ -4,7 +4,6 @@ import arrow.core.getOrElse import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -19,6 +18,7 @@ import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent import com.tangem.features.onramp.tokenlist.entity.OnrampOperation import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUMController +import com.tangem.features.onramp.tokenlist.entity.transformer.SetNothingToFoundStateTransformer import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateTokenItemsTransformer import com.tangem.features.onramp.utils.InputManager import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer @@ -65,30 +65,38 @@ internal class OnrampTokenListModel @Inject constructor( ) .flattenCurrencies() - val filterTokenList = currencies + val filterByQueryTokenList = currencies .filterByQuery(query = query) - .filterByAvailability() - UpdateTokenItemsTransformer( - appCurrency = appCurrency, - onItemClick = params.onTokenClick, - statuses = filterTokenList, - isBalanceHidden = isBalanceHidden, - hasSearchBar = params.hasSearchBar && currencies.isNotEmpty(), - unavailableTokensHeaderReference = when (params.filterOperation) { - OnrampOperation.BUY -> resourceReference(id = R.string.tokens_list_unavailable_to_purchase_header) - OnrampOperation.SELL -> resourceReference(id = R.string.tokens_list_unavailable_to_sell_header) - OnrampOperation.SWAP -> { - // TODO: [REDACTED_JIRA] - resourceReference( - id = R.string.tokens_list_unavailable_to_swap_header, - wrappedList(""), - ) + if (query.isNotEmpty() && filterByQueryTokenList.isEmpty()) { + SetNothingToFoundStateTransformer( + isBalanceHidden = isBalanceHidden, + hasSearchBar = params.hasSearchBar && currencies.isNotEmpty(), + emptySearchMessageReference = when (params.filterOperation) { + OnrampOperation.BUY -> R.string.action_buttons_buy_empty_search_message + OnrampOperation.SELL -> R.string.action_buttons_sell_empty_search_message + OnrampOperation.SWAP -> R.string.action_buttons_swap_empty_search_message } - }, - onQueryChange = ::onSearchQueryChange, - onActiveChange = ::onSearchBarActiveChange, - ) + .let(::resourceReference), + onQueryChange = ::onSearchQueryChange, + onActiveChange = ::onSearchBarActiveChange, + ) + } else { + UpdateTokenItemsTransformer( + appCurrency = appCurrency, + onItemClick = params.onTokenClick, + statuses = filterByQueryTokenList.filterByAvailability(), + isBalanceHidden = isBalanceHidden, + hasSearchBar = params.hasSearchBar && currencies.isNotEmpty(), + unavailableTokensHeaderReference = when (params.filterOperation) { + OnrampOperation.BUY -> R.string.tokens_list_unavailable_to_purchase_header + OnrampOperation.SELL -> R.string.tokens_list_unavailable_to_sell_header + OnrampOperation.SWAP -> R.string.tokens_list_unavailable_to_swap_source_header + }.let(::resourceReference), + onQueryChange = ::onSearchQueryChange, + onActiveChange = ::onSearchBarActiveChange, + ) + } } .onEach(tokenListUMController::update) .flowOn(dispatchers.main) diff --git a/features/qr-scanning/impl/build.gradle.kts b/features/qr-scanning/impl/build.gradle.kts index f2dfb02aa4..87bb1cc68e 100644 --- a/features/qr-scanning/impl/build.gradle.kts +++ b/features/qr-scanning/impl/build.gradle.kts @@ -47,6 +47,9 @@ dependencies { implementation(projects.domain.qrScanning) implementation(projects.domain.qrScanning.models) + /** Data */ + implementation(projects.data.card) + /** Compose */ implementation(deps.compose.foundation) implementation(deps.compose.ui) @@ -57,4 +60,5 @@ dependencies { /** Other dependencies */ implementation(deps.arrow.core) implementation(deps.timber) + implementation(deps.tangem.card.core) } \ No newline at end of file diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt index 49b23ff8cd..a504e00e9f 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.common.routing.AppRoute +import com.tangem.data.card.sdk.CardSdkProvider import com.tangem.domain.qrscanning.models.SourceType import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter import com.tangem.feature.qrscanning.presentation.QrScanningState @@ -20,6 +21,7 @@ import javax.inject.Inject internal class QrScanningViewModel @Inject constructor( private val stateHolder: QrScanningStateController, private val clickIntents: QrScanningClickIntentsImplementor, + private val cardSdkProvider: CardSdkProvider, savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -31,6 +33,12 @@ internal class QrScanningViewModel @Inject constructor( val uiState: StateFlow = stateHolder.uiState val launchGalleryEvent: SharedFlow = clickIntents.launchGallery + init { + // samsung for some reason disables reader mode, and then it works unstable + // to prevent this disable ir manually before scan QR + cardSdkProvider.sdk.forceDisableReaderMode() + } + fun setRouter(router: QrScanningInnerRouter) { clickIntents.initialize( router = router, @@ -49,4 +57,10 @@ internal class QrScanningViewModel @Inject constructor( fun onDismissBottomSheetState() { stateHolder.update(DismissBottomSheetTransformer()) } + + override fun onCleared() { + super.onCleared() + // don't forget enable reader mode after scan complete + cardSdkProvider.sdk.forceEnableReaderMode() + } } \ No newline at end of file diff --git a/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt b/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt index c0c257c1c9..ae6768f793 100644 --- a/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt +++ b/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt @@ -2,6 +2,7 @@ package com.tangem.feature.referral.data import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.datasource.api.tangemTech.TangemTechApi @@ -18,14 +19,18 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import javax.inject.Inject +@Suppress("LongParameterList") internal class ReferralRepositoryImpl @Inject constructor( private val referralApi: TangemTechApi, private val referralConverter: ReferralConverter, private val coroutineDispatcher: CoroutineDispatcherProvider, private val demoModeDatasource: DemoModeDatasource, private val userWalletsStore: UserWalletsStore, + excludedBlockchains: ExcludedBlockchains, ) : ReferralRepository { + private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains) + override val isDemoMode: Boolean get() = demoModeDatasource.isDemoModeActive @@ -65,8 +70,6 @@ internal class ReferralRepositoryImpl @Inject constructor( val blockchain = Blockchain.fromNetworkId(tokenData.networkId) ?: error("Blockchain ${tokenData.networkId} not found") - val cryptoCurrencyFactory = CryptoCurrencyFactory() - val contractAddress = tokenData.contractAddress val decimalCount = tokenData.decimalCount return if (contractAddress != null && decimalCount != null) { diff --git a/features/referral/data/src/main/java/com/tangem/feature/referral/di/ReferralRepositoryModule.kt b/features/referral/data/src/main/java/com/tangem/feature/referral/di/ReferralRepositoryModule.kt index 6c0f5e9c47..7d01957033 100644 --- a/features/referral/data/src/main/java/com/tangem/feature/referral/di/ReferralRepositoryModule.kt +++ b/features/referral/data/src/main/java/com/tangem/feature/referral/di/ReferralRepositoryModule.kt @@ -1,5 +1,6 @@ package com.tangem.feature.referral.di +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.demo.DemoModeDatasource import com.tangem.datasource.local.userwallet.UserWalletsStore @@ -25,6 +26,7 @@ class ReferralRepositoryModule { coroutineDispatcherProvider: CoroutineDispatcherProvider, demoModeDatasource: DemoModeDatasource, userWalletsStore: UserWalletsStore, + excludedBlockchains: ExcludedBlockchains, ): ReferralRepository { return ReferralRepositoryImpl( referralApi = tangemTechApi, @@ -32,6 +34,7 @@ class ReferralRepositoryModule { coroutineDispatcher = coroutineDispatcherProvider, demoModeDatasource = demoModeDatasource, userWalletsStore = userWalletsStore, + excludedBlockchains = excludedBlockchains, ) } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt index 36229ac7ff..e66c38a778 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt @@ -75,6 +75,7 @@ internal class StakingStateController @Inject constructor( walletName = "", cryptoCurrencyName = "", cryptoCurrencySymbol = "", + cryptoCurrencyBlockchainId = "", currentStep = StakingStep.InitialInfo, initialInfoState = StakingStates.InitialInfoState.Empty(), amountState = AmountState.Empty(), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt index 5e717bb916..d7e8d24c25 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt @@ -5,6 +5,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.staking.analytics.StakingAnalyticsEvent import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender +import com.tangem.lib.crypto.BlockchainUtils.isSolana internal class StakingStateRouter( private val appRouter: AppRouter, @@ -23,7 +24,13 @@ internal class StakingStateRouter( fun onNextClick() { when (stateController.value.currentStep) { StakingStep.InitialInfo -> when (stateController.value.actionType) { - StakingActionCommonType.Enter, StakingActionCommonType.Exit -> showAmount() + StakingActionCommonType.Enter -> showAmount() + // TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing + StakingActionCommonType.Exit -> if (isSolana(stateController.value.cryptoCurrencyBlockchainId)) { + showConfirmation() + } else { + showAmount() + } StakingActionCommonType.Pending.Other, StakingActionCommonType.Pending.Rewards, -> showConfirmation() @@ -50,7 +57,9 @@ internal class StakingStateRouter( val isEnter = uiState.actionType == StakingActionCommonType.Enter val isExit = uiState.actionType == StakingActionCommonType.Exit - if (isEnter || isExit) { + // TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing + val isSolana = isSolana(uiState.cryptoCurrencyBlockchainId) + if (isEnter || isExit && !isSolana) { showAmount() } else { showInitial() diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt index f2f28dd8af..c8c0be08e6 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt @@ -29,6 +29,7 @@ internal data class StakingUiState( val walletName: String, val cryptoCurrencyName: String, val cryptoCurrencySymbol: String, + val cryptoCurrencyBlockchainId: String, val currentStep: StakingStep, val initialInfoState: StakingStates.InitialInfoState, val amountState: AmountState, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt index 9d819bcbf8..6825aeb4f6 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt @@ -64,6 +64,7 @@ internal class SetInitialDataStateTransformer( title = TextReference.EMPTY, cryptoCurrencyName = cryptoCurrency.name, cryptoCurrencySymbol = cryptoCurrency.symbol, + cryptoCurrencyBlockchainId = cryptoCurrency.network.id.value, clickIntents = clickIntents, currentStep = StakingStep.InitialInfo, initialInfoState = createInitialInfoState(), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt index d36ad76e77..2df8cdd525 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt @@ -40,9 +40,10 @@ internal fun StakingConfirmationContent( validatorState: StakingStates.ValidatorState, clickIntents: StakingClickIntents, type: StakingActionCommonType, + isSolana: Boolean, // TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing ) { if (state !is StakingStates.ConfirmationState.Data) return - val isAmountEditable = type == StakingActionCommonType.Enter || type == StakingActionCommonType.Exit + val isAmountEditable = type == StakingActionCommonType.Enter || type == StakingActionCommonType.Exit && !isSolana val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED val isTransactionInProgress = state.notifications.any { it is StakingNotification.Warning.TransactionInProgress } Column( @@ -90,6 +91,7 @@ private fun Preview_StakingConfirmationContent() { validatorState = ValidatorStatePreviewData.validatorState, clickIntents = StakingClickIntentsStub, type = StakingActionCommonType.Enter, + isSolana = false, ) } } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt index 21be700364..8e6b03d04c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt @@ -28,6 +28,7 @@ import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingAc import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingInfoBottomSheetConfig import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingActionSelectorBottomSheet import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingInfoBottomSheet +import com.tangem.lib.crypto.BlockchainUtils.isSolana import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -170,6 +171,7 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M validatorState = uiState.validatorState, clickIntents = uiState.clickIntents, type = uiState.actionType, + isSolana = isSolana(uiState.cryptoCurrencyBlockchainId), ) StakingStep.RestakeValidator, StakingStep.Validators, diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index 0548a65ea7..8e465b9c2b 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -7,6 +7,7 @@ import arrow.core.raise.either import arrow.core.right import com.squareup.moshi.Moshi import com.tangem.blockchain.common.* +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.datasource.api.common.response.ApiResponse @@ -48,12 +49,13 @@ internal class DefaultSwapRepository( private val errorsDataConverter: ErrorsDataConverter, private val dataSignatureVerifier: DataSignatureVerifier, moshi: Moshi, + excludedBlockchains: ExcludedBlockchains, ) : SwapRepository { private val expressDataConverter = ExpressDataConverter() private val leastTokenInfoConverter = LeastTokenInfoConverter() private val swapPairInfoConverter = SwapPairInfoConverter() - private val cryptoCurrencyFactory = CryptoCurrencyFactory() + private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains) private val exchangeStatusConverter = ExchangeStatusConverter() private val txDetailsMoshiAdapter = moshi.adapter(TxDetails::class.java) diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt index 9b44aacbef..ed7aa550c5 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt @@ -1,5 +1,6 @@ package com.tangem.feature.swap +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectList @@ -18,12 +19,13 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext -class DefaultSwapTransactionRepository( +internal class DefaultSwapTransactionRepository( private val appPreferencesStore: AppPreferencesStore, private val dispatchers: CoroutineDispatcherProvider, + excludedBlockchains: ExcludedBlockchains, ) : SwapTransactionRepository { - private val converter = SavedSwapTransactionListConverter() + private val converter = SavedSwapTransactionListConverter(excludedBlockchains) override suspend fun storeTransaction( userWalletId: UserWalletId, diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt index 9fc5d1725d..2df4aa2f11 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt @@ -30,6 +30,7 @@ internal class ErrorsDataConverter( 2270 -> ExpressDataError.ExchangeNotEnoughBalanceError(code = error.code) 2280 -> ExpressDataError.ExchangeInvalidAddressError(code = error.code) 2290 -> tryParseExchangeInvalidFromDecimalsError(error = error) + 2320 -> tryParseProviderDifferentAmountError(error = error) else -> ExpressDataError.UnknownErrorWithCode(error.code) } } catch (e: Exception) { @@ -79,4 +80,20 @@ internal class ErrorsDataConverter( expressFromDecimals = expressFromDecimals, ) } + + private fun tryParseProviderDifferentAmountError(error: ExpressError): ExpressDataError { + val decimals = error.value?.decimals ?: return ExpressDataError.UnknownErrorWithCode(error.code) + + val fromAmount = error.value?.fromAmount?.toBigDecimalOrNull() + ?: return ExpressDataError.UnknownErrorWithCode(error.code) + val fromAmountProvider = error.value?.fromAmountProvider?.toBigDecimalOrNull() + ?: return ExpressDataError.UnknownErrorWithCode(error.code) + + return ExpressDataError.ProviderDifferentAmountError( + code = error.code, + decimals = decimals, + fromAmount = fromAmount.movePointLeft(decimals), + fromProviderAmount = fromAmountProvider.movePointLeft(decimals), + ) + } } \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt index 33112a9591..d3d6093b01 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt @@ -1,5 +1,6 @@ package com.tangem.feature.swap.converters +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory import com.tangem.data.common.currency.UserTokensResponseFactory import com.tangem.domain.models.scan.ScanResponse @@ -11,10 +12,11 @@ import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListMode import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel import com.tangem.utils.converter.Converter -class SavedSwapTransactionListConverter : - Converter { +internal class SavedSwapTransactionListConverter( + excludedBlockchains: ExcludedBlockchains, +) : Converter { - private val responseCryptoCurrenciesFactory = ResponseCryptoCurrenciesFactory() + private val responseCryptoCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains) private val userTokensResponseFactory = UserTokensResponseFactory() override fun convert(value: SavedSwapTransactionListModel) = SavedSwapTransactionListModelInner( diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt index fde540c8ab..5a1b3dcf85 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt @@ -1,6 +1,7 @@ package com.tangem.feature.swap.di import com.squareup.moshi.Moshi +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.datasource.api.express.TangemExpressApi import com.tangem.datasource.api.express.models.response.ExpressErrorResponse import com.tangem.datasource.crypto.DataSignatureVerifier @@ -34,6 +35,7 @@ internal class SwapDataModule { userWalletsListManager: UserWalletsListManager, errorsDataConverter: ErrorsDataConverter, @NetworkMoshi moshi: Moshi, + excludedBlockchains: ExcludedBlockchains, ): SwapRepository { return DefaultSwapRepository( tangemExpressApi = tangemExpressApi, @@ -43,6 +45,7 @@ internal class SwapDataModule { errorsDataConverter = errorsDataConverter, dataSignatureVerifier = dataSignature, moshi = moshi, + excludedBlockchains = excludedBlockchains, ) } @@ -51,10 +54,12 @@ internal class SwapDataModule { fun provideSwapTransactionRepository( appPreferencesStore: AppPreferencesStore, dispatcherProvider: CoroutineDispatcherProvider, + excludedBlockchains: ExcludedBlockchains, ): SwapTransactionRepository { return DefaultSwapTransactionRepository( appPreferencesStore = appPreferencesStore, dispatchers = dispatcherProvider, + excludedBlockchains = excludedBlockchains, ) } diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressDataError.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressDataError.kt index b2ac31292d..1446dd81b2 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressDataError.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressDataError.kt @@ -39,6 +39,13 @@ sealed class ExpressDataError { val expressFromDecimals: Int, ) : ExpressDataError() + data class ProviderDifferentAmountError( + override val code: Int, + val fromAmount: BigDecimal, + val fromProviderAmount: BigDecimal, + val decimals: Int, + ) : ExpressDataError() + data class UnknownErrorWithCode(override val code: Int) : ExpressDataError() data class InvalidSignatureError(override val code: Int = 990) : ExpressDataError() diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index b5e80ff692..cc94ac10b4 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -10,10 +10,7 @@ import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.* -import com.tangem.core.ui.format.bigdecimal.anyDecimals -import com.tangem.core.ui.format.bigdecimal.crypto -import com.tangem.core.ui.format.bigdecimal.format -import com.tangem.core.ui.format.bigdecimal.uncapped +import com.tangem.core.ui.format.bigdecimal.* import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency @@ -781,6 +778,19 @@ internal class StateBuilder( iconResId = R.drawable.ic_alert_circle_24, ), ) + is ExpressDataError.ProviderDifferentAmountError -> SwapWarning.GeneralError( + notificationConfig = NotificationConfig( + title = resourceReference(id = R.string.common_error), + subtitle = resourceReference( + R.string.express_error_provider_amount_roundup, + formatArgs = wrappedList( + expressDataError.code, + expressDataError.fromProviderAmount.format { simple(decimals = expressDataError.decimals) }, + ), + ), + iconResId = R.drawable.ic_alert_circle_24, + ), + ) else -> SwapWarning.GeneralWarning( notificationConfig = NotificationConfig( title = providerErrorTitle, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt index 44c918c906..a0ef72bd20 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt @@ -28,6 +28,13 @@ internal fun getExpressErrorMessage(expressDataError: ExpressDataError): TextRef id = R.string.express_error_swap_pair_unavailable, formatArgs = wrappedList(expressDataError.code), ) + is ExpressDataError.ProviderDifferentAmountError -> resourceReference( + R.string.express_error_provider_amount_roundup, + formatArgs = wrappedList( + expressDataError.code, + expressDataError.fromProviderAmount.format { simple(decimals = expressDataError.decimals) }, + ), + ) else -> resourceReference(R.string.express_error_code, wrappedList(expressDataError.code.toString())) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt index 81e435fa5e..21f40750f2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt @@ -9,6 +9,7 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents @@ -25,6 +26,7 @@ internal class GetSingleWalletWarningsFactory @Inject constructor( private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, private val isNeedToBackupUseCase: IsNeedToBackupUseCase, private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase, + private val getWalletsUseCase: GetWalletsUseCase, ) { private var readyForRateAppNotification = false @@ -36,22 +38,32 @@ internal class GetSingleWalletWarningsFactory @Inject constructor( flow = getPrimaryCurrencyStatusUpdatesUseCase(userWallet.walletId), flow2 = isReadyToShowRateAppUseCase().conflate(), flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(), - ) { primaryCurrencyStatus, isReadyToShowRating, isNeedToBackup -> + flow4 = getWalletsUseCase().conflate(), + ) { maybePrimaryCurrencyStatus, isReadyToShowRating, isNeedToBackup, userWallets -> readyForRateAppNotification = true buildList { - addCriticalNotifications(cardTypesResolver) - - addInformationalNotifications(cardTypesResolver, clickIntents) - - addWarningNotifications( - userWallet, - cardTypesResolver, - primaryCurrencyStatus, - isNeedToBackup, - clickIntents, + addCriticalNotifications( + cardTypesResolver = cardTypesResolver, ) - addRateTheAppNotification(isReadyToShowRating, clickIntents) + addInformationalNotifications( + userWallets = userWallets, + cardTypesResolver = cardTypesResolver, + clickIntents = clickIntents, + ) + + addWarningNotifications( + userWallet = userWallet, + cardTypesResolver = cardTypesResolver, + maybePrimaryCurrencyStatus = maybePrimaryCurrencyStatus, + isNeedToBackup = isNeedToBackup, + clickIntents = clickIntents, + ) + + addRateTheAppNotification( + isReadyToShowRating = isReadyToShowRating, + clickIntents = clickIntents, + ) }.toImmutableList() } } @@ -76,14 +88,19 @@ internal class GetSingleWalletWarningsFactory @Inject constructor( } private fun MutableList.addInformationalNotifications( + userWallets: List, cardTypesResolver: CardTypesResolver, clickIntents: WalletClickIntents, ) { + val userHasWalletOrWallet2 = userWallets.any { + val typesResolver = it.scanResponse.cardTypesResolver + typesResolver.isTangemWallet() || typesResolver.isWallet2() + } addIf( element = WalletNotification.NoteMigration( onClick = { clickIntents.onNoteMigrationButtonClick(NOTE_MIGRATION_URL) }, ), - condition = cardTypesResolver.isTangemNote(), + condition = cardTypesResolver.isTangemNote() && !userHasWalletOrWallet2, ) addIf( diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 95d47dd076..42c41b11d8 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -70,8 +70,8 @@ zxingQrCode = "3.5.1" mviCore = "1.3.1" kotlinSerialization = "1.4.1" arrow = "1.2.3" -walletConnectCore = "1.35.2" -walletConnectWeb3 = "1.35.2" +reownCore = "1.0.2" +reownWeb3 = "1.0.2" prettyLogger = "2.2.0" okHttp-prettyLogging = "3.1.0" chucker = "4.0.0" @@ -88,9 +88,9 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-871" +tangemBlockchainSdk = "develop-875" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-403" +tangemCardSdk = "develop-410" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ @@ -251,8 +251,8 @@ mviCore-watcher = { module = "com.github.badoo.mvicore:mvicore-diff", version.re kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" } arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } arrow-fx = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" } -walletConnectCore = { module = "com.walletconnect:android-core", version.ref = "walletConnectCore" } -walletConnectWeb3 = { module = "com.walletconnect:web3wallet", version.ref = "walletConnectWeb3" } +reownCore = { module = "com.reown:android-core", version.ref = "reownCore" } +reownWeb3 = { module = "com.reown:walletkit", version.ref = "reownWeb3" } prettyLogger = { module = "com.orhanobut:logger", version.ref = "prettyLogger" } chucker = { module = "com.github.chuckerteam.chucker:library", version.ref = "chucker" } chuckerStub = { module = "com.github.chuckerteam.chucker:library-no-op", version.ref = "chucker" } diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/di/ExcludedBlockchainsModule.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/di/ExcludedBlockchainsModule.kt new file mode 100644 index 0000000000..c63d1dd071 --- /dev/null +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/di/ExcludedBlockchainsModule.kt @@ -0,0 +1,20 @@ +package com.tangem.blockchainsdk.di + +import com.tangem.blockchainsdk.utils.ExcludedBlockchains +import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object ExcludedBlockchainsModule { + + @Provides + @Singleton + fun bindExcludedBlockchains(excludedBlockchainsManager: ExcludedBlockchainsManager): ExcludedBlockchains { + return ExcludedBlockchains(excludedBlockchainsManager) + } +} \ No newline at end of file diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt index a9830d7036..2d32f24015 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt @@ -387,10 +387,6 @@ fun Blockchain.toMigratedCointId(): String = when (this) { else -> toCoinId() } -fun Blockchain.isSupportedInApp(): Boolean { - return !excludedBlockchains.contains(this) -} - fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? { return when (this) { Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(NODL_AMOUNT_TO_CREATE_ACCOUNT) else BigDecimal.ONE @@ -409,11 +405,4 @@ fun Blockchain.minimalAmount(): BigDecimal { } private const val NODL = "NODL" -private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5 - -// no need to add testnets -private val excludedBlockchains = listOf( - Blockchain.Unknown, - Blockchain.Nexa, - Blockchain.NexaTestnet, -) \ No newline at end of file +private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5 \ No newline at end of file diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/ExcludedBlockchains.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/ExcludedBlockchains.kt new file mode 100644 index 0000000000..6e2cb4877d --- /dev/null +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/ExcludedBlockchains.kt @@ -0,0 +1,45 @@ +package com.tangem.blockchainsdk.utils + +import com.tangem.blockchain.common.Blockchain +import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager +import org.jetbrains.annotations.TestOnly +import javax.inject.Inject + +class ExcludedBlockchains @Inject internal constructor( + private val excludedBlockchainsManager: ExcludedBlockchainsManager, +) : Set { + + private val excludedBlockchains: Set by lazy(mode = LazyThreadSafetyMode.NONE) { + excludedBlockchainsManager.excludedBlockchainsIds.fold(mutableSetOf()) { acc, blockchainId -> + val blockchain = Blockchain.fromId(blockchainId) + acc.add(blockchain) + + blockchain.getTestnetVersion()?.let { acc.add(it) } + + acc + } + } + + override val size: Int + get() = excludedBlockchains.size + + @TestOnly + constructor() : this( + excludedBlockchainsManager = object : ExcludedBlockchainsManager { + + override val excludedBlockchainsIds: Set = emptySet() + + override suspend fun init() { + /* no-op */ + } + }, + ) + + override fun contains(element: Blockchain): Boolean = excludedBlockchains.contains(element) + + override fun containsAll(elements: Collection): Boolean = excludedBlockchains.containsAll(elements) + + override fun isEmpty(): Boolean = excludedBlockchains.isEmpty() + + override fun iterator(): Iterator = excludedBlockchains.iterator() +} \ No newline at end of file diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/ProviderTypeExt.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/ProviderTypeExt.kt index 1d88ec5f27..3b32c69ae9 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/ProviderTypeExt.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/ProviderTypeExt.kt @@ -13,6 +13,7 @@ fun createPrivateProviderType(name: String): ProviderType? { "tangemRosetta" -> ProviderType.Cardano.Rosetta "fireAcademy" -> ProviderType.Chia.FireAcademy "tangemChia" -> ProviderType.Chia.Tangem + "tangemChia3" -> ProviderType.Chia.TangemNew "infura" -> ProviderType.EthereumLike.Infura "getblock" -> ProviderType.GetBlock "arkhiaHedera" -> ProviderType.Hedera.Arkhia diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt index c2205435c2..456959fddd 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt @@ -3,8 +3,8 @@ package com.tangem.lib.crypto import com.tangem.blockchain.blockchains.xrp.XrpAddressService import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.compatibility.l2BlockchainsList +import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId -import com.tangem.blockchainsdk.utils.isSupportedInApp import com.tangem.lib.crypto.converter.XrpTaggedAddressConverter import com.tangem.lib.crypto.models.XrpTaggedAddress @@ -64,8 +64,10 @@ object BlockchainUtils { return blockchain == Blockchain.Tron || blockchain == Blockchain.TronTestnet } - fun isSupportedNetworkId(blockchainId: String): Boolean { - return Blockchain.fromNetworkId(blockchainId)?.isSupportedInApp() ?: false + fun isSupportedNetworkId(blockchainId: String, excludedBlockchains: ExcludedBlockchains): Boolean { + val blockchain = Blockchain.fromNetworkId(blockchainId) + + return blockchain != null && blockchain !in excludedBlockchains } fun isArbitrum(blockchainId: String): Boolean {