Updated on 2026-08-14

This commit is contained in:
Tangem 2026-01-26 13:50:00 +03:00
parent b08892762c
commit f095f3a508
24 changed files with 184 additions and 26 deletions

View file

@ -100,24 +100,28 @@ sealed class AnalyticsParam {
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
override val feeToken: String,
) : TxSentFrom("Send"), TxData
data class Swap(
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
override val feeToken: String,
) : TxSentFrom("Swap"), TxData
data class Staking(
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
override val feeToken: String,
) : TxSentFrom("Staking"), TxData
data class Approve(
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
override val feeToken: String,
val permissionType: String,
) : TxSentFrom("Approve"), TxData
@ -125,6 +129,7 @@ sealed class AnalyticsParam {
override val blockchain: String,
override val token: String,
override val feeType: FeeType?,
override val feeToken: String,
) : TxSentFrom("WalletConnect"), TxData
data object Sell : TxSentFrom("Sell")
@ -133,24 +138,28 @@ sealed class AnalyticsParam {
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
override val feeToken: String,
) : TxSentFrom("NFT"), TxData
data class SendWithSwap(
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
override val feeToken: String,
) : TxSentFrom("Send&Swap"), TxData
data class Earning(
override val blockchain: String,
override val token: String,
override val feeType: FeeType,
override val feeToken: String,
) : TxSentFrom("Earning"), TxData
}
sealed interface TxData {
val blockchain: String
val token: String
val feeToken: String
val feeType: FeeType?
}
@ -274,5 +283,6 @@ sealed class AnalyticsParam {
const val ENS = "ENS"
const val ENS_ADDRESS = "ENS Address"
const val ACCOUNT_DERIVATION_FROM = "Account Derivation (from)"
const val FEE_TOKEN = "Fee Token"
}
}

View file

@ -87,6 +87,7 @@ sealed class Basic(
sentFrom.feeType?.value?.let {
this[AnalyticsParam.Key.FEE_TYPE] = it
}
this[AnalyticsParam.Key.FEE_TOKEN] = sentFrom.feeToken
}
if (sentFrom is AnalyticsParam.TxSentFrom.Approve) {
this[AnalyticsParam.Key.PERMISSION_TYPE] = sentFrom.permissionType

View file

@ -90,6 +90,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
blockchain = network.name,
token = network.currencySymbol,
feeType = null,
feeToken = network.currencySymbol,
),
memoType = MemoType.Null,
),

View file

