Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-29 19:37:17 +03:00
commit bffd007a35
21 changed files with 396 additions and 100 deletions

View file

@ -49,6 +49,6 @@ interface TangemExpressApi {
@Query("toAddress") toAddress: String,
): ApiResponse<ExchangeDataResponse>
@GET("exchange-result")
suspend fun getExchangeResults(@Query("txId") txId: String): ApiResponse<ExchangeResultsResponse>
@GET("exchange-status")
suspend fun getExchangeStatus(@Query("txId") txId: String): ApiResponse<ExchangeStatusResponse>
}

View file

@ -1,42 +0,0 @@
package com.tangem.datasource.api.express.models.response
import com.squareup.moshi.Json
data class ExchangeResultsResponse(
@Json(name = "status")
val status: ExchangeResultsStatus,
@Json(name = "externalStatus")
val externalStatus: String,
@Json(name = "externalTxUrl")
val externalTxUrl: String,
@Json(name = "error")
val error: ExchangeResultsError?,
)
enum class ExchangeResultsStatus {
@Json(name = "processing")
PROCESSING,
@Json(name = "done")
DONE,
@Json(name = "failed")
FAILED,
@Json(name = "refunded")
REFUNDED,
@Json(name = "verificationRequired")
VERIFICATION_REQUIRED,
}
data class ExchangeResultsError(
@Json(name = "code")
val code: Int,
@Json(name = "description")
val description: String,
)

View file

@ -0,0 +1,59 @@
package com.tangem.datasource.api.express.models.response
import com.squareup.moshi.Json
data class ExchangeStatusResponse(
@Json(name = "providerId")
val providerId: String,
@Json(name = "externalTxId")
val externalTxId: String,
@Json(name = "externalTxStatus")
val externalStatus: ExchangeStatus,
@Json(name = "externalTxUrl")
val externalTxUrl: String,
@Json(name = "error")
val error: ExchangeStatusError?,
)
enum class ExchangeStatus {
@Json(name = "new")
NEW,
@Json(name = "waiting")
WAITING,
@Json(name = "confirming")
CONFIRMING,
@Json(name = "exchanging")
EXCHANGING,
@Json(name = "sending")
SENDING,
@Json(name = "finished")
FINISHED,
@Json(name = "failed")
FAILED,
@Json(name = "refunded")
REFUNDED,
@Json(name = "verifying")
VERIFYING,
}
data class ExchangeStatusError(
@Json(name = "code")
val code: Int,
@Json(name = "description")
val description: String,
)

View file

@ -32,6 +32,8 @@ object PreferencesKeys {
val SELECTED_APP_CURRENCY_KEY by lazy { stringPreferencesKey(name = "selectedAppCurrency") }
val BALANCE_HIDING_SETTINGS_KEY by lazy { stringPreferencesKey(name = "balanceHidingSettings") }
val SWAP_TRANSACTIONS_KEY by lazy { stringPreferencesKey(name = "swapTransactions") }
}
/** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore<Preferences> */

View file

