Updated on 2026-08-14
This commit is contained in:
parent
b2726d153c
commit
f7f2ab7f88
35 changed files with 831 additions and 91 deletions
|
|
@ -402,6 +402,28 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "vechain",
|
||||
"symbol" : "VET",
|
||||
"name" : "Vechain",
|
||||
"networks" : [
|
||||
{
|
||||
"networkId" : "vechain/test"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "vethor-token",
|
||||
"symbol": "VTHO",
|
||||
"name": "VeThor",
|
||||
"networks": [
|
||||
{
|
||||
"networkId": "vechain/test",
|
||||
"contractAddress": "0x0000000000000000000000000000456E65726779",
|
||||
"decimalCount": 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "tron",
|
||||
"symbol" : "TRX",
|
||||
|
|
|
|||
|
|
@ -215,6 +215,22 @@ internal object TokensDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideGetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetFeePaidCryptoCurrencyStatusSyncUseCase {
|
||||
return GetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideGetCurrenciesUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrenciesUseCase {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ package com.tangem.tap.features.send.redux
|
|||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.FeePaidCurrency
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
|
||||
import com.tangem.tap.common.redux.ToastNotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
|
|
@ -28,10 +30,14 @@ object ReleaseSendState : Action
|
|||
|
||||
data class PrepareSendScreen(
|
||||
val walletManager: WalletManager,
|
||||
val feePaidCurrency: FeePaidCurrency,
|
||||
val currency: CryptoCurrency,
|
||||
val coinAmount: Amount?,
|
||||
val coinRate: BigDecimal?,
|
||||
val tokenAmount: Amount? = null,
|
||||
val tokenRate: BigDecimal? = null,
|
||||
val feeCurrencyRate: BigDecimal? = null,
|
||||
val feeCurrencyDecimals: Int = 0,
|
||||
) : SendScreenAction
|
||||
|
||||
// Address
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.send.redux.reducers
|
||||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.FeePaidCurrency
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.tap.common.extensions.scaleToFiat
|
||||
|
|
@ -14,6 +15,7 @@ import java.math.BigDecimal
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Suppress("LargeClass")
|
||||
class ReceiptReducer : SendInternalReducer {
|
||||
|
||||
private lateinit var sendState: SendState
|
||||
|
|
@ -33,9 +35,10 @@ class ReceiptReducer : SendInternalReducer {
|
|||
|
||||
private fun handleRefresh(state: ReceiptState): SendState {
|
||||
val wallet = sendState.walletManager?.wallet ?: return sendState
|
||||
val feePaidCurrency = wallet.blockchain.feePaidCurrency()
|
||||
|
||||
val layoutType = determineLayoutType(amountState.mainCurrency.type, amountState.typeOfAmount)
|
||||
val symbols = determineSymbols(wallet, amountState.typeOfAmount)
|
||||
val layoutType = determineLayoutType(amountState.mainCurrency.type, amountState.typeOfAmount, feePaidCurrency)
|
||||
val symbols = determineSymbols(wallet, amountState.typeOfAmount, feePaidCurrency)
|
||||
val showBlank = !SendState.isReadyToSend()
|
||||
val result = state.copy(
|
||||
visibleTypeOfReceipt = layoutType,
|
||||
|
|
@ -44,6 +47,10 @@ class ReceiptReducer : SendInternalReducer {
|
|||
crypto = createCryptoType(symbols, showBlank),
|
||||
tokenFiat = createTokenFiatType(symbols, showBlank),
|
||||
tokenCrypto = createTokenCryptoType(symbols, showBlank),
|
||||
customTokenFiat = createCustomTokenFiat(symbols, showBlank),
|
||||
customTokenCrypto = createCustomTokenCrypto(symbols, showBlank),
|
||||
sameCurrencyFiat = createSameCurrencyFiat(symbols, showBlank),
|
||||
sameCurrencyCrypto = createSameCurrencyCrypto(symbols, showBlank),
|
||||
)
|
||||
return updateLastState(sendState.copy(receiptState = result), result)
|
||||
}
|
||||
|
|
@ -170,7 +177,143 @@ class ReceiptReducer : SendInternalReducer {
|
|||
}
|
||||
}
|
||||
|
||||
private fun determineSymbols(wallet: Wallet, amountType: AmountType): ReceiptSymbols {
|
||||
private fun createCustomTokenCrypto(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenCrypto {
|
||||
val feeValue = feeState.getCurrentFeeValue()
|
||||
if (showBlank) {
|
||||
return ReceiptTokenCrypto("0", feeValue.stripZeroPlainString(), "0", symbols)
|
||||
}
|
||||
val tokensToSend = amountState.amountToSendCrypto
|
||||
|
||||
return if (sendState.currencyIsConvertible() && sendState.feeIsConvertible()) {
|
||||
val currencyConverter = when (amountState.typeOfAmount) {
|
||||
is AmountType.Token -> sendState.tokenConverter!!
|
||||
else -> sendState.coinConverter!!
|
||||
}
|
||||
val currencyFiat = currencyConverter.toFiatUnscaled(tokensToSend)
|
||||
val feeFiat = sendState.customFeeConverter!!.toFiatUnscaled(feeValue)
|
||||
val totalFiat = currencyFiat.plus(feeFiat)
|
||||
|
||||
ReceiptTokenCrypto(
|
||||
amountToken = tokensToSend.stripZeroPlainString(),
|
||||
feeCoin = feeValue.stripZeroPlainString().addPrecisionSign(),
|
||||
totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(),
|
||||
symbols = symbols,
|
||||
)
|
||||
} else {
|
||||
ReceiptTokenCrypto(
|
||||
amountToken = tokensToSend.stripZeroPlainString(),
|
||||
feeCoin = feeValue.stripZeroPlainString().addPrecisionSign(),
|
||||
totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
|
||||
symbols = symbols,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCustomTokenFiat(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenFiat {
|
||||
val feeCoin = feeState.getCurrentFeeValue()
|
||||
if (showBlank) {
|
||||
val feeFiat = convertToFiatPrecision(feeCoin)
|
||||
return ReceiptTokenFiat("0", feeFiat, "0", "0", "0", symbols)
|
||||
}
|
||||
|
||||
val tokensToSend = amountState.amountToSendCrypto
|
||||
|
||||
return if (sendState.currencyIsConvertible() && sendState.feeIsConvertible()) {
|
||||
val currencyConverter = when (amountState.typeOfAmount) {
|
||||
is AmountType.Token -> sendState.tokenConverter!!
|
||||
else -> sendState.coinConverter!!
|
||||
}
|
||||
val feeFiat = sendState.customFeeConverter!!.toFiatUnscaled(feeCoin)
|
||||
val amountFiat = currencyConverter.toFiatUnscaled(tokensToSend)
|
||||
val totalFiat = amountFiat.plus(feeFiat)
|
||||
ReceiptTokenFiat(
|
||||
amountFiat = amountFiat.scaleToFiat(true).stripZeroPlainString(),
|
||||
feeFiat = feeFiat.scaleToFiat(true).stripZeroPlainString().addPrecisionSign(),
|
||||
totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(),
|
||||
willSentToken = tokensToSend.scaleToFiat(true).stripZeroPlainString(),
|
||||
willSentFeeCoin = feeCoin.stripZeroPlainString(),
|
||||
symbols = symbols,
|
||||
)
|
||||
} else {
|
||||
ReceiptTokenFiat(
|
||||
amountFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
|
||||
feeFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
|
||||
totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
|
||||
willSentToken = tokensToSend.stripZeroPlainString(),
|
||||
willSentFeeCoin = feeCoin.stripZeroPlainString(),
|
||||
symbols = symbols,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSameCurrencyCrypto(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptCrypto {
|
||||
val feeCrypto = feeState.getCurrentFeeValue()
|
||||
val feeFiat = convertToFiatPrecision(feeCrypto)
|
||||
if (showBlank) {
|
||||
return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", "0", "0", symbols)
|
||||
}
|
||||
val isToken = amountState.typeOfAmount is AmountType.Token
|
||||
|
||||
if (feeState.feeIsIncluded) {
|
||||
return ReceiptCrypto(
|
||||
amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripZeroPlainString(),
|
||||
feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(),
|
||||
totalCrypto = amountState.amountToSendCrypto.stripZeroPlainString(),
|
||||
feeFiat = feeFiat,
|
||||
willSentFiat = convertToFiatPrecision(value = amountState.amountToSendCrypto, isToken = isToken),
|
||||
symbols = symbols,
|
||||
)
|
||||
} else {
|
||||
val totalCrypto = amountState.amountToSendCrypto.plus(feeCrypto)
|
||||
return ReceiptCrypto(
|
||||
amountCrypto = amountState.amountToSendCrypto.stripZeroPlainString(),
|
||||
feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(),
|
||||
totalCrypto = totalCrypto.stripZeroPlainString(),
|
||||
feeFiat = feeFiat,
|
||||
willSentFiat = convertToFiatPrecision(value = totalCrypto, isToken = isToken),
|
||||
symbols = symbols,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSameCurrencyFiat(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptFiat {
|
||||
val feeCrypto = feeState.getCurrentFeeValue()
|
||||
val isToken = amountState.typeOfAmount is AmountType.Token
|
||||
val feeFiat = convertToFiatPrecision(feeCrypto, isToken = isToken)
|
||||
|
||||
if (showBlank) {
|
||||
return ReceiptFiat("0", feeFiat, "0", "0", symbols)
|
||||
}
|
||||
|
||||
return if (feeState.feeIsIncluded) {
|
||||
val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto.minus(feeCrypto), isToken = isToken)
|
||||
val totalFiat = convertToFiatPrecision(amountState.amountToSendCrypto, isToken = isToken)
|
||||
ReceiptFiat(
|
||||
amountFiat = amountFiat,
|
||||
feeFiat = feeFiat,
|
||||
totalFiat = totalFiat,
|
||||
willSentCrypto = amountState.amountToSendCrypto.scaleToFiat(true).stripZeroPlainString(),
|
||||
symbols = symbols,
|
||||
)
|
||||
} else {
|
||||
val totalAmountCrypto = amountState.amountToSendCrypto.plus(feeCrypto)
|
||||
val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto, isToken = isToken)
|
||||
val totalFiat = convertToFiatPrecision(totalAmountCrypto, isToken = isToken)
|
||||
ReceiptFiat(
|
||||
amountFiat = amountFiat,
|
||||
feeFiat = feeFiat,
|
||||
totalFiat = totalFiat,
|
||||
willSentCrypto = totalAmountCrypto.scaleToFiat().stripZeroPlainString(),
|
||||
symbols = symbols,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun determineSymbols(
|
||||
wallet: Wallet,
|
||||
amountType: AmountType,
|
||||
feePaidCurrency: FeePaidCurrency,
|
||||
): ReceiptSymbols {
|
||||
return ReceiptSymbols(
|
||||
fiat = store.state.globalState.appCurrency.code,
|
||||
crypto = wallet.blockchain.currency,
|
||||
|
|
@ -178,17 +321,52 @@ class ReceiptReducer : SendInternalReducer {
|
|||
is AmountType.Token -> amountType.token.symbol
|
||||
else -> null
|
||||
},
|
||||
fee = when (feePaidCurrency) {
|
||||
FeePaidCurrency.Coin -> wallet.blockchain.currency
|
||||
FeePaidCurrency.SameCurrency -> store.state.sendState.currency?.symbol
|
||||
is FeePaidCurrency.Token -> feePaidCurrency.token.symbol
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun determineLayoutType(mainCurrencyType: MainCurrencyType, amountType: AmountType): ReceiptLayoutType {
|
||||
private fun determineLayoutType(
|
||||
mainCurrencyType: MainCurrencyType,
|
||||
amountType: AmountType,
|
||||
feePaidCurrency: FeePaidCurrency,
|
||||
): ReceiptLayoutType {
|
||||
return when (mainCurrencyType) {
|
||||
MainCurrencyType.FIAT -> when (amountType) {
|
||||
MainCurrencyType.FIAT -> determineFiatLayoutType(feePaidCurrency, amountType)
|
||||
MainCurrencyType.CRYPTO -> determineCryptoLayoutType(feePaidCurrency, amountType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun determineFiatLayoutType(feePaidCurrency: FeePaidCurrency, amountType: AmountType): ReceiptLayoutType {
|
||||
return when (feePaidCurrency) {
|
||||
is FeePaidCurrency.Token -> {
|
||||
val amountToken = (amountType as? AmountType.Token)?.token
|
||||
val sameToken =
|
||||
feePaidCurrency.token.contractAddress.equals(amountToken?.contractAddress, ignoreCase = true)
|
||||
if (sameToken) ReceiptLayoutType.SAME_CURRENCY_FIAT else ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN_FIAT
|
||||
}
|
||||
FeePaidCurrency.Coin -> when (amountType) {
|
||||
AmountType.Coin -> ReceiptLayoutType.FIAT
|
||||
is AmountType.Token -> ReceiptLayoutType.TOKEN_FIAT
|
||||
AmountType.Reserve -> ReceiptLayoutType.UNKNOWN
|
||||
}
|
||||
MainCurrencyType.CRYPTO -> when (amountType) {
|
||||
FeePaidCurrency.SameCurrency -> ReceiptLayoutType.SAME_CURRENCY_FIAT
|
||||
}
|
||||
}
|
||||
|
||||
private fun determineCryptoLayoutType(feePaidCurrency: FeePaidCurrency, amountType: AmountType): ReceiptLayoutType {
|
||||
return when (feePaidCurrency) {
|
||||
is FeePaidCurrency.Token -> {
|
||||
val amountToken = (amountType as? AmountType.Token)?.token
|
||||
val sameToken =
|
||||
feePaidCurrency.token.contractAddress.equals(amountToken?.contractAddress, ignoreCase = true)
|
||||
if (sameToken) ReceiptLayoutType.SAME_CURRENCY else ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN
|
||||
}
|
||||
FeePaidCurrency.SameCurrency -> ReceiptLayoutType.SAME_CURRENCY
|
||||
FeePaidCurrency.Coin -> when (amountType) {
|
||||
AmountType.Coin -> ReceiptLayoutType.CRYPTO
|
||||
is AmountType.Token -> ReceiptLayoutType.TOKEN_CRYPTO
|
||||
AmountType.Reserve -> ReceiptLayoutType.UNKNOWN
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.features.send.redux.reducers
|
||||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.FeePaidCurrency
|
||||
import com.tangem.tap.common.CurrencyConverter
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
|
|
@ -91,29 +90,40 @@ private class PrepareSendScreenStatesReducer : SendInternalReducer {
|
|||
val walletManager = action.walletManager
|
||||
val amountToExtract = prepareAction.tokenAmount ?: prepareAction.coinAmount!!
|
||||
val decimals = amountToExtract.decimals
|
||||
val feePaidInNetworkCurrency = isFeePaidInNetworkCurrency(walletManager.wallet.blockchain)
|
||||
val canIncludeFee = canIncludeFee(
|
||||
typeOfAmount = amountToExtract.type,
|
||||
feePaidCurrency = action.feePaidCurrency,
|
||||
)
|
||||
|
||||
return sendState.copy(
|
||||
walletManager = walletManager,
|
||||
coinConverter = action.coinRate?.let { CurrencyConverter(it, decimals) },
|
||||
tokenConverter = action.tokenRate?.let { CurrencyConverter(it, decimals) },
|
||||
customFeeConverter = action.feeCurrencyRate?.let { CurrencyConverter(it, action.feeCurrencyDecimals) },
|
||||
amountState = sendState.amountState.copy(
|
||||
feePaidInCurrencyNetworkCurrency = feePaidInNetworkCurrency,
|
||||
amountToExtract = amountToExtract,
|
||||
typeOfAmount = amountToExtract.type,
|
||||
balanceCrypto = amountToExtract.value ?: BigDecimal.ZERO,
|
||||
),
|
||||
feeState = sendState.feeState.copy(
|
||||
includeFeeSwitcherIsEnabled = feePaidInNetworkCurrency || isCoinAmount(amountToExtract.type),
|
||||
),
|
||||
feeState = sendState.feeState.copy(includeFeeSwitcherIsEnabled = canIncludeFee),
|
||||
canIncludeFee = canIncludeFee,
|
||||
currency = action.currency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun isFeePaidInNetworkCurrency(blockchain: Blockchain): Boolean =
|
||||
// blockchain.tokenTransactionFeePaidInNetworkCurrency()
|
||||
blockchain.feePaidCurrency() == FeePaidCurrency.SameCurrency // TODO [REDACTED_TASK_KEY]
|
||||
private fun canIncludeFee(typeOfAmount: AmountType, feePaidCurrency: FeePaidCurrency): Boolean {
|
||||
return when (feePaidCurrency) {
|
||||
FeePaidCurrency.Coin -> typeOfAmount == AmountType.Coin
|
||||
FeePaidCurrency.SameCurrency -> true
|
||||
is FeePaidCurrency.Token -> {
|
||||
val sendToken = (typeOfAmount as? AmountType.Token)?.token ?: return false
|
||||
|
||||
private fun isCoinAmount(typeOfAmount: AmountType): Boolean = typeOfAmount == AmountType.Coin
|
||||
sendToken.contractAddress.equals(feePaidCurrency.token.contractAddress, ignoreCase = true) &&
|
||||
sendToken.name.equals(feePaidCurrency.token.name, ignoreCase = true) &&
|
||||
sendToken.symbol.equals(feePaidCurrency.token.symbol, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun updateLastState(sendState: SendState, lastChangedState: IdStateHolder): SendState {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@ package com.tangem.tap.features.send.redux.states
|
|||
|
||||
// Shows only one type of the layout
|
||||
enum class ReceiptLayoutType {
|
||||
UNKNOWN, FIAT, CRYPTO, TOKEN_FIAT, TOKEN_CRYPTO
|
||||
UNKNOWN,
|
||||
FIAT,
|
||||
CRYPTO,
|
||||
TOKEN_FIAT,
|
||||
TOKEN_CRYPTO,
|
||||
FEE_IN_CUSTOM_TOKEN,
|
||||
FEE_IN_CUSTOM_TOKEN_FIAT,
|
||||
SAME_CURRENCY,
|
||||
SAME_CURRENCY_FIAT,
|
||||
}
|
||||
|
||||
data class ReceiptState(
|
||||
|
|
@ -11,6 +19,10 @@ data class ReceiptState(
|
|||
val crypto: ReceiptCrypto? = null,
|
||||
val tokenFiat: ReceiptTokenFiat? = null,
|
||||
val tokenCrypto: ReceiptTokenCrypto? = null,
|
||||
val customTokenFiat: ReceiptTokenFiat? = null,
|
||||
val customTokenCrypto: ReceiptTokenCrypto? = null,
|
||||
val sameCurrencyFiat: ReceiptFiat? = null,
|
||||
val sameCurrencyCrypto: ReceiptCrypto? = null,
|
||||
val mainCurrency: MainCurrency? = null,
|
||||
) : SendScreenState {
|
||||
override val stateId: StateId = StateId.RECEIPT
|
||||
|
|
@ -20,6 +32,7 @@ data class ReceiptSymbols(
|
|||
val fiat: String,
|
||||
val crypto: String,
|
||||
val token: String? = null,
|
||||
val fee: String? = null,
|
||||
)
|
||||
|
||||
data class ReceiptFiat(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchain.common.WalletManager
|
|||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.common.CurrencyConverter
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.text.DecimalDigitsInputFilter
|
||||
|
|
@ -31,8 +32,10 @@ interface SendScreenState : StateType, IdStateHolder
|
|||
|
||||
data class SendState(
|
||||
val walletManager: WalletManager? = null,
|
||||
val currency: CryptoCurrency? = null,
|
||||
val coinConverter: CurrencyConverter? = null,
|
||||
val tokenConverter: CurrencyConverter? = null,
|
||||
val customFeeConverter: CurrencyConverter? = null,
|
||||
val lastChangedStates: LinkedHashSet<StateId> = linkedSetOf(),
|
||||
val addressState: AddressState = AddressState(),
|
||||
val transactionExtrasState: TransactionExtrasState = TransactionExtrasState(),
|
||||
|
|
@ -44,6 +47,7 @@ data class SendState(
|
|||
val dialog: StateDialog? = null,
|
||||
val externalTransactionData: ExternalTransactionData? = null,
|
||||
val isSuccessSend: Boolean = false,
|
||||
val canIncludeFee: Boolean = false,
|
||||
) : SendScreenState {
|
||||
|
||||
override val stateId: StateId = StateId.SEND_SCREEN
|
||||
|
|
@ -99,6 +103,9 @@ data class SendState(
|
|||
fun coinIsConvertible(): Boolean = coinConverter != null
|
||||
fun tokenIsConvertible(): Boolean = tokenConverter != null
|
||||
|
||||
fun currencyIsConvertible(): Boolean = coinConverter != null || tokenConverter != null
|
||||
fun feeIsConvertible(): Boolean = customFeeConverter != null
|
||||
|
||||
fun mainCurrencyCanBeSwitched(): Boolean {
|
||||
return when (amountState.typeOfAmount) {
|
||||
AmountType.Coin -> coinIsConvertible()
|
||||
|
|
@ -137,16 +144,13 @@ data class AmountState(
|
|||
val decimalSeparator: String = ".",
|
||||
val error: TapError? = null,
|
||||
val inputIsEnabled: Boolean = true,
|
||||
private val feePaidInCurrencyNetworkCurrency: Boolean = false,
|
||||
) : SendScreenState {
|
||||
|
||||
override val stateId: StateId = StateId.AMOUNT
|
||||
|
||||
fun isReady(): Boolean = error == null && !amountToSendCrypto.isZero()
|
||||
|
||||
private fun isCoinAmount(): Boolean = typeOfAmount == AmountType.Coin
|
||||
|
||||
fun canIncludeFee(): Boolean = isCoinAmount() || feePaidInCurrencyNetworkCurrency
|
||||
fun canIncludeFee(): Boolean = store.state.sendState.canIncludeFee
|
||||
|
||||
fun createMainCurrency(type: MainCurrencyType, canSwitched: Boolean): MainCurrency {
|
||||
return if (!canSwitched) {
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ internal class SendStateSubscriber(
|
|||
}
|
||||
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
private fun handleReceiptState(fg: SendFragment, state: ReceiptState, feeProgressState: ProgressState) =
|
||||
private fun handleReceiptState(fg: SendFragment, state: ReceiptState, feeProgressState: ProgressState) {
|
||||
with(fg.binding.clReceiptContainer) {
|
||||
val mainLayout = clReceiptContainer as ViewGroup
|
||||
val totalLayout = llTotalContainer.llTotal as ViewGroup
|
||||
|
|
@ -410,9 +410,96 @@ internal class SendStateSubscriber(
|
|||
}
|
||||
llTotalContainer.tvTotalTokenCryptoValue.update(willSent.toString())
|
||||
}
|
||||
else -> {}
|
||||
ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN -> {
|
||||
val receipt = state.customTokenCrypto ?: return
|
||||
|
||||
totalLayout.show(false)
|
||||
totalTokenLayout.show(true)
|
||||
|
||||
tvReceiptAmountValue.update(
|
||||
"${receipt.amountToken} ${receipt.symbols.token ?: receipt.symbols.crypto}",
|
||||
)
|
||||
tvReceiptFeeValue.update("${receipt.feeCoin} ${receipt.symbols.fee}")
|
||||
|
||||
val willSent = SpannableStringBuilder()
|
||||
.bold {
|
||||
append(roughOrEmpty(receipt.totalFiat)).append(" ")
|
||||
append(receipt.symbols.fiat)
|
||||
}
|
||||
llTotalContainer.tvTotalTokenCryptoValue.update(willSent.toString())
|
||||
}
|
||||
ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN_FIAT -> {
|
||||
val receipt = state.customTokenFiat ?: return
|
||||
|
||||
totalLayout.show(true)
|
||||
totalTokenLayout.show(false)
|
||||
tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}")
|
||||
tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}")
|
||||
llTotalContainer.tvTotalValue.post {
|
||||
llTotalContainer.tvTotalValue.update(
|
||||
"${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}",
|
||||
)
|
||||
}
|
||||
|
||||
val willSent = getString(
|
||||
R.string.send_total_subtitle_asset_format,
|
||||
"${receipt.symbols.token ?: receipt.symbols.crypto} ${receipt.willSentToken}",
|
||||
"${receipt.symbols.fee} ${receipt.willSentFeeCoin}",
|
||||
)
|
||||
llTotalContainer.tvWillBeSentValue.update(willSent)
|
||||
}
|
||||
|
||||
ReceiptLayoutType.SAME_CURRENCY -> {
|
||||
val receipt = state.sameCurrencyCrypto ?: return
|
||||
|
||||
totalLayout.show(true)
|
||||
totalTokenLayout.show(false)
|
||||
tvReceiptAmountValue.update(
|
||||
"${receipt.amountCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}",
|
||||
)
|
||||
tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}")
|
||||
llTotalContainer.tvTotalValue.post {
|
||||
llTotalContainer.tvTotalValue.update(
|
||||
"${receipt.totalCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}",
|
||||
)
|
||||
}
|
||||
|
||||
if (receipt.willSentFiat == BigDecimalFormatter.EMPTY_BALANCE_SIGN) {
|
||||
llTotalContainer.tvWillBeSentValue.hide()
|
||||
} else {
|
||||
llTotalContainer.tvWillBeSentValue.show()
|
||||
llTotalContainer.tvWillBeSentValue.update(
|
||||
getString(
|
||||
R.string.send_total_subtitle_fiat_format,
|
||||
"${receipt.willSentFiat} ${receipt.symbols.fiat}",
|
||||
"${receipt.feeFiat} ${receipt.symbols.fiat}",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
ReceiptLayoutType.SAME_CURRENCY_FIAT -> {
|
||||
val receipt = state.sameCurrencyFiat ?: return
|
||||
|
||||
totalLayout.show(true)
|
||||
totalTokenLayout.show(false)
|
||||
tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}")
|
||||
tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}")
|
||||
llTotalContainer.tvTotalValue.post {
|
||||
llTotalContainer.tvTotalValue.update(
|
||||
"${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}",
|
||||
)
|
||||
}
|
||||
|
||||
val willSent = getString(
|
||||
R.string.send_total_subtitle_format,
|
||||
"${receipt.willSentCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}",
|
||||
)
|
||||
llTotalContainer.tvWillBeSentValue.update(willSent)
|
||||
}
|
||||
ReceiptLayoutType.UNKNOWN, null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ROUGH_SIGN = "≈"
|
||||
|
|
|
|||
|
|
@ -210,10 +210,14 @@ object TradeCryptoMiddleware {
|
|||
store.dispatchOnMain(
|
||||
action = PrepareSendScreen(
|
||||
walletManager = walletManager,
|
||||
feePaidCurrency = walletManager.wallet.blockchain.feePaidCurrency(),
|
||||
currency = currency,
|
||||
coinAmount = walletManager.wallet.amounts[AmountType.Coin],
|
||||
coinRate = action.coinFiatRate,
|
||||
tokenAmount = sendableAmount,
|
||||
tokenRate = action.tokenFiatRate,
|
||||
feeCurrencyRate = action.feeCurrencyStatus?.value?.fiatRate,
|
||||
feeCurrencyDecimals = action.feeCurrencyStatus?.currency?.decimals ?: 0,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -262,8 +266,12 @@ object TradeCryptoMiddleware {
|
|||
store.dispatchOnMain(
|
||||
action = PrepareSendScreen(
|
||||
walletManager = walletManager,
|
||||
feePaidCurrency = walletManager.wallet.blockchain.feePaidCurrency(),
|
||||
currency = currency,
|
||||
coinAmount = amountToSend,
|
||||
coinRate = cryptoStatus.value.fiatRate,
|
||||
feeCurrencyRate = action.feeCurrencyStatus?.value?.fiatRate,
|
||||
feeCurrencyDecimals = action.feeCurrencyStatus?.currency?.decimals ?: 0,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -715,7 +715,7 @@
|
|||
<string name="warning_rate_app_message">Ваш отзыв мотивирует нас сделать кошелек Tangem еще лучше</string>
|
||||
<string name="warning_rate_app_title">Нравится Tangem?</string>
|
||||
<string name="warning_rent_fee_title">Необходима плата за аренду сети</string>
|
||||
<string name="warning_send_blocked_funds_for_fee_message">%1$s - это токен в сети %2$s. Для совершения транзакции %3$s, вам необходимо внести депозит в размере %4$s (%5$s), чтобы покрыть комиссию сети.</string>
|
||||
<string name="warning_send_blocked_funds_for_fee_message">%1$s - это монета в сети %2$s. Для совершения транзакции %3$s, вам необходимо внести немного %4$s (%5$s), чтобы покрыть комиссию сети.</string>
|
||||
<string name="warning_send_blocked_funds_for_fee_title">Недостаточно %1$s для оплаты комиссии сети</string>
|
||||
<string name="warning_send_blocked_pending_transactions_message">Отправка средств станет доступной после завершения транзакции %s</string>
|
||||
<string name="warning_send_blocked_pending_transactions_title">Транзакция в обработке</string>
|
||||
|
|
|
|||
|
|
@ -728,7 +728,7 @@
|
|||
<string name="warning_rate_app_message">Your review keeps us motivated to make Tangem Wallet even better</string>
|
||||
<string name="warning_rate_app_title">Enjoying Tangem?</string>
|
||||
<string name="warning_rent_fee_title">Network rent fee required</string>
|
||||
<string name="warning_send_blocked_funds_for_fee_message">%1$s is a token in the %2$s network. To make a %3$s transaction, you must deposit some %4$s (%5$s) to cover the network fee.</string>
|
||||
<string name="warning_send_blocked_funds_for_fee_message">%1$s is an asset in the %2$s network. To make a %3$s transaction, you must deposit some %4$s (%5$s) to cover the network fee.</string>
|
||||
<string name="warning_send_blocked_funds_for_fee_title">Insufficient %1$s to cover network fee</string>
|
||||
<string name="warning_send_blocked_pending_transactions_message">Sending funds will be available once the %s transaction is complete</string>
|
||||
<string name="warning_send_blocked_pending_transactions_title">Transaction pending</string>
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ fun getActiveIconRes(blockchainId: String): Int {
|
|||
"NEAR", "NEAR/test" -> R.drawable.img_near_22
|
||||
"decimal", "decimal/test" -> R.drawable.img_decimal_22
|
||||
"xdc", "xdc/test" -> R.drawable.img_xdc_22
|
||||
"vechain", "vechain/test" -> R.drawable.img_vechain_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +96,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int {
|
|||
"near-protocol", "near-protocol/test" -> R.drawable.img_near_22
|
||||
"decimal", "decimal/test" -> R.drawable.img_decimal_22
|
||||
"xdc-network", "xdc-network/test" -> R.drawable.img_xdc_22
|
||||
"vechain", "vechain/test" -> R.drawable.img_vechain_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -140,6 +142,7 @@ fun getActiveIconResByCoinId(coinId: String): Int {
|
|||
"near" -> R.drawable.img_near_22
|
||||
"decimal" -> R.drawable.img_decimal_22
|
||||
"xdce-crowd-sale" -> R.drawable.img_xdc_22
|
||||
"vechain" -> R.drawable.img_vechain_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -188,6 +191,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
|
|||
"NEAR", "NEAR/test" -> R.drawable.ic_near_22
|
||||
"decimal", "decimal/test" -> R.drawable.ic_decimal_22
|
||||
"xdc", "xdc/test" -> R.drawable.ic_xdc_22
|
||||
"vechain", "vechain/test" -> R.drawable.ic_vechain_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -236,6 +240,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int {
|
|||
"near-protocol", "near-protocol/test" -> R.drawable.ic_near_22
|
||||
"decimal", "decimal/test" -> R.drawable.ic_decimal_22
|
||||
"xdc-network", "xdc-network/test" -> R.drawable.ic_xdc_22
|
||||
"vechain", "vechain/test" -> R.drawable.ic_vechain_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
9
core/ui/src/main/res/drawable/ic_vechain_22.xml
Normal file
9
core/ui/src/main/res/drawable/ic_vechain_22.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M17.25,5H16.135C15.863,5 15.613,5.166 15.496,5.423L12.561,11.859L12.558,11.851L11.777,13.565L11.78,13.573L10.999,15.288L7.096,6.717H8.209C8.481,6.717 8.73,6.883 8.848,7.141L11.396,12.7L12.177,10.986L10.12,6.499C9.704,5.584 8.825,5 7.862,5H4.75L5.529,6.717H5.531L10.215,17H11.777L17.25,5Z" />
|
||||
</vector>
|
||||
16
core/ui/src/main/res/drawable/img_vechain_22.xml
Normal file
16
core/ui/src/main/res/drawable/img_vechain_22.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"/>
|
||||
<path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"
|
||||
android:fillColor="#82BE00"/>
|
||||
<path
|
||||
android:pathData="M17.25,5H16.135C15.863,5 15.613,5.166 15.496,5.423L12.561,11.859L12.558,11.851L11.777,13.565L11.78,13.573L10.999,15.288L7.096,6.717H8.209C8.481,6.717 8.73,6.883 8.848,7.141L11.396,12.7L12.177,10.986L10.12,6.499C9.704,5.584 8.825,5 7.862,5H4.75L5.529,6.717H5.531L10.215,17H11.777L17.25,5Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -30,6 +30,7 @@ internal object TokensDataModule {
|
|||
tangemExpressApi: TangemExpressApi,
|
||||
userTokensStore: UserTokensStore,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
assetsStore: AssetsStore,
|
||||
cacheRegistry: CacheRegistry,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -38,6 +39,7 @@ internal object TokensDataModule {
|
|||
tangemTechApi = tangemTechApi,
|
||||
tangemExpressApi = tangemExpressApi,
|
||||
userTokensStore = userTokensStore,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
userWalletsStore = userWalletsStore,
|
||||
assetsStore = assetsStore,
|
||||
cacheRegistry = cacheRegistry,
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ import com.tangem.domain.core.error.DataError
|
|||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -32,6 +34,7 @@ import kotlinx.coroutines.flow.*
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.FeePaidCurrency as FeePaidSdkCurrency
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
internal class DefaultCurrenciesRepository(
|
||||
|
|
@ -39,6 +42,7 @@ internal class DefaultCurrenciesRepository(
|
|||
private val tangemExpressApi: TangemExpressApi,
|
||||
private val userTokensStore: UserTokensStore,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val assetsStore: AssetsStore,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -323,6 +327,32 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getFeePaidCurrency(userWalletId: UserWalletId, currency: CryptoCurrency): FeePaidCurrency {
|
||||
val blockchain = Blockchain.fromId(currency.network.id.value)
|
||||
return when (val feePaidCurrency = blockchain.feePaidCurrency()) {
|
||||
FeePaidSdkCurrency.Coin -> FeePaidCurrency.Coin
|
||||
FeePaidSdkCurrency.SameCurrency -> FeePaidCurrency.SameCurrency
|
||||
is FeePaidSdkCurrency.Token -> {
|
||||
val balance = walletManagersFacade.tokenBalance(
|
||||
userWalletId = userWalletId,
|
||||
network = currency.network,
|
||||
name = feePaidCurrency.token.name,
|
||||
symbol = feePaidCurrency.token.symbol,
|
||||
contractAddress = feePaidCurrency.token.contractAddress,
|
||||
decimals = feePaidCurrency.token.decimals,
|
||||
id = feePaidCurrency.token.id,
|
||||
)
|
||||
FeePaidCurrency.Token(
|
||||
tokenId = getTokenId(network = currency.network, sdkToken = feePaidCurrency.token),
|
||||
name = feePaidCurrency.token.name,
|
||||
symbol = feePaidCurrency.token.symbol,
|
||||
contractAddress = feePaidCurrency.token.contractAddress,
|
||||
balance = balance,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow<List<CryptoCurrency>> {
|
||||
return userTokensStore.get(userWallet.walletId).map { storedTokens ->
|
||||
responseCurrenciesFactory.createCurrencies(
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
|
|||
"decimal/test" -> Blockchain.DecimalTestnet
|
||||
"xdc-network" -> Blockchain.XDC
|
||||
"xdc-network/test" -> Blockchain.XDCTestnet
|
||||
"TODO" -> Blockchain.Vechain // TODO [REDACTED_TASK_KEY]
|
||||
"TODO/test" -> Blockchain.VechainTestnet // TODO [REDACTED_TASK_KEY]
|
||||
"vechain" -> Blockchain.Vechain
|
||||
"vechain/test" -> Blockchain.VechainTestnet
|
||||
"aptos" -> Blockchain.Aptos
|
||||
"aptos/test" -> Blockchain.AptosTestnet
|
||||
else -> null
|
||||
|
|
@ -157,8 +157,8 @@ fun Blockchain.toNetworkId(): String {
|
|||
Blockchain.DecimalTestnet -> "decimal/test"
|
||||
Blockchain.XDC -> "xdc-network"
|
||||
Blockchain.XDCTestnet -> "xdc-network/test"
|
||||
Blockchain.Vechain -> "TODO" // TODO [REDACTED_TASK_KEY]
|
||||
Blockchain.VechainTestnet -> "TODO/test" // TODO [REDACTED_TASK_KEY]
|
||||
Blockchain.Vechain -> "vechain"
|
||||
Blockchain.VechainTestnet -> "vechain/test"
|
||||
Blockchain.Aptos -> "aptos"
|
||||
Blockchain.AptosTestnet -> "aptos/test"
|
||||
}
|
||||
|
|
@ -209,8 +209,7 @@ fun Blockchain.toCoinId(): String {
|
|||
Blockchain.NearTestnet -> "near/test"
|
||||
Blockchain.Decimal, Blockchain.DecimalTestnet -> "decimal"
|
||||
Blockchain.XDC, Blockchain.XDCTestnet -> "xdce-crowd-sale"
|
||||
Blockchain.Vechain -> "TODO" // TODO [REDACTED_TASK_KEY]
|
||||
Blockchain.VechainTestnet -> "TODO/test" // TODO [REDACTED_TASK_KEY]
|
||||
Blockchain.Vechain, Blockchain.VechainTestnet -> "vechain"
|
||||
Blockchain.Aptos -> "aptos"
|
||||
Blockchain.AptosTestnet -> "aptos/test"
|
||||
Blockchain.Unknown -> "unknown"
|
||||
|
|
@ -240,6 +239,4 @@ private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5
|
|||
private val excludedBlockchains = listOf(
|
||||
Blockchain.Unknown,
|
||||
Blockchain.Ducatus,
|
||||
Blockchain.Vechain, // TODO [REDACTED_TASK_KEY]
|
||||
Blockchain.VechainTestnet, // TODO [REDACTED_TASK_KEY]
|
||||
)
|
||||
|
|
@ -565,6 +565,35 @@ class DefaultWalletManagersFacade(
|
|||
return walletManager.wallet.recentTransactions.mapNotNull(transactionDataConverter::convert)
|
||||
}
|
||||
|
||||
override suspend fun tokenBalance(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
name: String,
|
||||
symbol: String,
|
||||
contractAddress: String,
|
||||
decimals: Int,
|
||||
id: String?,
|
||||
): BigDecimal {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
)
|
||||
requireNotNull(walletManager) { "Unable to get a wallet manager for blockchain: $blockchain" }
|
||||
return walletManager.wallet.fundsAvailable(
|
||||
AmountType.Token(
|
||||
token = Token(
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
contractAddress = contractAddress,
|
||||
decimals = decimals,
|
||||
id = id,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set<CryptoCurrency.Token>) {
|
||||
if (tokens.isEmpty()) return
|
||||
|
||||
|
|
|
|||
|
|
@ -270,4 +270,15 @@ interface WalletManagersFacade {
|
|||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
): List<TxHistoryItem>
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun tokenBalance(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
name: String,
|
||||
symbol: String,
|
||||
contractAddress: String,
|
||||
decimals: Int,
|
||||
id: String? = null,
|
||||
): BigDecimal
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class FeePaidCurrency {
|
||||
object Coin : FeePaidCurrency()
|
||||
object SameCurrency : FeePaidCurrency()
|
||||
data class Token(
|
||||
val tokenId: CryptoCurrency.ID,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val contractAddress: String,
|
||||
val balance: BigDecimal,
|
||||
) : FeePaidCurrency()
|
||||
}
|
||||
|
|
@ -15,6 +15,14 @@ sealed class CryptoCurrencyWarning {
|
|||
val coinCurrency: CryptoCurrency,
|
||||
) : CryptoCurrencyWarning()
|
||||
|
||||
data class CustomTokenNotEnoughForFee(
|
||||
val currency: CryptoCurrency,
|
||||
val feeCurrency: CryptoCurrency?,
|
||||
val networkName: String,
|
||||
val feeCurrencyName: String,
|
||||
val feeCurrencySymbol: String,
|
||||
) : CryptoCurrencyWarning()
|
||||
|
||||
object SomeNetworksUnreachable : CryptoCurrencyWarning()
|
||||
|
||||
data class SomeNetworksNoAccount(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
|
|
@ -11,10 +12,12 @@ import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository
|
|||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Use case to determine which TokenActions are available for a [CryptoCurrency]
|
||||
|
|
@ -104,7 +107,13 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
activeList.add(TokenActionsState.ActionState.Receive(true))
|
||||
|
||||
// send
|
||||
if (isSendDisabled(cryptoCurrencyStatus = cryptoCurrencyStatus, coinStatus = coinStatus)) {
|
||||
if (
|
||||
isSendDisabled(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
) {
|
||||
disabledList.add(TokenActionsState.ActionState.Send(false))
|
||||
} else {
|
||||
activeList.add(TokenActionsState.ActionState.Send(true))
|
||||
|
|
@ -157,13 +166,41 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
return activeList + disabledList
|
||||
}
|
||||
|
||||
private fun isSendDisabled(
|
||||
private suspend fun isSendDisabled(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): Boolean = cryptoCurrencyStatus.value.amount.isNullOrZero() ||
|
||||
coinStatus?.value?.amount.isNullOrZero() ||
|
||||
currenciesRepository.hasPendingTransactions(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
): Boolean {
|
||||
val feePaidCurrency = currenciesRepository.getFeePaidCurrency(userWalletId, cryptoCurrencyStatus.currency)
|
||||
val notEnoughBalanceForFee = isNotEnoughBalanceForFee(
|
||||
feePaidCurrency = feePaidCurrency,
|
||||
tokenStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
return cryptoCurrencyStatus.value.amount.isNullOrZero() ||
|
||||
notEnoughBalanceForFee ||
|
||||
currenciesRepository.hasPendingTransactions(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
}
|
||||
|
||||
private fun isNotEnoughBalanceForFee(
|
||||
feePaidCurrency: FeePaidCurrency,
|
||||
tokenStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): Boolean {
|
||||
return when (feePaidCurrency) {
|
||||
FeePaidCurrency.Coin -> !tokenStatus.value.amount.isZero() && coinStatus?.value?.amount.isZero()
|
||||
FeePaidCurrency.SameCurrency -> tokenStatus.value.amount.isZero()
|
||||
is FeePaidCurrency.Token -> {
|
||||
val feePaidTokenBalance = feePaidCurrency.balance
|
||||
!tokenStatus.value.amount.isZero() && feePaidTokenBalance.isZero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal?.isZero(): Boolean {
|
||||
return this?.signum() == 0
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.domain.tokens
|
|||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
|
|
@ -47,6 +48,7 @@ class GetCurrencyWarningsUseCase(
|
|||
)
|
||||
return combine(
|
||||
getCoinRelatedWarnings(
|
||||
userWalletId = userWalletId,
|
||||
operations = operations,
|
||||
networkId = currency.network.id,
|
||||
currencyId = currency.id,
|
||||
|
|
@ -136,6 +138,7 @@ class GetCurrencyWarningsUseCase(
|
|||
|
||||
@Suppress("LongParameterList")
|
||||
private suspend fun getCoinRelatedWarnings(
|
||||
userWalletId: UserWalletId,
|
||||
operations: CurrenciesStatusesOperations,
|
||||
networkId: Network.ID,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
|
|
@ -162,14 +165,11 @@ class GetCurrencyWarningsUseCase(
|
|||
if (currenciesRepository.hasPendingTransactions(tokenStatus, coinStatus)) {
|
||||
add(CryptoCurrencyWarning.HasPendingTransactions(coinStatus.currency.symbol))
|
||||
}
|
||||
if (!tokenStatus.value.amount.isZero() && coinStatus.value.amount.isZero()) {
|
||||
add(
|
||||
CryptoCurrencyWarning.BalanceNotEnoughForFee(
|
||||
tokenCurrency = tokenStatus.currency,
|
||||
coinCurrency = coinStatus.currency,
|
||||
),
|
||||
)
|
||||
}
|
||||
getFeeWarning(
|
||||
userWalletId = userWalletId,
|
||||
coinStatus = coinStatus,
|
||||
tokenStatus = tokenStatus,
|
||||
)?.let(::add)
|
||||
}
|
||||
}
|
||||
else -> listOf(CryptoCurrencyWarning.SomeNetworksUnreachable)
|
||||
|
|
@ -177,6 +177,72 @@ class GetCurrencyWarningsUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getFeeWarning(
|
||||
userWalletId: UserWalletId,
|
||||
coinStatus: CryptoCurrencyStatus,
|
||||
tokenStatus: CryptoCurrencyStatus,
|
||||
): CryptoCurrencyWarning? {
|
||||
val feePaidCurrency = currenciesRepository.getFeePaidCurrency(userWalletId, tokenStatus.currency)
|
||||
return when {
|
||||
feePaidCurrency is FeePaidCurrency.Coin &&
|
||||
!tokenStatus.value.amount.isZero() &&
|
||||
coinStatus.value.amount.isZero() -> {
|
||||
CryptoCurrencyWarning.BalanceNotEnoughForFee(
|
||||
tokenCurrency = tokenStatus.currency,
|
||||
coinCurrency = coinStatus.currency,
|
||||
)
|
||||
}
|
||||
feePaidCurrency is FeePaidCurrency.SameCurrency && !tokenStatus.value.amount.isZero() -> {
|
||||
CryptoCurrencyWarning.BalanceNotEnoughForFee(
|
||||
tokenCurrency = tokenStatus.currency,
|
||||
coinCurrency = coinStatus.currency,
|
||||
)
|
||||
}
|
||||
feePaidCurrency is FeePaidCurrency.Token -> {
|
||||
val feePaidTokenBalance = feePaidCurrency.balance
|
||||
if (!tokenStatus.value.amount.isZero() && feePaidTokenBalance.isZero()) {
|
||||
constructTokenBalanceNotEnoughWarning(
|
||||
userWalletId = userWalletId,
|
||||
tokenStatus = tokenStatus,
|
||||
feePaidToken = feePaidCurrency,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun constructTokenBalanceNotEnoughWarning(
|
||||
userWalletId: UserWalletId,
|
||||
tokenStatus: CryptoCurrencyStatus,
|
||||
feePaidToken: FeePaidCurrency.Token,
|
||||
): CryptoCurrencyWarning {
|
||||
val token = currenciesRepository
|
||||
.getMultiCurrencyWalletCurrenciesSync(userWalletId)
|
||||
.find {
|
||||
it is CryptoCurrency.Token && it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true)
|
||||
}
|
||||
return if (token != null) {
|
||||
CryptoCurrencyWarning.CustomTokenNotEnoughForFee(
|
||||
currency = tokenStatus.currency,
|
||||
feeCurrency = token,
|
||||
networkName = token.network.name,
|
||||
feeCurrencyName = feePaidToken.name,
|
||||
feeCurrencySymbol = feePaidToken.symbol,
|
||||
)
|
||||
} else {
|
||||
CryptoCurrencyWarning.CustomTokenNotEnoughForFee(
|
||||
currency = tokenStatus.currency,
|
||||
feeCurrency = null,
|
||||
networkName = tokenStatus.currency.network.name,
|
||||
feeCurrencyName = feePaidToken.name,
|
||||
feeCurrencySymbol = feePaidToken.symbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNetworkUnavailableWarning(currencyStatus: CryptoCurrencyStatus): CryptoCurrencyWarning? {
|
||||
return (currencyStatus.value as? CryptoCurrencyStatus.Unreachable)?.let {
|
||||
CryptoCurrencyWarning.SomeNetworksUnreachable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
class GetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
internal val currenciesRepository: CurrenciesRepository,
|
||||
internal val quotesRepository: QuotesRepository,
|
||||
internal val networksRepository: NetworksRepository,
|
||||
internal val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): Either<TokenListError, CryptoCurrencyStatus?> {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val feePaidCurrency = currenciesRepository.getFeePaidCurrency(userWalletId, cryptoCurrency)
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
userWalletId = userWalletId,
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
)
|
||||
|
||||
return either {
|
||||
when (feePaidCurrency) {
|
||||
FeePaidCurrency.Coin -> getCryptoCurrency(operations, cryptoCurrency.id)
|
||||
FeePaidCurrency.SameCurrency -> cryptoCurrencyStatus
|
||||
is FeePaidCurrency.Token -> getCryptoCurrency(operations, feePaidCurrency.tokenId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getCryptoCurrency(
|
||||
operations: CurrenciesStatusesOperations,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): CryptoCurrencyStatus? {
|
||||
return operations.getCurrencyStatusSync(cryptoCurrencyId).getOrNull()
|
||||
}
|
||||
}
|
||||
|
|
@ -36,9 +36,14 @@ sealed class TradeCryptoAction : Action {
|
|||
val tokenCurrency: CryptoCurrency.Token,
|
||||
val tokenFiatRate: BigDecimal?,
|
||||
val coinFiatRate: BigDecimal?,
|
||||
val feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) : New()
|
||||
|
||||
data class SendCoin(val userWallet: UserWallet, val coinStatus: CryptoCurrencyStatus) : New()
|
||||
data class SendCoin(
|
||||
val userWallet: UserWallet,
|
||||
val coinStatus: CryptoCurrencyStatus,
|
||||
val feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) : New()
|
||||
|
||||
data class Swap(val cryptoCurrency: CryptoCurrency) : New()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.tokens.repository
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -175,4 +176,9 @@ interface CurrenciesRepository {
|
|||
* @param coinStatus main currency status in [cryptoCurrencyStatus] network
|
||||
*/
|
||||
fun hasPendingTransactions(cryptoCurrencyStatus: CryptoCurrencyStatus, coinStatus: CryptoCurrencyStatus?): Boolean
|
||||
|
||||
/**
|
||||
* Retrieves fee paid currency for specific [currency].
|
||||
*/
|
||||
suspend fun getFeePaidCurrency(userWalletId: UserWalletId, currency: CryptoCurrency): FeePaidCurrency
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import arrow.core.getOrElse
|
|||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -115,4 +116,8 @@ internal class MockCurrenciesRepository(
|
|||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override suspend fun getFeePaidCurrency(userWalletId: UserWalletId, currency: CryptoCurrency): FeePaidCurrency {
|
||||
return FeePaidCurrency.Coin
|
||||
}
|
||||
}
|
||||
|
|
@ -647,7 +647,10 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
gasPrice = (feeAmountWithDecimals / fee.gasLimit.toBigDecimal()).toBigInteger(),
|
||||
)
|
||||
}
|
||||
Blockchain.Vechain -> TODO("[REDACTED_TASK_KEY]")
|
||||
Blockchain.Vechain -> Fee.Vechain(
|
||||
amount = feeAmount,
|
||||
gasPriceCoef = fee.gasLimit,
|
||||
)
|
||||
Blockchain.Aptos -> {
|
||||
Fee.Aptos(
|
||||
amount = feeAmount,
|
||||
|
|
@ -1342,7 +1345,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
return when (this) {
|
||||
is Fee.Common -> 0
|
||||
is Fee.Ethereum -> gasLimit.toInt()
|
||||
is Fee.Vechain -> TODO("[REDACTED_TASK_KEY]")
|
||||
is Fee.Vechain -> gasPriceCoef
|
||||
is Fee.Aptos -> amount.longValue?.div(gasUnitPrice)?.toInt() ?: 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ internal class TokenDetailsNotificationsAnalyticsSender(
|
|||
|
||||
private fun getEvent(notification: TokenDetailsNotification): AnalyticsEvent? {
|
||||
return when (notification) {
|
||||
is TokenDetailsNotification.NetworkFee -> TokenDetailsAnalyticsEvent.Notice.NotEnoughFee(
|
||||
is TokenDetailsNotification.NetworkFee,
|
||||
is TokenDetailsNotification.NetworkFeeWithBuyButton,
|
||||
-> TokenDetailsAnalyticsEvent.Notice.NotEnoughFee(
|
||||
currency = cryptoCurrency,
|
||||
)
|
||||
is TokenDetailsNotification.NetworksUnreachable,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.networkIconResId
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
||||
|
|
@ -62,28 +63,55 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
|
|||
)
|
||||
|
||||
data class NetworkFee(
|
||||
private val feeInfo: CryptoCurrencyWarning.BalanceNotEnoughForFee,
|
||||
private val onBuyClick: () -> Unit,
|
||||
private val currency: CryptoCurrency,
|
||||
private val networkName: String,
|
||||
private val feeCurrencyName: String,
|
||||
private val feeCurrencySymbol: String,
|
||||
) : Warning(
|
||||
title = TextReference.Res(
|
||||
id = R.string.warning_send_blocked_funds_for_fee_title,
|
||||
formatArgs = wrappedList(feeInfo.coinCurrency.name),
|
||||
formatArgs = wrappedList(feeCurrencyName),
|
||||
),
|
||||
subtitle = TextReference.Res(
|
||||
id = R.string.warning_send_blocked_funds_for_fee_message,
|
||||
formatArgs = wrappedList(
|
||||
feeInfo.tokenCurrency.name,
|
||||
feeInfo.coinCurrency.name,
|
||||
feeInfo.tokenCurrency.name,
|
||||
feeInfo.coinCurrency.name,
|
||||
feeInfo.coinCurrency.symbol,
|
||||
currency.name,
|
||||
networkName,
|
||||
currency.name,
|
||||
feeCurrencyName,
|
||||
feeCurrencySymbol,
|
||||
),
|
||||
),
|
||||
iconResId = feeInfo.tokenCurrency.networkIconResId,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
iconResId = currency.networkIconResId,
|
||||
)
|
||||
|
||||
data class NetworkFeeWithBuyButton(
|
||||
private val currency: CryptoCurrency,
|
||||
private val networkName: String,
|
||||
private val feeCurrencyName: String,
|
||||
private val feeCurrencySymbol: String,
|
||||
private val onBuyClick: () -> Unit,
|
||||
) : Warning(
|
||||
title = TextReference.Res(
|
||||
id = R.string.warning_send_blocked_funds_for_fee_title,
|
||||
formatArgs = wrappedList(feeCurrencyName),
|
||||
),
|
||||
subtitle = TextReference.Res(
|
||||
id = R.string.warning_send_blocked_funds_for_fee_message,
|
||||
formatArgs = wrappedList(
|
||||
currency.name,
|
||||
networkName,
|
||||
currency.name,
|
||||
feeCurrencyName,
|
||||
feeCurrencySymbol,
|
||||
),
|
||||
),
|
||||
iconResId = currency.networkIconResId,
|
||||
buttonsState =
|
||||
NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(
|
||||
id = R.string.common_buy_currency,
|
||||
formatArgs = wrappedList(feeInfo.coinCurrency.symbol),
|
||||
formatArgs = wrappedList(feeCurrencySymbol),
|
||||
),
|
||||
onClick = onBuyClick,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -25,10 +25,32 @@ internal class TokenDetailsNotificationConverter(
|
|||
|
||||
private fun mapToNotification(warning: CryptoCurrencyWarning): TokenDetailsNotification {
|
||||
return when (warning) {
|
||||
is CryptoCurrencyWarning.BalanceNotEnoughForFee -> TokenDetailsNotification.NetworkFee(
|
||||
feeInfo = warning,
|
||||
is CryptoCurrencyWarning.BalanceNotEnoughForFee -> TokenDetailsNotification.NetworkFeeWithBuyButton(
|
||||
currency = warning.tokenCurrency,
|
||||
networkName = warning.coinCurrency.name,
|
||||
feeCurrencyName = warning.coinCurrency.name,
|
||||
feeCurrencySymbol = warning.coinCurrency.symbol,
|
||||
onBuyClick = { clickIntents.onBuyCoinClick(warning.coinCurrency) },
|
||||
)
|
||||
is CryptoCurrencyWarning.CustomTokenNotEnoughForFee -> {
|
||||
val feeCurrency = warning.feeCurrency
|
||||
if (feeCurrency != null) {
|
||||
TokenDetailsNotification.NetworkFeeWithBuyButton(
|
||||
currency = warning.currency,
|
||||
networkName = feeCurrency.network.name,
|
||||
feeCurrencyName = warning.feeCurrencyName,
|
||||
feeCurrencySymbol = warning.feeCurrencySymbol,
|
||||
onBuyClick = { clickIntents.onBuyCoinClick(feeCurrency) },
|
||||
)
|
||||
} else {
|
||||
TokenDetailsNotification.NetworkFee(
|
||||
currency = warning.currency,
|
||||
networkName = warning.networkName,
|
||||
feeCurrencyName = warning.feeCurrencyName,
|
||||
feeCurrencySymbol = warning.feeCurrencySymbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
is CryptoCurrencyWarning.ExistentialDeposit -> TokenDetailsNotification.ExistentialDeposit(
|
||||
existentialInfo = warning,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
|
||||
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
|
|
@ -356,23 +357,35 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
val cryptoCurrencyStatus = cryptoCurrencyStatus ?: return
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, cryptoCurrencyStatus).getOrNull()
|
||||
|
||||
when (val currency = cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
userWallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch },
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
is CryptoCurrency.Token -> {
|
||||
sendToken(tokenCurrency = currency, tokenFiatRate = cryptoCurrencyStatus.value.fiatRate)
|
||||
sendToken(
|
||||
tokenCurrency = currency,
|
||||
tokenFiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendToken(tokenCurrency: CryptoCurrency.Token, tokenFiatRate: BigDecimal?) {
|
||||
private fun sendToken(
|
||||
tokenCurrency: CryptoCurrency.Token,
|
||||
tokenFiatRate: BigDecimal?,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch }
|
||||
val maybeCoinStatus = getNetworkCoinStatusUseCase(
|
||||
|
|
@ -394,6 +407,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
ifLeft = { null },
|
||||
ifRight = { it.value.fiatRate },
|
||||
),
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val unlockWalletsUseCase: UnlockWalletsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase,
|
||||
private val canUseBiometryUseCase: CanUseBiometryUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
|
|
@ -636,9 +637,17 @@ internal class WalletViewModel @Inject constructor(
|
|||
event = TokenScreenAnalyticsEvent.ButtonSend(coinStatus.currency.symbol),
|
||||
)
|
||||
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(userWallet = userWallet, coinStatus = coinStatus),
|
||||
)
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWallet.walletId, coinStatus).getOrNull()
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = coinStatus,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMultiCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
|
|
@ -649,21 +658,30 @@ internal class WalletViewModel @Inject constructor(
|
|||
)
|
||||
|
||||
val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
|
||||
when (cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
uiState = stateFactory.getStateWithClosedBottomSheet()
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWallet.walletId, cryptoCurrencyStatus).getOrNull()
|
||||
when (cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
uiState = stateFactory.getStateWithClosedBottomSheet()
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
is CryptoCurrency.Token -> sendToken(userWallet, cryptoCurrencyStatus, maybeFeeCurrencyStatus)
|
||||
}
|
||||
is CryptoCurrency.Token -> sendToken(userWallet, cryptoCurrencyStatus)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendToken(userWallet: UserWallet, cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
private fun sendToken(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
getNetworkCoinStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
|
|
@ -681,6 +699,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
tokenCurrency = requireNotNull(cryptoCurrencyStatus.currency as? CryptoCurrency.Token),
|
||||
tokenFiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
coinFiatRate = coinStatus.value.fiatRate,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.IsCryptoCurrencyCoinCouldHideUseCase
|
||||
import com.tangem.domain.tokens.RemoveCurrencyUseCase
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -76,6 +73,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
|
||||
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getExploreUrlUseCase: GetExploreUrlUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
|
|
@ -91,22 +89,39 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId))
|
||||
|
||||
when (val currency = cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> sendCoin(cryptoCurrencyStatus, userWallet)
|
||||
is CryptoCurrency.Token -> sendToken(currency, cryptoCurrencyStatus.value, userWallet)
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWallet.walletId, cryptoCurrencyStatus).getOrNull()
|
||||
when (val currency = cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> sendCoin(cryptoCurrencyStatus, userWallet, maybeFeeCurrencyStatus)
|
||||
is CryptoCurrency.Token -> sendToken(
|
||||
cryptoCurrency = currency,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus.value,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
userWallet = userWallet,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendCoin(cryptoCurrencyStatus: CryptoCurrencyStatus, userWallet: UserWallet) {
|
||||
private fun sendCoin(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
userWallet: UserWallet,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(userWallet = userWallet, coinStatus = cryptoCurrencyStatus),
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendToken(
|
||||
cryptoCurrency: CryptoCurrency.Token,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus.Status,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
userWallet: UserWallet,
|
||||
) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
|
|
@ -125,6 +140,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
tokenCurrency = cryptoCurrency,
|
||||
tokenFiatRate = cryptoCurrencyStatus.fiatRate,
|
||||
coinFiatRate = coinStatus.value.fiatRate,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ spr-client = "3.6.2"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "develop-453"
|
||||
tangemBlockchainSdk = "develop-458"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-318"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue