diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index c0d2c19ebd..86ddd48e06 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -45,7 +45,7 @@ }, { "name": "NEW_ATTESTATION_ENABLED", - "version": "5.23.0" + "version": "undefined" }, { "name": "TWIN_REFACTORING_ENABLED", diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt index 1699dbc089..926d9213aa 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt @@ -23,9 +23,10 @@ internal fun Response.toSafeApiResponse(analyticsErrorHandler: Anal if (code == null) { ApiResponseError.UnknownException(IllegalArgumentException("Unknown error status code: ${code()}")) } else { - sendHttpError(code, analyticsErrorHandler) + val errorBody = errorBody()?.string().orEmpty() // !!!Beware!!! string() closes stream after invocation + sendHttpError(code, analyticsErrorHandler, errorBody) - ApiResponseError.HttpException(code, message(), errorBody()?.string()) + ApiResponseError.HttpException(code, message(), errorBody) } } catch (e: Exception) { Timber.e(e, "UnknownException occured") @@ -39,6 +40,7 @@ internal fun Response.toSafeApiResponse(analyticsErrorHandler: Anal private fun Response.sendHttpError( code: ApiResponseError.HttpException.Code, analyticsErrorHandler: AnalyticsErrorHandler, + errorBody: String, ) { val fullRequestUrl = raw().request.url.toUrl() val shortUrl = fullRequestUrl.authority + fullRequestUrl.path @@ -46,7 +48,7 @@ private fun Response.sendHttpError( ApiErrorEvent( endpoint = shortUrl, code = code.numericCode, - message = errorBody()?.string().orEmpty(), + message = errorBody, ), ) } 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 770cecd7a1..74cd8c167a 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 @@ -22,6 +22,7 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network +import timber.log.Timber internal class ManagedCryptoCurrencyFactory( private val excludedBlockchains: ExcludedBlockchains, @@ -187,11 +188,18 @@ internal class ManagedCryptoCurrencyFactory( decimals = blockchain.decimals(), isL2Network = l2BlockchainsList.contains(blockchain), ) - network.canHandleTokens -> SourceNetwork.Default( - network = network, - decimals = decimals ?: return null, - contractAddress = contractAddress, - ) + network.canHandleTokens -> { + val formattedContractAddress = blockchain.reformatContractAddress(contractAddress) + if (formattedContractAddress == null) { + Timber.w("Couldn't reformat $contractAddress") + return null + } + SourceNetwork.Default( + network = network, + decimals = decimals ?: return null, + contractAddress = formattedContractAddress, + ) + } else -> null } } 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 2e0146f681..a00841547c 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 @@ -72,7 +72,7 @@ internal class TokenMarketInfoConverter( TokenMarketInfo.Network( networkId = network.networkId, exchangeable = network.exchangeable, - contractAddress = network.contractAddress, + contractAddress = blockchain.reformatContractAddress(network.contractAddress), decimalCount = network.decimalCount, ) } diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt index d4fbe4d861..84c6a72576 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampTransactionRepository.kt @@ -63,11 +63,17 @@ internal class DefaultOnrampTransactionRepository( }.map(transactionConverter::convert) } - override suspend fun updateTransactionStatus(externalTxId: String, status: OnrampStatus.Status) = - withContext(dispatchers.io) { - val updatedTx = getTransactionById(externalTxId)?.copy(status = status) ?: return@withContext - storeTransaction(updatedTx) - } + override suspend fun updateTransactionStatus( + externalTxId: String, + externalTxUrl: String, + status: OnrampStatus.Status, + ) = withContext(dispatchers.io) { + val updatedTx = getTransactionById(externalTxId)?.copy( + externalTxUrl = externalTxUrl, + status = status, + ) ?: return@withContext + storeTransaction(updatedTx) + } override suspend fun removeTransaction(externalTxId: String) { withContext(dispatchers.io) { diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt index 8e28d63d32..6a31dba336 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt @@ -10,7 +10,12 @@ class OnrampUpdateTransactionStatusUseCase( private val errorResolver: OnrampErrorResolver, ) { - suspend operator fun invoke(externalTxId: String, status: OnrampStatus.Status) = Either.catch { - onrampTransactionRepository.updateTransactionStatus(externalTxId = externalTxId, status = status) - }.mapLeft(errorResolver::resolve) + suspend operator fun invoke(externalTxId: String, externalTxUrl: String, status: OnrampStatus.Status) = + Either.catch { + onrampTransactionRepository.updateTransactionStatus( + externalTxId = externalTxId, + externalTxUrl = externalTxUrl, + status = status, + ) + }.mapLeft(errorResolver::resolve) } \ No newline at end of file diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt index ae94b073a8..a5187b67b9 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampTransactionRepository.kt @@ -14,7 +14,7 @@ interface OnrampTransactionRepository { fun getTransactions(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Flow> - suspend fun updateTransactionStatus(externalTxId: String, status: OnrampStatus.Status) + suspend fun updateTransactionStatus(externalTxId: String, externalTxUrl: String, status: OnrampStatus.Status) suspend fun removeTransaction(externalTxId: String) } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index 3ce42734fc..e7ae73cb18 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -201,7 +201,7 @@ internal class DefaultSendComponent @AssistedInject constructor( params = SendConfirmComponent.Params( state = model.uiState.value, userWallet = model.userWallet, - currentRoute = currentRoute.filterIsInstance(), + currentRoute = currentRoute, isBalanceHidingFlow = model.isBalanceHiddenFlow, analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY, cryptoCurrencyStatus = model.cryptoCurrencyStatus, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt index ff6ba04840..943e48d31f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt @@ -140,7 +140,7 @@ internal class SendConfirmComponent( val feeCryptoCurrencyStatus: CryptoCurrencyStatus, val appCurrency: AppCurrency, val callback: ModelCallback, - val currentRoute: Flow, + val currentRoute: Flow, val isBalanceHidingFlow: StateFlow, val predefinedValues: PredefinedValues, ) { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index cfd1b22477..daf5df6be6 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -490,7 +490,7 @@ internal class SendConfirmModel @Inject constructor( flow = uiState, flow2 = params.currentRoute, transform = { state, route -> state to route }, - ).onEach { (state, _) -> + ).filter { it.second is SendRoute.Confirm }.onEach { (state, _) -> val amountUM = state.amountUM as? AmountState.Data val confirmUM = state.confirmUM params.callback.onResult( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index a1d123dc42..2e22376c66 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -31,11 +31,12 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.SendComponent import com.tangem.features.send.v2.common.NavigationUM import com.tangem.features.send.v2.send.SendRoute -import com.tangem.features.send.v2.send.confirm.model.SendConfirmAlertFactory import com.tangem.features.send.v2.send.confirm.SendConfirmComponent +import com.tangem.features.send.v2.send.confirm.model.SendConfirmAlertFactory import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM import com.tangem.features.send.v2.send.ui.state.SendUM import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent +import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRTrigger import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent import com.tangem.features.send.v2.subcomponents.destination.model.transformers.SendDestinationInitialStateTransformer import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM @@ -74,6 +75,7 @@ internal class SendModel @Inject constructor( private val getCardInfoUseCase: GetCardInfoUseCase, private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, + private val sendAmountUpdateQRTrigger: SendAmountUpdateQRTrigger, ) : Model(), SendComponentCallback { private val params: SendComponent.Params = paramsContainer.require() @@ -110,6 +112,7 @@ internal class SendModel @Inject constructor( } override fun onAmountResult(amountUM: AmountState) { + predefinedAmountValue = null // reset predefined amount _uiState.update { it.copy(amountUM = amountUM) } } @@ -118,6 +121,7 @@ internal class SendModel @Inject constructor( } override fun onResult(sendUM: SendUM) { + predefinedAmountValue = null // reset predefined amount _uiState.update { sendUM } } @@ -227,7 +231,13 @@ internal class SendModel @Inject constructor( private fun onQrCodeScanned(address: String) { val parsedQrCode = parseQrCodeUseCase(address, cryptoCurrency).getOrNull() + // Decompose component can be active or inactive depending on its state and navigation stack + // If it is in inactive state use parameter to pass value to amount component predefinedAmountValue = parsedQrCode?.amount?.parseBigDecimal(cryptoCurrency.decimals) + // If it is in active state use flow to update value in amount component + modelScope.launch { + predefinedAmountValue?.let { sendAmountUpdateQRTrigger.triggerUpdateAmount(it) } + } } private fun onFailedTxEmailClick(errorMessage: String? = null) { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountReduceTrigger.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountReduceTrigger.kt index 901ee1f0df..459994ffcf 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountReduceTrigger.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountReduceTrigger.kt @@ -25,14 +25,33 @@ interface SendAmountReduceListener { val ignoreReduceTriggerFlow: Flow } +/** + * Trigger amount change from another component. + * Different from another triggers because it takes raw string instead of BigDecimal + */ +interface SendAmountUpdateQRTrigger { + suspend fun triggerUpdateAmount(amountValue: String) +} + +/** + * Trigger amount change from another component. + * Different from another triggers because it takes raw string instead of BigDecimal + */ +interface SendAmountUpdateQRListener { + val updateAmountTriggerFlow: Flow +} + @Singleton internal class DefaultSendAmountReduceTrigger @Inject constructor() : SendAmountReduceTrigger, - SendAmountReduceListener { + SendAmountReduceListener, + SendAmountUpdateQRTrigger, + SendAmountUpdateQRListener { override val reduceToTriggerFlow = MutableSharedFlow() override val reduceByTriggerFlow = MutableSharedFlow() override val ignoreReduceTriggerFlow = MutableSharedFlow() + override val updateAmountTriggerFlow = MutableSharedFlow() override suspend fun triggerReduceBy(reduceBy: ReduceByData) { reduceByTriggerFlow.emit(reduceBy) @@ -45,4 +64,8 @@ internal class DefaultSendAmountReduceTrigger @Inject constructor() : override suspend fun triggerIgnoreReduce() { ignoreReduceTriggerFlow.emit(Unit) } + + override suspend fun triggerUpdateAmount(amountValue: String) { + updateAmountTriggerFlow.emit(amountValue) + } } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/di/SendAmountModule.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/di/SendAmountModule.kt index b486aeaeae..c88ca068c2 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/di/SendAmountModule.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/di/SendAmountModule.kt @@ -1,8 +1,7 @@ package com.tangem.features.send.v2.subcomponents.amount.di +import com.tangem.features.send.v2.subcomponents.amount.* import com.tangem.features.send.v2.subcomponents.amount.DefaultSendAmountReduceTrigger -import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener -import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -20,4 +19,12 @@ internal interface SendAmountModule { @Singleton @Binds fun provideSendAmountReduceListener(impl: DefaultSendAmountReduceTrigger): SendAmountReduceListener + + @Singleton + @Binds + fun provideSendAmountUpdateQRListener(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateQRListener + + @Singleton + @Binds + fun provideSendAmountUpdateQRTrigger(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateQRTrigger } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt index 67bac1cf20..3c137c1bb4 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt @@ -27,6 +27,7 @@ import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenS import com.tangem.features.send.v2.send.ui.state.ButtonsUM import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener +import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRListener import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents.SelectedCurrencyType import com.tangem.features.send.v2.subcomponents.fee.SendFeeData @@ -50,6 +51,7 @@ internal class SendAmountModel @Inject constructor( private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase, private val sendAmountReduceListener: SendAmountReduceListener, private val feeReloadTrigger: SendFeeReloadTrigger, + private val sendAmountUpdateQRListener: SendAmountUpdateQRListener, private val analyticsEventHandler: AnalyticsEventHandler, ) : Model(), AmountScreenClickIntents { @@ -71,6 +73,7 @@ internal class SendAmountModel @Inject constructor( subscribeOnAmountReduceByTriggerUpdates() subscribeOnAmountReduceToTriggerUpdates() subscribeOnAmountIgnoreReduceTriggerUpdates() + subscribeOnAmountUpdateQRTriggerUpdates() } private fun initMinBoundary() { @@ -105,8 +108,8 @@ internal class SendAmountModel @Inject constructor( ), ) } - params.predefinedAmountValue?.let(::onAmountValueChange) } + params.predefinedAmountValue?.let(::onAmountValueChange) } fun updateState(amountUM: AmountState) { @@ -215,6 +218,14 @@ internal class SendAmountModel @Inject constructor( .launchIn(modelScope) } + private fun subscribeOnAmountUpdateQRTriggerUpdates() { + sendAmountUpdateQRListener.updateAmountTriggerFlow + .onEach { amount -> + onAmountValueChange(amount) + saveResult() + }.launchIn(modelScope) + } + private fun saveResult() { val params = params as? SendAmountComponentParams.AmountParams ?: return params.callback.onAmountResult(uiState.value) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt index e25e788d52..bcf2f0b1b2 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt @@ -15,9 +15,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter -import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.utils.Provider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -116,7 +116,11 @@ internal class OnrampStatusFactory @AssistedInject constructor( fiatCurrency = onrampTx.fromCurrencyCode, ), ) - onrampUpdateTransactionStatusUseCase(externalTxId = externalTxId, statusModel.status) + onrampUpdateTransactionStatusUseCase( + externalTxId = externalTxId, + externalTxUrl = statusModel.externalTxUrl.orEmpty(), + status = statusModel.status, + ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt index 3c1961eee0..aeb4c7ad66 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt @@ -72,7 +72,11 @@ internal class OnrampStatusFactory @Inject constructor( fiatCurrency = onrampTx.fromCurrencyCode, ), ) - onrampUpdateTransactionStatusUseCase(externalTxId = externalTxId, statusModel.status) + onrampUpdateTransactionStatusUseCase( + externalTxId = externalTxId, + externalTxUrl = statusModel.externalTxUrl.orEmpty(), + status = statusModel.status, + ) } }, ) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 42f5de2a7f..74c3ad64d0 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,7 +5,7 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.23-1032" +tangemBlockchainSdk = "releases-5.23-1037" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "releases-5.23-456" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^