@ -3,14 +3,13 @@ package com.tangem.core.ui.components.appbar
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@ -49,7 +48,11 @@ fun AppBarWithBackButton(
contentDescription = null,
modifier = Modifier
.size(size = TangemTheme.dimens.size24)
.clickable { onBackClick() },
.clickable(
indication = rememberRipple(bounded = false),
interactionSource = remember { MutableInteractionSource() },
onClick = onBackClick,
),
tint = TangemTheme.colors.icon.primary1,
)
if (!text.isNullOrBlank()) {

View file

@ -8,15 +8,19 @@ plugins {
dependencies {
/** AndroidX */
implementation(deps.androidx.datastore)
/** Project*/
implementation(project(":core:datasource"))
implementation(project(":core:utils"))
implementation(project(":features:swap:domain"))
implementation(projects.core.datasource)
implementation(projects.core.utils)
implementation(projects.features.swap.domain)
/** Network */
implementation(deps.retrofit)
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.arrow.core)
/** Domain */
implementation(projects.domain.tokens.models)

View file

@ -0,0 +1,163 @@
package com.tangem.feature.swap
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectList
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.domain.SwapTransactionRepository
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class DefaultSwapTransactionRepository(
private val appPreferencesStore: AppPreferencesStore,
) : SwapTransactionRepository {
override suspend fun storeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrencyId: CryptoCurrency.ID,
toCryptoCurrencyId: CryptoCurrency.ID,
transaction: SavedSwapTransactionModel,
) {
appPreferencesStore.editData { mutablePreferences ->
val savedTransactions: List<SavedSwapTransactionListModel>? = mutablePreferences.getObjectList(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
)
val tokenTransactions = savedTransactions
?.firstOrNull {
it.checkId(
checkUserWalletId = userWalletId,
fromCurrencyId = fromCryptoCurrencyId,
toCurrencyId = toCryptoCurrencyId,
)
}
?.transactions
?.addOrReplace(
item = transaction,
predicate = { it.txId == transaction.txId },
) ?: listOf(transaction)
mutablePreferences.setObject(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
value = savedTransactions?.updateList(
userWalletId = userWalletId,
fromCryptoCurrencyId = fromCryptoCurrencyId,
toCryptoCurrencyId = toCryptoCurrencyId,
transactions = tokenTransactions,
) ?: listOf(
SavedSwapTransactionListModel(
userWalletId = userWalletId.stringValue,
fromCryptoCurrencyId = fromCryptoCurrencyId.value,
toCryptoCurrencyId = toCryptoCurrencyId.value,
transactions = tokenTransactions,
),
),
)
}
}
override fun getTransactions(
userWalletId: UserWalletId,
cryptoCurrencyId: CryptoCurrency.ID,
): Flow<List<SavedSwapTransactionListModel>?> {
return appPreferencesStore.getObjectList<SavedSwapTransactionListModel>(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
).map { savedTransactions ->
savedTransactions
?.filter {
it.userWalletId == userWalletId.stringValue &&
(
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
)
}
}
}
override suspend fun removeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrencyId: CryptoCurrency.ID,
toCryptoCurrencyId: CryptoCurrency.ID,
txId: String,
) {
appPreferencesStore.editData { mutablePreferences ->
val savedList: List<SavedSwapTransactionListModel>? = mutablePreferences.getObjectList(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
)
val tokenTransactions = savedList
?.first {
it.checkId(
checkUserWalletId = userWalletId,
fromCurrencyId = fromCryptoCurrencyId,
toCurrencyId = toCryptoCurrencyId,
)
}
?.transactions
?.dropWhile { it.txId == txId }
val editedList =
if (tokenTransactions.isNullOrEmpty()) {
savedList?.dropWhile {
it.checkId(
checkUserWalletId = userWalletId,
fromCurrencyId = fromCryptoCurrencyId,
toCurrencyId = toCryptoCurrencyId,
)
}
} else {
savedList.updateList(
userWalletId = userWalletId,
fromCryptoCurrencyId = fromCryptoCurrencyId,
toCryptoCurrencyId = toCryptoCurrencyId,
transactions = tokenTransactions,
)
}
if (editedList.isNullOrEmpty()) {
mutablePreferences.remove(key = PreferencesKeys.SWAP_TRANSACTIONS_KEY)
} else {
mutablePreferences.setObject(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
value = editedList,
)
}
}
}
private fun SavedSwapTransactionListModel.checkId(
checkUserWalletId: UserWalletId,
fromCurrencyId: CryptoCurrency.ID,
toCurrencyId: CryptoCurrency.ID,
): Boolean {
return userWalletId == checkUserWalletId.stringValue &&
toCryptoCurrencyId == toCurrencyId.value &&
fromCryptoCurrencyId == fromCurrencyId.value
}
private fun List<SavedSwapTransactionListModel>.updateList(
userWalletId: UserWalletId,
fromCryptoCurrencyId: CryptoCurrency.ID,
toCryptoCurrencyId: CryptoCurrency.ID,
transactions: List<SavedSwapTransactionModel>,
): List<SavedSwapTransactionListModel> {
return addOrReplace(
item = SavedSwapTransactionListModel(
userWalletId = userWalletId.stringValue,
fromCryptoCurrencyId = fromCryptoCurrencyId.value,
toCryptoCurrencyId = toCryptoCurrencyId.value,
transactions = transactions,
),
predicate = {
it.checkId(
checkUserWalletId = userWalletId,
fromCurrencyId = fromCryptoCurrencyId,
toCurrencyId = toCryptoCurrencyId,
)
},
)
}
}