@ -52,6 +52,30 @@ sealed class CommonSendAnalyticEvents(
),
)
data class FeeSummaryScreenOpened(
val categoryName: String,
val source: CommonSendSource,
) : CommonSendAnalyticEvents(
category = categoryName,
event = "Fee Summary Screen Opened",
params = mapOf(
SOURCE to source.analyticsName,
),
)
data class FeeTokenScreenOpened(
val categoryName: String,
val source: CommonSendSource,
val availableTokens: String,
) : CommonSendAnalyticEvents(
category = categoryName,
event = "Fee Token Screen Opened",
params = mapOf(
SOURCE to source.analyticsName,
"Available Fee" to availableTokens,
),
)
@Suppress("NullableToStringCall")
/** Confirmation screen opened */
data class ConfirmationScreenOpened(
@ -175,6 +199,7 @@ sealed class CommonSendAnalyticEvents(
companion object {
const val SEND_CATEGORY = "Token / Send"
const val SWAP_CATEGORY = "Swap"
const val NFT_SEND_CATEGORY = "NFT"
}

View file

@ -2,6 +2,7 @@ package com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TOKEN
import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE
import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.CommonSendSource
@ -19,12 +20,14 @@ sealed class CommonSendFeeAnalyticEvents(
override val categoryName: String,
val feeType: AnalyticsParam.FeeType,
val source: CommonSendSource,
val feeToken: String,
) : CommonSendFeeAnalyticEvents(
category = categoryName,
event = "Fee Selected",
params = mapOf(
FEE_TYPE to feeType.value,
SOURCE to source.analyticsName,
FEE_TOKEN to feeToken,
),
)

View file

@ -88,18 +88,27 @@ internal class DefaultFeeSelectorComponent @AssistedInject constructor(
)
return when (config) {
FeeSelectorRoute.NetworkFee -> feeExtendedSelectorComponentFactory.create(
appComponentContext = appComponentContext,
params = componentParams,
)
FeeSelectorRoute.ChooseSpeed -> feeSpeedSelectorComponentFactory.create(
appComponentContext = appComponentContext,
params = componentParams,
)
FeeSelectorRoute.ChooseToken -> feeTokenSelectorComponentFactory.create(
appComponentContext = appComponentContext,
params = componentParams,
)
FeeSelectorRoute.NetworkFee -> {
model.onFeeScreenOpened()
feeExtendedSelectorComponentFactory.create(
appComponentContext = appComponentContext,
params = componentParams,
)
}
FeeSelectorRoute.ChooseSpeed -> {
model.onChooseSpeedScreenOpened()
feeSpeedSelectorComponentFactory.create(
appComponentContext = appComponentContext,
params = componentParams,
)
}
FeeSelectorRoute.ChooseToken -> {
model.onChooseTokenScreenOpened()
feeTokenSelectorComponentFactory.create(
appComponentContext = appComponentContext,
params = componentParams,
)
}
}
}

View file

@ -58,12 +58,6 @@ internal class FeeSelectorBlockModel @Inject constructor(
}
fun showFeeSelector() {
analyticsEventHandler.send(
CommonSendAnalyticEvents.FeeScreenOpened(
categoryName = params.analyticsCategoryName,
source = params.analyticsSendSource,
),
)
analyticsEventHandler.send(
CommonSendAnalyticEvents.ScreenReopened(
categoryName = params.analyticsCategoryName,

View file

@ -153,13 +153,15 @@ internal class FeeSelectorLogic @AssistedInject constructor(
categoryName = params.analyticsCategoryName,
feeType = feeSelectorUM.toAnalyticType(),
source = params.analyticsSendSource,
feeToken = feeSelectorUM.feeExtraInfo.feeCryptoCurrencyStatus.currency.symbol,
),
)
val isCustomFeeEdited = feeSelectorUM.selectedFeeItem.fee.amount.value != feeSelectorUM.fees.normal.amount.value
if (feeSelectorUM.selectedFeeItem is FeeItem.Custom && isCustomFeeEdited) {
analyticsEventHandler.send(GasPriceInserter(categoryName = params.analyticsCategoryName))
}
if (feeSelectorUM.feeNonce is FeeNonce.Nonce) {
val nonceField = feeSelectorUM.feeNonce as? FeeNonce.Nonce
if (nonceField != null && nonceField.nonce != null) {
analyticsEventHandler.send(
NonceInserted(
categoryName = params.analyticsCategoryName,
@ -268,12 +270,11 @@ internal class FeeSelectorLogic @AssistedInject constructor(
private suspend fun getSelectedTokenStatus(tokenId: CryptoCurrency.ID): Either<GetFeeError, CryptoCurrencyStatus> =
either {
val feeTokenId = tokenId
if (params.feeCryptoCurrencyStatus.currency.id != feeTokenId) {
if (params.feeCryptoCurrencyStatus.currency.id != tokenId) {
getSingleCryptoCurrencyStatusUseCase
.invokeMultiWalletSync(
userWalletId = params.userWalletId,
cryptoCurrencyId = feeTokenId,
cryptoCurrencyId = tokenId,
).getOrElse {
raise(GetFeeError.DataError(IllegalStateException("No token found for id: $tokenId")))
}

View file

@ -3,11 +3,13 @@ package com.tangem.features.send.v2.feeselector.model
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.utils.stack
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.params.FeeSelectorParams
@ -23,6 +25,7 @@ internal class FeeSelectorModel @Inject constructor(
paramsContainer: ParamsContainer,
feeSelectorLogicFactory: FeeSelectorLogic.Factory,
override val dispatchers: CoroutineDispatcherProvider,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model(), FeeSelectorIntents {
private val params = paramsContainer.require<FeeSelectorParams.FeeSelectorDetailsParams>()
@ -78,6 +81,42 @@ internal class FeeSelectorModel @Inject constructor(
}
}
fun onFeeScreenOpened() {
analyticsEventHandler.send(
CommonSendAnalyticEvents.FeeSummaryScreenOpened(
categoryName = params.analyticsCategoryName,
source = params.analyticsSendSource,
),
)
}
fun onChooseSpeedScreenOpened() {
analyticsEventHandler.send(
CommonSendAnalyticEvents.FeeScreenOpened(
categoryName = params.analyticsCategoryName,
source = params.analyticsSendSource,
),
)
}
fun onChooseTokenScreenOpened() {
analyticsEventHandler.send(
CommonSendAnalyticEvents.FeeTokenScreenOpened(
categoryName = params.analyticsCategoryName,
source = params.analyticsSendSource,
availableTokens = getAvailableTokensString(),
),
)
}
private fun getAvailableTokensString(): String {
return (uiState.value as? FeeSelectorUM.Content)
?.feeExtraInfo
?.availableFeeCurrencies
?.joinToString(", ") { it.currency.symbol }
.orEmpty()
}
private fun contentState(): FeeSelectorUM.Content {
val currentState = uiState.value
require(currentState is FeeSelectorUM.Content) { "Current state must be FeeSelectorUM.Content" }

View file

@ -65,7 +65,7 @@ internal fun FeeSelectorModalBottomSheet(
.fillMaxWidth()
.padding(horizontal = 16.dp),
enabled = state.isPrimaryButtonEnabled,
text = stringResourceSafe(R.string.common_confirm),
text = stringResourceSafe(R.string.common_apply),
onClick = feeSelectorIntents::onDoneClick,
)
}

View file

@ -27,6 +27,7 @@ internal sealed class SendAnalyticEvents(
val blockchain: String,
val isNonceNotEmpty: Boolean,
private val ensStatus: AnalyticsParam.EmptyFull,
private val feeToken: String,
val derivationIndex: Int?,
) : SendAnalyticEvents(
event = "Transaction Sent Screen Opened",

View file

@ -18,10 +18,16 @@ internal class SendAnalyticHelper @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
) {
fun sendSuccessAnalytics(cryptoCurrency: CryptoCurrency, sendUM: SendUM, account: Account.CryptoPortfolio?) {
fun sendSuccessAnalytics(
cryptoCurrency: CryptoCurrency,
sendUM: SendUM,
feeToken: CryptoCurrency,
account: Account.CryptoPortfolio?,
) {
val destinationUM = sendUM.destinationUM as? DestinationUM.Content
val feeSelectorUM = sendUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
val feeType = feeSelectorUM.toAnalyticType()
val feeTokenSymbol = feeToken.symbol
val isNotMainAccount = account != null && !account.isMainAccount
val derivationIndex = if (isNotMainAccount) account.derivationIndex.value else null
analyticsEventHandler.send(
@ -32,6 +38,7 @@ internal class SendAnalyticHelper @Inject constructor(
isNonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
ensStatus = getEnsStatus(sendUM),
derivationIndex = derivationIndex,
feeToken = feeTokenSymbol,
),
)
analyticsEventHandler.send(
@ -40,6 +47,7 @@ internal class SendAnalyticHelper @Inject constructor(
blockchain = cryptoCurrency.network.name,
token = cryptoCurrency.symbol,
feeType = feeType,
feeToken = feeTokenSymbol,
),
memoType = getSendTransactionMemoType(destinationUM?.memoTextField),
),

View file

@ -390,6 +390,7 @@ internal class SendConfirmModel @Inject constructor(
private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
val feeToken = getCurrencyStatusForFeePayment().currency
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
@ -433,6 +434,7 @@ internal class SendConfirmModel @Inject constructor(
cryptoCurrency = cryptoCurrency,
sendUM = uiState.value,
account = params.accountFlow.value,
feeToken = feeToken,
)
params.callback.onResult(uiState.value)
params.onSendTransaction()

View file

@ -5,6 +5,7 @@ import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.left
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.navigationButtons.NavigationUM
@ -28,6 +29,7 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
@ -72,6 +74,7 @@ import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import kotlin.properties.Delegates
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
internal interface SendComponentCallback :
SendAmountComponent.ModelCallback,
@ -452,6 +455,17 @@ internal class SendModel @Inject constructor(
}
}
fun getSelectedFeeToken(): CryptoCurrency {
val feeUMV2 = uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
return if (isFeeInTokenCurrency) {
feeUMV2.feeExtraInfo.feeCryptoCurrencyStatus.currency
} else {
feeCryptoCurrencyStatusFlow.value.currency
}
}
private suspend fun getFeeCurrencyStatus(
cryptoCurrencyStatus: CryptoCurrencyStatus,
isMultiCurrency: Boolean,

View file

@ -36,6 +36,7 @@ internal class NFTSendAnalyticHelper @Inject constructor(
blockchain = cryptoCurrency.network.name,
token = NFT_SEND_CATEGORY, // should send "NFT" in token param
feeType = feeType,
feeToken = cryptoCurrency.symbol,
),
memoType = getSendTransactionMemoType(destinationUM?.memoTextField),
),

View file

@ -72,6 +72,7 @@ internal class StakingAnalyticSender(
token = tokenCryptoCurrency.symbol,
feeType = AnalyticsParam.FeeType.Normal,
permissionType = ApproveType.LIMITED.name,
feeToken = tokenCryptoCurrency.symbol,
),
memoType = Basic.TransactionSent.MemoType.Null,
),
@ -88,6 +89,7 @@ internal class StakingAnalyticSender(
blockchain = cryptoCurrencyStatus.currency.network.name,
token = value.cryptoCurrencySymbol,
feeType = AnalyticsParam.FeeType.Normal,
feeToken = cryptoCurrencyStatus.currency.symbol,
),
memoType = Basic.TransactionSent.MemoType.Null,
),

View file

@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.sendviaswap.confirm.model
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.left
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
@ -457,12 +458,24 @@ internal class SendWithSwapConfirmModel @Inject constructor(
blockchain = fromCurrency.network.name,
token = fromCurrency.symbol,
feeType = feeType,
feeToken = getSelectedFeeToken().symbol,
),
memoType = Basic.TransactionSent.MemoType.Null,
),
)
}
private fun getSelectedFeeToken(): CryptoCurrency {
val feeUMV2 = uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
return if (isFeeInTokenCurrency) {
feeUMV2.feeExtraInfo.feeCryptoCurrencyStatus.currency
} else {
primaryCurrencyStatus.currency
}
}
private fun configConfirmNavigation() {
combine(
flow = uiState,

View file

@ -2,6 +2,7 @@ package com.tangem.features.swap.v2.impl.sendviaswap.model
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.common.ui.navigationButtons.NavigationUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -43,6 +44,7 @@ import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import kotlin.properties.Delegates
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
@Suppress("LongParameterList")
@ModelScoped
@ -160,6 +162,17 @@ internal class SendWithSwapModel @Inject constructor(
}
}
fun getSelectedFeeToken(): CryptoCurrency {
val feeUMV2 = uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
return if (isFeeInTokenCurrency) {
feeUMV2.feeExtraInfo.feeCryptoCurrencyStatus.currency
} else {
primaryFeePaidCurrencyStatusFlow.value.currency
}
}
private fun initUserWallet() {
getUserWalletUseCase(params.userWalletId).fold(
ifRight = { wallet ->

View file

@ -88,7 +88,7 @@ internal class DefaultSwapComponent @AssistedInject constructor(
sendingCryptoCurrencyStatus = config.sendingCurrencyStatus,
feeCryptoCurrencyStatus = config.feeCurrencyStatus,
analyticsParams = SwapFeeSelectorBlockComponent.AnalyticsParams(
analyticsCategoryName = CommonSendAnalyticEvents.SEND_CATEGORY,
analyticsCategoryName = CommonSendAnalyticEvents.SWAP_CATEGORY,
analyticsSendSource = CommonSendAnalyticEvents.CommonSendSource.Swap,
),
),

View file

@ -4,6 +4,7 @@ import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_MESSAGE
import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TOKEN
import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER
import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_TOKEN
import com.tangem.core.analytics.models.AnalyticsParam.Key.SEND_TOKEN
@ -84,6 +85,7 @@ sealed class SwapEvents(
val receiveBlockchain: String,
val sendToken: String,
val receiveToken: String,
val feeToken: String,
val fromDerivationIndex: Int?,
val toDerivationIndex: Int?,
) : SwapEvents(
@ -96,6 +98,7 @@ sealed class SwapEvents(
"Send Blockchain" to sendBlockchain,
"Receive Blockchain" to receiveBlockchain,
"Account Derivation From or To (optional)" to "$fromDerivationIndex, $toDerivationIndex",
FEE_TOKEN to feeToken,
),
), AppsFlyerIncludedEvent

View file

@ -114,7 +114,6 @@ import java.text.DecimalFormat
import java.text.NumberFormat
import java.util.Locale
import javax.inject.Inject
import kotlin.collections.filter
typealias SuccessLoadedSwapData = Map<SwapProvider, SwapState.QuotesLoadedState>
@ -1098,6 +1097,7 @@ internal class SwapModel @Inject constructor(
receiveBlockchain = toCurrency.network.name,
sendToken = fromCurrency.symbol,
receiveToken = toCurrency.symbol,
feeToken = getFeeToken().symbol,
fromDerivationIndex = fromDerivationIndex,
toDerivationIndex = toDerivationIndex,
),
@ -1713,6 +1713,7 @@ internal class SwapModel @Inject constructor(
blockchain = fromToken.network.name,
token = fromToken.symbol,
feeType = AnalyticsParam.FeeType.fromString(feeType.getNameForAnalytics()),
feeToken = getFeeToken().symbol,
)
analyticsEventHandler.send(
Basic.TransactionSent(
@ -1722,12 +1723,26 @@ internal class SwapModel @Inject constructor(
)
}
private fun getFeeToken(): CryptoCurrency {
val fromToken = requireNotNull(dataState.fromCryptoCurrency) {
"fromCryptoCurrency should not be null"
}
return when (val fee = getSelectedFee()) {
is TxFee.FeeComponent -> fee.selectedToken?.currency ?: fromToken.currency
is TxFee.Legacy,
null,
-> fromToken.currency
}
}
private fun sendApproveSuccessEvent(fromToken: CryptoCurrency, feeType: FeeType, approveType: SwapApproveType) {
val feeToken = getFeeToken().symbol
val event = AnalyticsParam.TxSentFrom.Approve(
blockchain = fromToken.network.name,
token = fromToken.symbol,
feeType = AnalyticsParam.FeeType.fromString(feeType.getNameForAnalytics()),
permissionType = approveType.getNameForAnalytics(),
feeToken = feeToken,
)
analyticsEventHandler.send(
Basic.TransactionSent(

View file

@ -162,6 +162,7 @@ internal class YieldSupplyApproveModel @Inject constructor(
blockchain = cryptoCurrency.network.name,
token = cryptoCurrency.symbol,
feeType = AnalyticsParam.FeeType.Normal,
feeToken = feeCryptoCurrencyStatus.currency.symbol,
)
analyticsEventHandler.send(
Basic.TransactionSent(

View file

@ -266,6 +266,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
blockchain = cryptoCurrency.network.name,
token = cryptoCurrency.symbol,
feeType = AnalyticsParam.FeeType.Normal,
feeToken = feeCryptoCurrencyStatusFlow.value.currency.symbol,
)
analytics.send(
YieldSupplyAnalytics.FundsEarned(

View file

@ -192,6 +192,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
blockchain = cryptoCurrency.network.name,
token = cryptoCurrency.symbol,
feeType = AnalyticsParam.FeeType.Normal,
feeToken = feeCryptoCurrencyStatus.currency.symbol,
)
analytics.send(
Basic.TransactionSent(