Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-27 18:53:00 +03:00
commit b577c3c21b
11 changed files with 76 additions and 34 deletions

View file

@ -20,6 +20,7 @@ import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.lib.crypto.TransactionManager
import com.tangem.lib.crypto.models.Currency
import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.lib.crypto.models.ProxyNetworkInfo
import com.tangem.lib.crypto.models.transactions.SendTxResult
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner
@ -148,8 +149,13 @@ class TransactionManagerImpl(
}
}
override fun getBlockchainId(networkId: String): String {
return requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }.id
override fun getBlockchainInfo(networkId: String): ProxyNetworkInfo {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
return ProxyNetworkInfo(
name = blockchain.fullName,
blockchainId = blockchain.id,
blockchainCurrency = blockchain.currency,
)
}
private fun handleSendResult(result: SimpleResult): SendTxResult {

View file

@ -10,9 +10,9 @@ package com.tangem.core.ui.utils
*/
fun getValidatedNumberWithFixedDecimals(text: String, decimals: Int): String {
val filteredChars = text.filterIndexed { index, c ->
c.isDigit()
|| (c == '.' && index != 0 && text.indexOf('.') == index)
|| (c == '.' && index != 0 && text.count { it == '.' } <= 1)
val isOneOrZeroPoint = c == '.' && index != 0 && text.count { it == '.' } <= 1
val isIndexPointIndex = c == '.' && index != 0 && text.indexOf('.') == index
c.isDigit() || isIndexPointIndex || isOneOrZeroPoint
}
// If dot is present, take first 3 digits before decimal and first decimals digits after decimal
return if (filteredChars.count { it == '.' } == 1) {

View file

@ -36,7 +36,6 @@ class TokensConverter @Inject constructor() : Converter<CoinsResponse.Coin, Curr
companion object {
private const val DEFAULT_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/"
private const val SMALL_ICON_PATH = "small"
private const val LARGE_ICON_PATH = "large"
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.swap.domain
import com.tangem.feature.swap.domain.models.domain.Currency
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
interface BlockchainInteractor {
@ -11,5 +12,5 @@ interface BlockchainInteractor {
*
* workaround till not use backend only and not integrated server vs sdk
*/
fun getBlockchainId(networkId: String): String
fun getBlockchainInfo(networkId: String): NetworkInfo
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.swap.domain
import com.tangem.feature.swap.domain.models.domain.Currency
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
import com.tangem.lib.crypto.TransactionManager
import javax.inject.Inject
@ -8,8 +9,14 @@ internal class BlockchainInteractorImpl @Inject constructor(
private val transactionManager: TransactionManager,
) : BlockchainInteractor {
override fun getBlockchainId(networkId: String): String {
return transactionManager.getBlockchainId(networkId)
override fun getBlockchainInfo(networkId: String): NetworkInfo {
return transactionManager.getBlockchainInfo(networkId).let {
NetworkInfo(
name = it.name,
blockchainId = it.blockchainId,
blockchainCurrency = it.blockchainCurrency,
)
}
}
override fun getTokenDecimals(token: Currency): Int {

View file

@ -152,7 +152,7 @@ internal class SwapInteractorImpl @Inject constructor(
val isAllowedToSpend = checkAllowance(networkId, fromTokenAddress)
val fee = getAndUpdateFee(networkId, fromToken)
val isBalanceEnough = isBalanceEnough(fromToken, networkId, amount, fee)
val isFeeEnough = checkFeeIsEnough(fee, amount, networkId)
val isFeeEnough = checkFeeIsEnough(fee, amount, networkId, fromToken)
if (isAllowedToSpend && allowPermissionsHandler.isAddressAllowanceInProgress(fromTokenAddress)) {
allowPermissionsHandler.removeAddressFromProgress(fromTokenAddress)
transactionManager.updateWalletManager(networkId)
@ -497,6 +497,7 @@ internal class SwapInteractorImpl @Inject constructor(
)
}
@Suppress("LongParameterList")
private fun updatePermissionState(
networkId: String,
fromToken: Currency,
@ -526,13 +527,12 @@ internal class SwapInteractorImpl @Inject constructor(
}
private fun isBalanceEnough(fromToken: Currency, networkId: String, amount: SwapAmount, fee: BigDecimal?): Boolean {
val tokenBalance =
userWalletManager.getCurrentWalletTokensBalance(networkId)[fromToken.symbol]?.value ?: BigDecimal.ZERO
return if (fromToken is Currency.NonNativeToken) {
(userWalletManager.getCurrentWalletTokensBalance(networkId)[fromToken.symbol]?.value
?: BigDecimal.ZERO) >= amount.value
tokenBalance >= amount.value
} else {
/** to compare [BigDecimal] use only comparator */
(userWalletManager.getCurrentWalletTokensBalance(networkId)[fromToken.symbol]?.value
?: BigDecimal.ZERO) > amount.value.plus(fee ?: BigDecimal.ZERO)
tokenBalance > amount.value.plus(fee ?: BigDecimal.ZERO)
}
}
@ -551,16 +551,28 @@ internal class SwapInteractorImpl @Inject constructor(
}
}
private fun checkFeeIsEnough(fee: BigDecimal?, spendAmount: SwapAmount, networkId: String): Boolean {
private fun checkFeeIsEnough(
fee: BigDecimal?,
spendAmount: SwapAmount,
networkId: String,
fromToken: Currency,
): Boolean {
if (fee == null) {
return false
}
userWalletManager.getNativeTokenBalance(networkId)?.let { balance ->
return balance.value.minus(spendAmount.value) > fee.multiply(
BigDecimal.valueOf(
INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT,
),
)
val nativeTokenBalance = userWalletManager.getNativeTokenBalance(networkId)
val percentsToFeeIncrease = BigDecimal.valueOf(INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT)
return when (fromToken) {
is Currency.NativeToken -> {
nativeTokenBalance?.let { balance ->
return balance.value.minus(spendAmount.value) > fee.multiply(percentsToFeeIncrease)
} ?: false
}
is Currency.NonNativeToken -> {
nativeTokenBalance?.let { balance ->
return balance.value > fee.multiply(percentsToFeeIncrease)
} ?: false
}
}
return false
}

View file

@ -0,0 +1,7 @@
package com.tangem.feature.swap.domain.models.domain
data class NetworkInfo(
val name: String,
val blockchainId: String,
val blockchainCurrency: String,
)

View file

@ -3,6 +3,7 @@ package com.tangem.feature.swap.ui
import com.tangem.feature.swap.converters.TokensDataConverter
import com.tangem.feature.swap.domain.models.DataError
import com.tangem.feature.swap.domain.models.domain.Currency
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
import com.tangem.feature.swap.domain.models.domain.isNonNative
import com.tangem.feature.swap.domain.models.formatToUIRepresentation
import com.tangem.feature.swap.domain.models.ui.FoundTokensState
@ -24,14 +25,14 @@ import com.tangem.feature.swap.models.UiActions
/**
* State builder creates a specific states for SwapScreen
*/
class StateBuilder(val actions: UiActions) {
internal class StateBuilder(val actions: UiActions) {
private val tokensDataConverter = TokensDataConverter(actions.onSearchEntered, actions.onTokenSelected)
fun createInitialLoadingState(initialCurrency: Currency, blockchainId: String): SwapStateHolder {
fun createInitialLoadingState(initialCurrency: Currency, networkInfo: NetworkInfo): SwapStateHolder {
return SwapStateHolder(
networkId = initialCurrency.networkId,
blockchainId = blockchainId,
blockchainId = networkInfo.blockchainId,
sendCardData = SwapCardData(
type = TransactionCardType.SendCard(actions.onAmountChanged),
amount = null,
@ -55,7 +56,7 @@ class StateBuilder(val actions: UiActions) {
coinId = null,
),
fee = FeeState.Loading,
networkCurrency = initialCurrency.symbol,
networkCurrency = networkInfo.blockchainCurrency,
swapButton = SwapButton(enabled = false, loading = true, onClick = {}),
onRefresh = {},
onBackClicked = actions.onBackClicked,
@ -316,15 +317,15 @@ class StateBuilder(val actions: UiActions) {
return "$firstAddressPart...$secondAddressPart"
}
private companion object {
const val ADDRESS_MIN_LENGTH = 11
const val ADDRESS_FIRST_PART_LENGTH = 7
const val ADDRESS_SECOND_PART_LENGTH = 4
}
fun createSilentLoadState(uiState: SwapStateHolder): SwapStateHolder {
return uiState.copy(
updateInProgress = true,
)
}
private companion object {
const val ADDRESS_MIN_LENGTH = 11
const val ADDRESS_FIRST_PART_LENGTH = 7
const val ADDRESS_SECOND_PART_LENGTH = 4
}
}

View file

@ -35,6 +35,7 @@ import kotlinx.serialization.json.Json
import javax.inject.Inject
import kotlin.properties.Delegates
@Suppress("LargeClass")
@HiltViewModel
internal class SwapViewModel @Inject constructor(
private val swapInteractor: SwapInteractor,
@ -59,7 +60,7 @@ internal class SwapViewModel @Inject constructor(
var uiState: SwapStateHolder by mutableStateOf(
stateBuilder.createInitialLoadingState(
initialCurrency = currency,
blockchainId = blockchainInteractor.getBlockchainId(currency.networkId),
networkInfo = blockchainInteractor.getBlockchainInfo(currency.networkId),
),
)
private set

View file

@ -2,6 +2,7 @@ package com.tangem.lib.crypto
import com.tangem.lib.crypto.models.Currency
import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.lib.crypto.models.ProxyNetworkInfo
import com.tangem.lib.crypto.models.transactions.SendTxResult
import java.math.BigDecimal
@ -51,5 +52,5 @@ interface TransactionManager {
* workaround till not use backend only and not integrated server vs sdk
*/
@Throws(IllegalStateException::class)
fun getBlockchainId(networkId: String): String
fun getBlockchainInfo(networkId: String): ProxyNetworkInfo
}

View file

@ -0,0 +1,7 @@
package com.tangem.lib.crypto.models
data class ProxyNetworkInfo(
val name: String,
val blockchainId: String,
val blockchainCurrency: String,
)