View file

@ -1,5 +1,8 @@
package com.tangem.feature.swap
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Approver
import com.tangem.blockchain.common.Blockchain
@ -52,6 +55,7 @@ internal class SwapRepositoryImpl @Inject constructor(
private val leastTokenInfoConverter = LeastTokenInfoConverter()
private val swapPairInfoConverter = SwapPairInfoConverter()
private val cryptoCurrencyFactory = CryptoCurrencyFactory()
private val exchangeStatusConverter = ExchangeStatusConverter()
override suspend fun getPairs(
initialCurrency: LeastTokenInfo,
@ -103,6 +107,25 @@ internal class SwapRepositoryImpl @Inject constructor(
).getOrThrow()
}
override suspend fun getExchangeStatus(txId: String): Either<UnknownError, ExchangeStatusModel> {
return withContext(coroutineDispatcher.io) {
either {
catch(
{
exchangeStatusConverter.convert(
tangemExpressApi
.getExchangeStatus(txId)
.getOrThrow(),
)
},
{
raise(UnknownError(it.message))
},
)
}
}
}
override suspend fun getRates(currencyId: String, tokenIds: List<String>): Map<String, Double> {
// workaround cause backend do not return arbitrum and optimism rates
val addedTokens = if (tokenIds.contains(OPTIMISM_ID) || tokenIds.contains(ARBITRUM_ID)) {

View file

@ -0,0 +1,19 @@
package com.tangem.feature.swap.converters
import com.tangem.datasource.api.express.models.response.ExchangeStatusResponse
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
import com.tangem.utils.converter.Converter
internal class ExchangeStatusConverter : Converter<ExchangeStatusResponse, ExchangeStatusModel> {
override fun convert(value: ExchangeStatusResponse): ExchangeStatusModel {
return ExchangeStatusModel(
providerId = value.providerId,
status = ExchangeStatus.values().firstOrNull {
it.name.lowercase() == value.externalStatus.name.lowercase()
},
txId = value.externalTxId,
txUrl = value.externalTxUrl,
)
}
}

View file

@ -7,11 +7,14 @@ import com.tangem.datasource.api.oneinch.OneInchApiFactory
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.WalletsStateHolder
import com.tangem.feature.swap.SwapRepositoryImpl
import com.tangem.feature.swap.converters.ErrorsDataConverter
import com.tangem.feature.swap.DefaultSwapTransactionRepository
import com.tangem.feature.swap.domain.SwapRepository
import com.tangem.feature.swap.domain.SwapTransactionRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -47,6 +50,14 @@ internal class SwapDataModule {
)
}
@Provides
@Singleton
fun provideSwapTransactionRepository(appPreferencesStore: AppPreferencesStore): SwapTransactionRepository {
return DefaultSwapTransactionRepository(
appPreferencesStore = appPreferencesStore,
)
}
@Provides
@Singleton
internal fun provideErrorsConverter(@NetworkMoshi moshi: Moshi): ErrorsDataConverter {

View file

@ -27,9 +27,11 @@ dependencies {
implementation(projects.domain.legacy)
implementation(projects.domain.demo)
implementation(projects.domain.card)
implementation(projects.domain.appCurrency.models)
/** Core modules */
implementation(projects.core.utils)
implementation(projects.core.ui)
/** Feature Apis */
implementation(projects.features.wallet.api)
@ -40,4 +42,5 @@ dependencies {
implementation(deps.arrow.core)
implementation(deps.timber)
implementation(deps.tangem.blockchain)
implementation(deps.moshi)
}

View file

@ -213,7 +213,10 @@ internal class SwapInteractorImpl @Inject constructor(
return when (result) {
is SendTxResult.Success -> {
allowPermissionsHandler.addAddressToInProgress(permissionOptions.forTokenContractAddress)
TxState.TxSent(txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath) ?: "")
TxState.TxSent(
txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath).orEmpty(),
timestamp = System.currentTimeMillis(),
)
}
SendTxResult.UserCancelledError -> TxState.UserCancelled
is SendTxResult.BlockchainSdkError -> TxState.BlockchainError
@ -370,7 +373,7 @@ internal class SwapInteractorImpl @Inject constructor(
currencyToGet = currencyToGet,
amount = amount,
fee = fee,
providerId = swapProvider.providerId,
swapProvider = swapProvider,
userWalletId = requireNotNull(getSelectedWallet()).walletId,
)
}
@ -455,7 +458,8 @@ internal class SwapInteractorImpl @Inject constructor(
swapData.toTokenAmount,
currencyToGet.symbol,
),
txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath) ?: "",
txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath).orEmpty(),
timestamp = System.currentTimeMillis(),
)
}
SendTxResult.UserCancelledError -> TxState.UserCancelled
@ -471,7 +475,7 @@ internal class SwapInteractorImpl @Inject constructor(
currencyToGet: CryptoCurrencyStatus,
amount: SwapAmount,
fee: TxFee,
providerId: String,
swapProvider: SwapProvider,
userWalletId: UserWalletId,
): TxState {
val exchangeData = repository.getExchangeData(
@ -481,7 +485,7 @@ internal class SwapInteractorImpl @Inject constructor(
toNetwork = currencyToGet.currency.network.backendId,
fromAmount = amount.toStringWithRightOffset(),
fromDecimals = amount.decimals,
providerId = providerId,
providerId = swapProvider.providerId,
rateType = RateType.FLOAT,
toAddress = currencyToGet.value.networkAddress?.defaultAddress ?: "",
)
@ -511,6 +515,7 @@ internal class SwapInteractorImpl @Inject constructor(
}
},
ifRight = {
val timestamp = System.currentTimeMillis()
TxState.TxSent(
fromAmount = amountFormatter.formatSwapAmountToUI(
amount,
@ -523,7 +528,8 @@ internal class SwapInteractorImpl @Inject constructor(
txAddress = userWalletManager.getLastTransactionHash(
currencyToSend.currency.network.backendId,
derivationPath,
) ?: "",
).orEmpty(),
timestamp = timestamp,
)
},
)

View file

@ -1,5 +1,6 @@
package com.tangem.feature.swap.domain
import arrow.core.Either
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.domain.models.data.AggregatedSwapDataModel
@ -14,6 +15,8 @@ interface SwapRepository {
suspend fun getExchangeableTokens(networkId: String): List<Currency>
suspend fun getExchangeStatus(txId: String): Either<UnknownError, ExchangeStatusModel>
@Suppress("LongParameterList")
suspend fun findBestQuote(
fromContractAddress: String,

View file

@ -0,0 +1,29 @@
package com.tangem.feature.swap.domain
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
import kotlinx.coroutines.flow.Flow
interface SwapTransactionRepository {
suspend fun storeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrencyId: CryptoCurrency.ID,
toCryptoCurrencyId: CryptoCurrency.ID,
transaction: SavedSwapTransactionModel,
)
fun getTransactions(
userWalletId: UserWalletId,
cryptoCurrencyId: CryptoCurrency.ID,
): Flow<List<SavedSwapTransactionListModel>?>
suspend fun removeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrencyId: CryptoCurrency.ID,
toCryptoCurrencyId: CryptoCurrency.ID,
txId: String,
)
}

View file

@ -1,7 +1,7 @@
package com.tangem.feature.swap.domain.models.domain
data class ExchangeStatusModel(
val providerId: Int,
val providerId: String,
val status: ExchangeStatus? = null,
val txId: String? = null,
val txUrl: String? = null,

View file

@ -0,0 +1,19 @@
package com.tangem.feature.swap.domain.models.domain
data class SavedSwapTransactionListModel(
val userWalletId: String,
val fromCryptoCurrencyId: String,
val toCryptoCurrencyId: String,
val transactions: List<SavedSwapTransactionModel>,
)
data class SavedSwapTransactionModel(
val txId: String,
val timestamp: Long,
val fromCryptoAmount: String,
val toCryptoAmount: String,
val provider: SwapProvider,
val toFiatAmount: String? = null,
val fromFiatAmount: String? = null,
val status: ExchangeStatusModel? = null,
)

View file

@ -6,6 +6,7 @@ sealed class TxState {
val fromAmount: String? = null,
val toAmount: String? = null,
val txAddress: String,
val timestamp: Long,
) : TxState()
object UserCancelled : TxState()

View file

@ -2,14 +2,15 @@ package com.tangem.feature.swap.models
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.feature.swap.domain.models.domain.SwapProvider
data class SwapSuccessStateHolder(
val timestamp: Long,
val txUrl: String,
val fee: TextReference,
val rate: TextReference,
val selectedProvider: SwapProvider,
val providerName: TextReference,
val providerType: TextReference,
val providerIcon: String,
val fromTokenAmount: TextReference,
val toTokenAmount: TextReference,
val fromTokenFiatAmount: TextReference,

View file

@ -21,7 +21,6 @@ import com.tangem.feature.swap.domain.models.ui.*
import com.tangem.feature.swap.models.*
import com.tangem.feature.swap.models.states.*
import com.tangem.feature.swap.presentation.R
import com.tangem.feature.swap.viewmodels.SwapProcessDataState
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import java.math.BigDecimal
@ -565,38 +564,37 @@ internal class StateBuilder(
)
}
@Suppress("LongParameterList")
fun createSuccessState(
uiState: SwapStateHolder,
txState: TxState.TxSent,
dataState: SwapProcessDataState,
fromAmount: BigDecimal,
toAmount: BigDecimal,
txUrl: String,
onSecondaryBtnClick: () -> Unit,
): SwapStateHolder {
val fromToken = requireNotNull(uiState.sendCardData as? SwapCardState.SwapCardData)
val toToken = requireNotNull(uiState.receiveCardData as? SwapCardState.SwapCardData)
val fromTokenIconState = fromToken.token?.let(iconStateConverter::convert)
val toTokenIconState = toToken.token?.let(iconStateConverter::convert)
val providerState = uiState.providerState as ProviderState.Content
val fromToken = requireNotNull((uiState.sendCardData as? SwapCardState.SwapCardData)?.token)
val toToken = requireNotNull((uiState.receiveCardData as? SwapCardState.SwapCardData)?.token)
val fromTokenIconState = iconStateConverter.convert(fromToken)
val toTokenIconState = iconStateConverter.convert(toToken)
val fee = uiState.fee as? FeeItemState.Content ?: return uiState
val fromCryptoCurrencyStatus = requireNotNull(fromToken.token)
val toCryptoCurrencyStatus = requireNotNull(toToken.token)
val rate = txState.toAmount?.toBigDecimal()?.calculateRate(
txState.fromAmount?.toBigDecimal() ?: BigDecimal.ZERO,
toCryptoCurrencyStatus.currency.decimals,
)
val fromCurrencySymbol = fromCryptoCurrencyStatus.currency.symbol
val toCurrencySymbol = toCryptoCurrencyStatus.currency.symbol
val fromFiatAmount = getFormattedFiatAmount(fromToken.value.fiatRate?.multiply(fromAmount))
val toFiatAmount = getFormattedFiatAmount(toToken.value.fiatRate?.multiply(toAmount))
return uiState.copy(
successState = SwapSuccessStateHolder(
timestamp = System.currentTimeMillis(),
timestamp = txState.timestamp,
txUrl = txUrl,
selectedProvider = requireNotNull(dataState.selectedProvider),
providerName = TextReference.Str(providerState.name),
providerType = TextReference.Str(providerState.type),
providerIcon = providerState.iconUrl,
fee = TextReference.Str("${fee.amountCrypto} ${fee.symbolCrypto} (${fee.amountFiatFormatted})"),
rate = TextReference.Str("1 $fromCurrencySymbol$rate $toCurrencySymbol"),
fromTokenAmount = TextReference.Str("${txState.fromAmount.orEmpty()} ${fromToken.tokenCurrency}}"),
toTokenAmount = TextReference.Str("${txState.toAmount.orEmpty()} ${toToken.tokenCurrency}}"),
fromTokenFiatAmount = TextReference.Str(fromToken.amountEquivalent.orEmpty()),
toTokenFiatAmount = TextReference.Str(toToken.amountEquivalent.orEmpty()),
rate = TextReference.Str(providerState.rate),
fromTokenAmount = TextReference.Str(txState.fromAmount.orEmpty()),
toTokenAmount = TextReference.Str(txState.toAmount.orEmpty()),
fromTokenFiatAmount = TextReference.Str(fromFiatAmount),
toTokenFiatAmount = TextReference.Str(toFiatAmount),
fromTokenIconState = fromTokenIconState,
toTokenIconState = toTokenIconState,
onSecondaryButtonClick = onSecondaryBtnClick,
@ -985,7 +983,7 @@ internal class StateBuilder(
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
}
private fun getFormattedFiatAmount(amount: BigDecimal): String {
private fun getFormattedFiatAmount(amount: BigDecimal?): String {
val appCurrency = appCurrencyProvider()
return BigDecimalFormatter.formatFiatAmount(amount, appCurrency.code, appCurrency.symbol)

View file

@ -23,7 +23,6 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.shareText
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.swap.domain.models.domain.ExchangeProviderType
import com.tangem.feature.swap.domain.models.domain.SwapProvider
import com.tangem.feature.swap.models.SwapSuccessStateHolder
import com.tangem.feature.swap.presentation.R
@ -60,7 +59,7 @@ private fun SwapSuccessScreenContent(state: SwapSuccessStateHolder, padding: Pad
.background(TangemTheme.colors.background.secondary)
.padding(horizontal = TangemTheme.dimens.spacing16),
) {
TransactionDoneTitle(titleRes = R.string.swapping_success_view_title, date = 0L)
TransactionDoneTitle(titleRes = R.string.swapping_success_view_title, date = state.timestamp)
SpacerH16()
InputRowImage(
title = TextReference.Res(R.string.swapping_success_from_title),
@ -83,9 +82,9 @@ private fun SwapSuccessScreenContent(state: SwapSuccessStateHolder, padding: Pad
)
SpacerH16()
InputRowBestRate(
imageUrl = state.selectedProvider.imageLarge,
title = TextReference.Str(state.selectedProvider.name),
titleExtra = TextReference.Str(state.selectedProvider.type.name),
imageUrl = state.providerIcon,
title = state.providerName,
titleExtra = state.providerType,
subtitle = state.rate,
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
@ -153,13 +152,9 @@ private val state = SwapSuccessStateHolder(
timestamp = 0L,
txUrl = "https://www.google.com/#q=nam",
fee = TextReference.Str("1 000 DAI ~ 1 000 MATIC"),
selectedProvider = SwapProvider(
providerId = "1inch",
rateTypes = listOf(),
name = "1inch",
type = ExchangeProviderType.DEX,
imageLarge = "",
),
providerName = TextReference.Str("1inch"),
providerType = TextReference.Str(ExchangeProviderType.DEX.name),
providerIcon = "",
fromTokenAmount = TextReference.Str("1 000 DAI"),
toTokenAmount = TextReference.Str("1 000 MATIC"),
fromTokenFiatAmount = TextReference.Str("1 000 $"),

View file

@ -27,9 +27,7 @@ import com.tangem.feature.swap.presentation.SwapFragment
import com.tangem.feature.swap.router.SwapNavScreen
import com.tangem.feature.swap.router.SwapRouter
import com.tangem.feature.swap.ui.StateBuilder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.Debouncer
import com.tangem.utils.coroutines.runCatching
import com.tangem.utils.coroutines.*
import com.tangem.utils.isNullOrZero
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
@ -405,7 +403,8 @@ internal class SwapViewModel @Inject constructor(
uiState = stateBuilder.createSuccessState(
uiState = uiState,
txState = it,
dataState = dataState,
fromAmount = dataState.amount?.toBigDecimal() ?: BigDecimal.ZERO,
toAmount = dataState.swapDataModel?.toTokenAmount?.value ?: BigDecimal.ZERO,
txUrl = url,
onSecondaryBtnClick = {
val txHash = it.txAddress