Updated on 2026-08-14
This commit is contained in:
parent
b08892762c
commit
f095f3a508
24 changed files with 184 additions and 26 deletions
|
|
@ -100,24 +100,28 @@ sealed class AnalyticsParam {
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType,
|
override val feeType: FeeType,
|
||||||
|
override val feeToken: String,
|
||||||
) : TxSentFrom("Send"), TxData
|
) : TxSentFrom("Send"), TxData
|
||||||
|
|
||||||
data class Swap(
|
data class Swap(
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType,
|
override val feeType: FeeType,
|
||||||
|
override val feeToken: String,
|
||||||
) : TxSentFrom("Swap"), TxData
|
) : TxSentFrom("Swap"), TxData
|
||||||
|
|
||||||
data class Staking(
|
data class Staking(
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType,
|
override val feeType: FeeType,
|
||||||
|
override val feeToken: String,
|
||||||
) : TxSentFrom("Staking"), TxData
|
) : TxSentFrom("Staking"), TxData
|
||||||
|
|
||||||
data class Approve(
|
data class Approve(
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType,
|
override val feeType: FeeType,
|
||||||
|
override val feeToken: String,
|
||||||
val permissionType: String,
|
val permissionType: String,
|
||||||
) : TxSentFrom("Approve"), TxData
|
) : TxSentFrom("Approve"), TxData
|
||||||
|
|
||||||
|
|
@ -125,6 +129,7 @@ sealed class AnalyticsParam {
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType?,
|
override val feeType: FeeType?,
|
||||||
|
override val feeToken: String,
|
||||||
) : TxSentFrom("WalletConnect"), TxData
|
) : TxSentFrom("WalletConnect"), TxData
|
||||||
|
|
||||||
data object Sell : TxSentFrom("Sell")
|
data object Sell : TxSentFrom("Sell")
|
||||||
|
|
@ -133,24 +138,28 @@ sealed class AnalyticsParam {
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType,
|
override val feeType: FeeType,
|
||||||
|
override val feeToken: String,
|
||||||
) : TxSentFrom("NFT"), TxData
|
) : TxSentFrom("NFT"), TxData
|
||||||
|
|
||||||
data class SendWithSwap(
|
data class SendWithSwap(
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType,
|
override val feeType: FeeType,
|
||||||
|
override val feeToken: String,
|
||||||
) : TxSentFrom("Send&Swap"), TxData
|
) : TxSentFrom("Send&Swap"), TxData
|
||||||
|
|
||||||
data class Earning(
|
data class Earning(
|
||||||
override val blockchain: String,
|
override val blockchain: String,
|
||||||
override val token: String,
|
override val token: String,
|
||||||
override val feeType: FeeType,
|
override val feeType: FeeType,
|
||||||
|
override val feeToken: String,
|
||||||
) : TxSentFrom("Earning"), TxData
|
) : TxSentFrom("Earning"), TxData
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface TxData {
|
sealed interface TxData {
|
||||||
val blockchain: String
|
val blockchain: String
|
||||||
val token: String
|
val token: String
|
||||||
|
val feeToken: String
|
||||||
val feeType: FeeType?
|
val feeType: FeeType?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,5 +283,6 @@ sealed class AnalyticsParam {
|
||||||
const val ENS = "ENS"
|
const val ENS = "ENS"
|
||||||
const val ENS_ADDRESS = "ENS Address"
|
const val ENS_ADDRESS = "ENS Address"
|
||||||
const val ACCOUNT_DERIVATION_FROM = "Account Derivation (from)"
|
const val ACCOUNT_DERIVATION_FROM = "Account Derivation (from)"
|
||||||
|
const val FEE_TOKEN = "Fee Token"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,6 +87,7 @@ sealed class Basic(
|
||||||
sentFrom.feeType?.value?.let {
|
sentFrom.feeType?.value?.let {
|
||||||
this[AnalyticsParam.Key.FEE_TYPE] = it
|
this[AnalyticsParam.Key.FEE_TYPE] = it
|
||||||
}
|
}
|
||||||
|
this[AnalyticsParam.Key.FEE_TOKEN] = sentFrom.feeToken
|
||||||
}
|
}
|
||||||
if (sentFrom is AnalyticsParam.TxSentFrom.Approve) {
|
if (sentFrom is AnalyticsParam.TxSentFrom.Approve) {
|
||||||
this[AnalyticsParam.Key.PERMISSION_TYPE] = sentFrom.permissionType
|
this[AnalyticsParam.Key.PERMISSION_TYPE] = sentFrom.permissionType
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
||||||
blockchain = network.name,
|
blockchain = network.name,
|
||||||
token = network.currencySymbol,
|
token = network.currencySymbol,
|
||||||
feeType = null,
|
feeType = null,
|
||||||
|
feeToken = network.currencySymbol,
|
||||||
),
|
),
|
||||||
memoType = MemoType.Null,
|
memoType = MemoType.Null,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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")
|
@Suppress("NullableToStringCall")
|
||||||
/** Confirmation screen opened */
|
/** Confirmation screen opened */
|
||||||
data class ConfirmationScreenOpened(
|
data class ConfirmationScreenOpened(
|
||||||
|
|
@ -175,6 +199,7 @@ sealed class CommonSendAnalyticEvents(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SEND_CATEGORY = "Token / Send"
|
const val SEND_CATEGORY = "Token / Send"
|
||||||
|
const val SWAP_CATEGORY = "Swap"
|
||||||
const val NFT_SEND_CATEGORY = "NFT"
|
const val NFT_SEND_CATEGORY = "NFT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.AnalyticsEvent
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam
|
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.FEE_TYPE
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE
|
import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE
|
||||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.CommonSendSource
|
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.CommonSendSource
|
||||||
|
|
@ -19,12 +20,14 @@ sealed class CommonSendFeeAnalyticEvents(
|
||||||
override val categoryName: String,
|
override val categoryName: String,
|
||||||
val feeType: AnalyticsParam.FeeType,
|
val feeType: AnalyticsParam.FeeType,
|
||||||
val source: CommonSendSource,
|
val source: CommonSendSource,
|
||||||
|
val feeToken: String,
|
||||||
) : CommonSendFeeAnalyticEvents(
|
) : CommonSendFeeAnalyticEvents(
|
||||||
category = categoryName,
|
category = categoryName,
|
||||||
event = "Fee Selected",
|
event = "Fee Selected",
|
||||||
params = mapOf(
|
params = mapOf(
|
||||||
FEE_TYPE to feeType.value,
|
FEE_TYPE to feeType.value,
|
||||||
SOURCE to source.analyticsName,
|
SOURCE to source.analyticsName,
|
||||||
|
FEE_TOKEN to feeToken,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,18 +88,27 @@ internal class DefaultFeeSelectorComponent @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
return when (config) {
|
return when (config) {
|
||||||
FeeSelectorRoute.NetworkFee -> feeExtendedSelectorComponentFactory.create(
|
FeeSelectorRoute.NetworkFee -> {
|
||||||
appComponentContext = appComponentContext,
|
model.onFeeScreenOpened()
|
||||||
params = componentParams,
|
feeExtendedSelectorComponentFactory.create(
|
||||||
)
|
appComponentContext = appComponentContext,
|
||||||
FeeSelectorRoute.ChooseSpeed -> feeSpeedSelectorComponentFactory.create(
|
params = componentParams,
|
||||||
appComponentContext = appComponentContext,
|
)
|
||||||
params = componentParams,
|
}
|
||||||
)
|
FeeSelectorRoute.ChooseSpeed -> {
|
||||||
FeeSelectorRoute.ChooseToken -> feeTokenSelectorComponentFactory.create(
|
model.onChooseSpeedScreenOpened()
|
||||||
appComponentContext = appComponentContext,
|
feeSpeedSelectorComponentFactory.create(
|
||||||
params = componentParams,
|
appComponentContext = appComponentContext,
|
||||||
)
|
params = componentParams,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
FeeSelectorRoute.ChooseToken -> {
|
||||||
|
model.onChooseTokenScreenOpened()
|
||||||
|
feeTokenSelectorComponentFactory.create(
|
||||||
|
appComponentContext = appComponentContext,
|
||||||
|
params = componentParams,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,12 +58,6 @@ internal class FeeSelectorBlockModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showFeeSelector() {
|
fun showFeeSelector() {
|
||||||
analyticsEventHandler.send(
|
|
||||||
CommonSendAnalyticEvents.FeeScreenOpened(
|
|
||||||
categoryName = params.analyticsCategoryName,
|
|
||||||
source = params.analyticsSendSource,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
CommonSendAnalyticEvents.ScreenReopened(
|
CommonSendAnalyticEvents.ScreenReopened(
|
||||||
categoryName = params.analyticsCategoryName,
|
categoryName = params.analyticsCategoryName,
|
||||||
|
|
|
||||||
|
|
@ -153,13 +153,15 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
||||||
categoryName = params.analyticsCategoryName,
|
categoryName = params.analyticsCategoryName,
|
||||||
feeType = feeSelectorUM.toAnalyticType(),
|
feeType = feeSelectorUM.toAnalyticType(),
|
||||||
source = params.analyticsSendSource,
|
source = params.analyticsSendSource,
|
||||||
|
feeToken = feeSelectorUM.feeExtraInfo.feeCryptoCurrencyStatus.currency.symbol,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val isCustomFeeEdited = feeSelectorUM.selectedFeeItem.fee.amount.value != feeSelectorUM.fees.normal.amount.value
|
val isCustomFeeEdited = feeSelectorUM.selectedFeeItem.fee.amount.value != feeSelectorUM.fees.normal.amount.value
|
||||||
if (feeSelectorUM.selectedFeeItem is FeeItem.Custom && isCustomFeeEdited) {
|
if (feeSelectorUM.selectedFeeItem is FeeItem.Custom && isCustomFeeEdited) {
|
||||||
analyticsEventHandler.send(GasPriceInserter(categoryName = params.analyticsCategoryName))
|
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(
|
analyticsEventHandler.send(
|
||||||
NonceInserted(
|
NonceInserted(
|
||||||
categoryName = params.analyticsCategoryName,
|
categoryName = params.analyticsCategoryName,
|
||||||
|
|
@ -268,12 +270,11 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
||||||
|
|
||||||
private suspend fun getSelectedTokenStatus(tokenId: CryptoCurrency.ID): Either<GetFeeError, CryptoCurrencyStatus> =
|
private suspend fun getSelectedTokenStatus(tokenId: CryptoCurrency.ID): Either<GetFeeError, CryptoCurrencyStatus> =
|
||||||
either {
|
either {
|
||||||
val feeTokenId = tokenId
|
if (params.feeCryptoCurrencyStatus.currency.id != tokenId) {
|
||||||
if (params.feeCryptoCurrencyStatus.currency.id != feeTokenId) {
|
|
||||||
getSingleCryptoCurrencyStatusUseCase
|
getSingleCryptoCurrencyStatusUseCase
|
||||||
.invokeMultiWalletSync(
|
.invokeMultiWalletSync(
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
cryptoCurrencyId = feeTokenId,
|
cryptoCurrencyId = tokenId,
|
||||||
).getOrElse {
|
).getOrElse {
|
||||||
raise(GetFeeError.DataError(IllegalStateException("No token found for id: $tokenId")))
|
raise(GetFeeError.DataError(IllegalStateException("No token found for id: $tokenId")))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@ package com.tangem.features.send.v2.feeselector.model
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||||
import com.arkivanov.decompose.router.stack.pop
|
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.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.decompose.utils.stack
|
import com.tangem.core.decompose.utils.stack
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
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.FeeItem
|
||||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||||
import com.tangem.features.send.v2.api.params.FeeSelectorParams
|
import com.tangem.features.send.v2.api.params.FeeSelectorParams
|
||||||
|
|
@ -23,6 +25,7 @@ internal class FeeSelectorModel @Inject constructor(
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
feeSelectorLogicFactory: FeeSelectorLogic.Factory,
|
feeSelectorLogicFactory: FeeSelectorLogic.Factory,
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
) : Model(), FeeSelectorIntents {
|
) : Model(), FeeSelectorIntents {
|
||||||
|
|
||||||
private val params = paramsContainer.require<FeeSelectorParams.FeeSelectorDetailsParams>()
|
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 {
|
private fun contentState(): FeeSelectorUM.Content {
|
||||||
val currentState = uiState.value
|
val currentState = uiState.value
|
||||||
require(currentState is FeeSelectorUM.Content) { "Current state must be FeeSelectorUM.Content" }
|
require(currentState is FeeSelectorUM.Content) { "Current state must be FeeSelectorUM.Content" }
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ internal fun FeeSelectorModalBottomSheet(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
enabled = state.isPrimaryButtonEnabled,
|
enabled = state.isPrimaryButtonEnabled,
|
||||||
text = stringResourceSafe(R.string.common_confirm),
|
text = stringResourceSafe(R.string.common_apply),
|
||||||
onClick = feeSelectorIntents::onDoneClick,
|
onClick = feeSelectorIntents::onDoneClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ internal sealed class SendAnalyticEvents(
|
||||||
val blockchain: String,
|
val blockchain: String,
|
||||||
val isNonceNotEmpty: Boolean,
|
val isNonceNotEmpty: Boolean,
|
||||||
private val ensStatus: AnalyticsParam.EmptyFull,
|
private val ensStatus: AnalyticsParam.EmptyFull,
|
||||||
|
private val feeToken: String,
|
||||||
val derivationIndex: Int?,
|
val derivationIndex: Int?,
|
||||||
) : SendAnalyticEvents(
|
) : SendAnalyticEvents(
|
||||||
event = "Transaction Sent Screen Opened",
|
event = "Transaction Sent Screen Opened",
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,16 @@ internal class SendAnalyticHelper @Inject constructor(
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
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 destinationUM = sendUM.destinationUM as? DestinationUM.Content
|
||||||
val feeSelectorUM = sendUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
val feeSelectorUM = sendUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||||
val feeType = feeSelectorUM.toAnalyticType()
|
val feeType = feeSelectorUM.toAnalyticType()
|
||||||
|
val feeTokenSymbol = feeToken.symbol
|
||||||
val isNotMainAccount = account != null && !account.isMainAccount
|
val isNotMainAccount = account != null && !account.isMainAccount
|
||||||
val derivationIndex = if (isNotMainAccount) account.derivationIndex.value else null
|
val derivationIndex = if (isNotMainAccount) account.derivationIndex.value else null
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
|
|
@ -32,6 +38,7 @@ internal class SendAnalyticHelper @Inject constructor(
|
||||||
isNonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
isNonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
||||||
ensStatus = getEnsStatus(sendUM),
|
ensStatus = getEnsStatus(sendUM),
|
||||||
derivationIndex = derivationIndex,
|
derivationIndex = derivationIndex,
|
||||||
|
feeToken = feeTokenSymbol,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
|
|
@ -40,6 +47,7 @@ internal class SendAnalyticHelper @Inject constructor(
|
||||||
blockchain = cryptoCurrency.network.name,
|
blockchain = cryptoCurrency.network.name,
|
||||||
token = cryptoCurrency.symbol,
|
token = cryptoCurrency.symbol,
|
||||||
feeType = feeType,
|
feeType = feeType,
|
||||||
|
feeToken = feeTokenSymbol,
|
||||||
),
|
),
|
||||||
memoType = getSendTransactionMemoType(destinationUM?.memoTextField),
|
memoType = getSendTransactionMemoType(destinationUM?.memoTextField),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -390,6 +390,7 @@ internal class SendConfirmModel @Inject constructor(
|
||||||
|
|
||||||
private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
|
private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
|
||||||
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
|
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
|
||||||
|
val feeToken = getCurrencyStatusForFeePayment().currency
|
||||||
|
|
||||||
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
|
val isFeeInTokenCurrency = feeExtended?.transactionFee?.normal is Fee.Ethereum.TokenCurrency
|
||||||
|
|
||||||
|
|
@ -433,6 +434,7 @@ internal class SendConfirmModel @Inject constructor(
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
sendUM = uiState.value,
|
sendUM = uiState.value,
|
||||||
account = params.accountFlow.value,
|
account = params.accountFlow.value,
|
||||||
|
feeToken = feeToken,
|
||||||
)
|
)
|
||||||
params.callback.onResult(uiState.value)
|
params.callback.onResult(uiState.value)
|
||||||
params.onSendTransaction()
|
params.onSendTransaction()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import arrow.core.Either
|
||||||
import arrow.core.getOrElse
|
import arrow.core.getOrElse
|
||||||
import arrow.core.left
|
import arrow.core.left
|
||||||
import com.tangem.blockchain.common.TransactionData
|
import com.tangem.blockchain.common.TransactionData
|
||||||
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||||
import com.tangem.common.ui.navigationButtons.NavigationUM
|
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.BlockchainErrorInfo
|
||||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
import com.tangem.domain.models.account.Account
|
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.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
|
|
@ -72,6 +74,7 @@ import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
|
||||||
|
|
||||||
internal interface SendComponentCallback :
|
internal interface SendComponentCallback :
|
||||||
SendAmountComponent.ModelCallback,
|
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(
|
private suspend fun getFeeCurrencyStatus(
|
||||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||||
isMultiCurrency: Boolean,
|
isMultiCurrency: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ internal class NFTSendAnalyticHelper @Inject constructor(
|
||||||
blockchain = cryptoCurrency.network.name,
|
blockchain = cryptoCurrency.network.name,
|
||||||
token = NFT_SEND_CATEGORY, // should send "NFT" in token param
|
token = NFT_SEND_CATEGORY, // should send "NFT" in token param
|
||||||
feeType = feeType,
|
feeType = feeType,
|
||||||
|
feeToken = cryptoCurrency.symbol,
|
||||||
),
|
),
|
||||||
memoType = getSendTransactionMemoType(destinationUM?.memoTextField),
|
memoType = getSendTransactionMemoType(destinationUM?.memoTextField),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ internal class StakingAnalyticSender(
|
||||||
token = tokenCryptoCurrency.symbol,
|
token = tokenCryptoCurrency.symbol,
|
||||||
feeType = AnalyticsParam.FeeType.Normal,
|
feeType = AnalyticsParam.FeeType.Normal,
|
||||||
permissionType = ApproveType.LIMITED.name,
|
permissionType = ApproveType.LIMITED.name,
|
||||||
|
feeToken = tokenCryptoCurrency.symbol,
|
||||||
),
|
),
|
||||||
memoType = Basic.TransactionSent.MemoType.Null,
|
memoType = Basic.TransactionSent.MemoType.Null,
|
||||||
),
|
),
|
||||||
|
|
@ -88,6 +89,7 @@ internal class StakingAnalyticSender(
|
||||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||||
token = value.cryptoCurrencySymbol,
|
token = value.cryptoCurrencySymbol,
|
||||||
feeType = AnalyticsParam.FeeType.Normal,
|
feeType = AnalyticsParam.FeeType.Normal,
|
||||||
|
feeToken = cryptoCurrencyStatus.currency.symbol,
|
||||||
),
|
),
|
||||||
memoType = Basic.TransactionSent.MemoType.Null,
|
memoType = Basic.TransactionSent.MemoType.Null,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.sendviaswap.confirm.model
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import arrow.core.getOrElse
|
import arrow.core.getOrElse
|
||||||
import arrow.core.left
|
import arrow.core.left
|
||||||
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||||
import com.tangem.common.routing.AppRouter
|
import com.tangem.common.routing.AppRouter
|
||||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||||
|
|
@ -457,12 +458,24 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
||||||
blockchain = fromCurrency.network.name,
|
blockchain = fromCurrency.network.name,
|
||||||
token = fromCurrency.symbol,
|
token = fromCurrency.symbol,
|
||||||
feeType = feeType,
|
feeType = feeType,
|
||||||
|
feeToken = getSelectedFeeToken().symbol,
|
||||||
),
|
),
|
||||||
memoType = Basic.TransactionSent.MemoType.Null,
|
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() {
|
private fun configConfirmNavigation() {
|
||||||
combine(
|
combine(
|
||||||
flow = uiState,
|
flow = uiState,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.swap.v2.impl.sendviaswap.model
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import arrow.core.getOrElse
|
import arrow.core.getOrElse
|
||||||
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.common.ui.navigationButtons.NavigationUM
|
import com.tangem.common.ui.navigationButtons.NavigationUM
|
||||||
import com.tangem.core.decompose.di.ModelScoped
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
|
|
@ -43,6 +44,7 @@ import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
@ModelScoped
|
@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() {
|
private fun initUserWallet() {
|
||||||
getUserWalletUseCase(params.userWalletId).fold(
|
getUserWalletUseCase(params.userWalletId).fold(
|
||||||
ifRight = { wallet ->
|
ifRight = { wallet ->
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
||||||
sendingCryptoCurrencyStatus = config.sendingCurrencyStatus,
|
sendingCryptoCurrencyStatus = config.sendingCurrencyStatus,
|
||||||
feeCryptoCurrencyStatus = config.feeCurrencyStatus,
|
feeCryptoCurrencyStatus = config.feeCurrencyStatus,
|
||||||
analyticsParams = SwapFeeSelectorBlockComponent.AnalyticsParams(
|
analyticsParams = SwapFeeSelectorBlockComponent.AnalyticsParams(
|
||||||
analyticsCategoryName = CommonSendAnalyticEvents.SEND_CATEGORY,
|
analyticsCategoryName = CommonSendAnalyticEvents.SWAP_CATEGORY,
|
||||||
analyticsSendSource = CommonSendAnalyticEvents.CommonSendSource.Swap,
|
analyticsSendSource = CommonSendAnalyticEvents.CommonSendSource.Swap,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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.AnalyticsEvent
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE
|
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.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.PROVIDER
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_TOKEN
|
import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_TOKEN
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.SEND_TOKEN
|
import com.tangem.core.analytics.models.AnalyticsParam.Key.SEND_TOKEN
|
||||||
|
|
@ -84,6 +85,7 @@ sealed class SwapEvents(
|
||||||
val receiveBlockchain: String,
|
val receiveBlockchain: String,
|
||||||
val sendToken: String,
|
val sendToken: String,
|
||||||
val receiveToken: String,
|
val receiveToken: String,
|
||||||
|
val feeToken: String,
|
||||||
val fromDerivationIndex: Int?,
|
val fromDerivationIndex: Int?,
|
||||||
val toDerivationIndex: Int?,
|
val toDerivationIndex: Int?,
|
||||||
) : SwapEvents(
|
) : SwapEvents(
|
||||||
|
|
@ -96,6 +98,7 @@ sealed class SwapEvents(
|
||||||
"Send Blockchain" to sendBlockchain,
|
"Send Blockchain" to sendBlockchain,
|
||||||
"Receive Blockchain" to receiveBlockchain,
|
"Receive Blockchain" to receiveBlockchain,
|
||||||
"Account Derivation From or To (optional)" to "$fromDerivationIndex, $toDerivationIndex",
|
"Account Derivation From or To (optional)" to "$fromDerivationIndex, $toDerivationIndex",
|
||||||
|
FEE_TOKEN to feeToken,
|
||||||
),
|
),
|
||||||
), AppsFlyerIncludedEvent
|
), AppsFlyerIncludedEvent
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,6 @@ import java.text.DecimalFormat
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.filter
|
|
||||||
|
|
||||||
typealias SuccessLoadedSwapData = Map<SwapProvider, SwapState.QuotesLoadedState>
|
typealias SuccessLoadedSwapData = Map<SwapProvider, SwapState.QuotesLoadedState>
|
||||||
|
|
||||||
|
|
@ -1098,6 +1097,7 @@ internal class SwapModel @Inject constructor(
|
||||||
receiveBlockchain = toCurrency.network.name,
|
receiveBlockchain = toCurrency.network.name,
|
||||||
sendToken = fromCurrency.symbol,
|
sendToken = fromCurrency.symbol,
|
||||||
receiveToken = toCurrency.symbol,
|
receiveToken = toCurrency.symbol,
|
||||||
|
feeToken = getFeeToken().symbol,
|
||||||
fromDerivationIndex = fromDerivationIndex,
|
fromDerivationIndex = fromDerivationIndex,
|
||||||
toDerivationIndex = toDerivationIndex,
|
toDerivationIndex = toDerivationIndex,
|
||||||
),
|
),
|
||||||
|
|
@ -1713,6 +1713,7 @@ internal class SwapModel @Inject constructor(
|
||||||
blockchain = fromToken.network.name,
|
blockchain = fromToken.network.name,
|
||||||
token = fromToken.symbol,
|
token = fromToken.symbol,
|
||||||
feeType = AnalyticsParam.FeeType.fromString(feeType.getNameForAnalytics()),
|
feeType = AnalyticsParam.FeeType.fromString(feeType.getNameForAnalytics()),
|
||||||
|
feeToken = getFeeToken().symbol,
|
||||||
)
|
)
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
Basic.TransactionSent(
|
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) {
|
private fun sendApproveSuccessEvent(fromToken: CryptoCurrency, feeType: FeeType, approveType: SwapApproveType) {
|
||||||
|
val feeToken = getFeeToken().symbol
|
||||||
val event = AnalyticsParam.TxSentFrom.Approve(
|
val event = AnalyticsParam.TxSentFrom.Approve(
|
||||||
blockchain = fromToken.network.name,
|
blockchain = fromToken.network.name,
|
||||||
token = fromToken.symbol,
|
token = fromToken.symbol,
|
||||||
feeType = AnalyticsParam.FeeType.fromString(feeType.getNameForAnalytics()),
|
feeType = AnalyticsParam.FeeType.fromString(feeType.getNameForAnalytics()),
|
||||||
permissionType = approveType.getNameForAnalytics(),
|
permissionType = approveType.getNameForAnalytics(),
|
||||||
|
feeToken = feeToken,
|
||||||
)
|
)
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
Basic.TransactionSent(
|
Basic.TransactionSent(
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ internal class YieldSupplyApproveModel @Inject constructor(
|
||||||
blockchain = cryptoCurrency.network.name,
|
blockchain = cryptoCurrency.network.name,
|
||||||
token = cryptoCurrency.symbol,
|
token = cryptoCurrency.symbol,
|
||||||
feeType = AnalyticsParam.FeeType.Normal,
|
feeType = AnalyticsParam.FeeType.Normal,
|
||||||
|
feeToken = feeCryptoCurrencyStatus.currency.symbol,
|
||||||
)
|
)
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
Basic.TransactionSent(
|
Basic.TransactionSent(
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
||||||
blockchain = cryptoCurrency.network.name,
|
blockchain = cryptoCurrency.network.name,
|
||||||
token = cryptoCurrency.symbol,
|
token = cryptoCurrency.symbol,
|
||||||
feeType = AnalyticsParam.FeeType.Normal,
|
feeType = AnalyticsParam.FeeType.Normal,
|
||||||
|
feeToken = feeCryptoCurrencyStatusFlow.value.currency.symbol,
|
||||||
)
|
)
|
||||||
analytics.send(
|
analytics.send(
|
||||||
YieldSupplyAnalytics.FundsEarned(
|
YieldSupplyAnalytics.FundsEarned(
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
|
||||||
blockchain = cryptoCurrency.network.name,
|
blockchain = cryptoCurrency.network.name,
|
||||||
token = cryptoCurrency.symbol,
|
token = cryptoCurrency.symbol,
|
||||||
feeType = AnalyticsParam.FeeType.Normal,
|
feeType = AnalyticsParam.FeeType.Normal,
|
||||||
|
feeToken = feeCryptoCurrencyStatus.currency.symbol,
|
||||||
)
|
)
|
||||||
analytics.send(
|
analytics.send(
|
||||||
Basic.TransactionSent(
|
Basic.TransactionSent(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue