Updated on 2026-08-14
This commit is contained in:
commit
714eb51ce5
34 changed files with 345 additions and 114 deletions
|
|
@ -296,6 +296,7 @@ internal class ChildFactory @Inject constructor(
|
||||||
params = TokenDetailsComponent.Params(
|
params = TokenDetailsComponent.Params(
|
||||||
userWalletId = route.userWalletId,
|
userWalletId = route.userWalletId,
|
||||||
currency = route.currency,
|
currency = route.currency,
|
||||||
|
navigationAction = route.navigationAction,
|
||||||
),
|
),
|
||||||
componentFactory = tokenDetailsComponentFactory,
|
componentFactory = tokenDetailsComponentFactory,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.nft.models.NFTAsset
|
import com.tangem.domain.nft.models.NFTAsset
|
||||||
import com.tangem.domain.onramp.model.OnrampSource
|
import com.tangem.domain.onramp.model.OnrampSource
|
||||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||||
|
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
|
|
@ -55,6 +56,7 @@ sealed class AppRoute(val path: String) : Route {
|
||||||
data class CurrencyDetails(
|
data class CurrencyDetails(
|
||||||
val userWalletId: UserWalletId,
|
val userWalletId: UserWalletId,
|
||||||
val currency: CryptoCurrency,
|
val currency: CryptoCurrency,
|
||||||
|
val navigationAction: NavigationAction? = null,
|
||||||
) : AppRoute(path = "/currency_details/${userWalletId.stringValue}/${currency.id.value}")
|
) : AppRoute(path = "/currency_details/${userWalletId.stringValue}/${currency.id.value}")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,11 @@ class TokenItemStateConverter(
|
||||||
private val iconStateProvider: (CryptoCurrencyStatus) -> CurrencyIconState = {
|
private val iconStateProvider: (CryptoCurrencyStatus) -> CurrencyIconState = {
|
||||||
CryptoCurrencyToIconStateConverter().convert(it)
|
CryptoCurrencyToIconStateConverter().convert(it)
|
||||||
},
|
},
|
||||||
|
private val onApyLabelClick: ((CryptoCurrencyStatus) -> Unit)? = null,
|
||||||
private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = {
|
private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = {
|
||||||
createTitleState(it, yieldModuleApyMap, stakingApyMap)
|
createTitleState(it, yieldModuleApyMap, stakingApyMap, {
|
||||||
|
onApyLabelClick?.invoke(it)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
private val subtitleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.SubtitleState? = {
|
private val subtitleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.SubtitleState? = {
|
||||||
createSubtitleState(it, appCurrency)
|
createSubtitleState(it, appCurrency)
|
||||||
|
|
@ -102,6 +105,9 @@ class TokenItemStateConverter(
|
||||||
onItemLongClick = onItemLongClick?.let { onItemLongClick ->
|
onItemLongClick = onItemLongClick?.let { onItemLongClick ->
|
||||||
{ onItemLongClick(it, this) }
|
{ onItemLongClick(it, this) }
|
||||||
},
|
},
|
||||||
|
onApyLabelClick = onApyLabelClick?.let { onApyLabelClick ->
|
||||||
|
{ onApyLabelClick(this) }
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,6 +166,7 @@ class TokenItemStateConverter(
|
||||||
currencyStatus: CryptoCurrencyStatus,
|
currencyStatus: CryptoCurrencyStatus,
|
||||||
yieldModuleApyMap: Map<String, String>,
|
yieldModuleApyMap: Map<String, String>,
|
||||||
stakingApyMap: Map<String, List<Yield.Validator>>,
|
stakingApyMap: Map<String, List<Yield.Validator>>,
|
||||||
|
onApyLabelClick: () -> Unit,
|
||||||
): TokenItemState.TitleState {
|
): TokenItemState.TitleState {
|
||||||
return when (val value = currencyStatus.value) {
|
return when (val value = currencyStatus.value) {
|
||||||
is CryptoCurrencyStatus.Loading,
|
is CryptoCurrencyStatus.Loading,
|
||||||
|
|
@ -184,6 +191,7 @@ class TokenItemStateConverter(
|
||||||
hasPending = value.hasCurrentNetworkTransactions,
|
hasPending = value.hasCurrentNetworkTransactions,
|
||||||
earnApy = earnApyText,
|
earnApy = earnApyText,
|
||||||
earnApyIsActive = isActive,
|
earnApyIsActive = isActive,
|
||||||
|
onApyLabelClick = onApyLabelClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ package com.tangem.core.analytics.models.event
|
||||||
|
|
||||||
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.ACTION
|
||||||
|
import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN
|
||||||
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.STATE
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -108,6 +111,21 @@ sealed class MainScreenAnalyticsEvent(
|
||||||
event = "Hot Token Error",
|
event = "Hot Token Error",
|
||||||
params = mapOf(ERROR_CODE to errorCode),
|
params = mapOf(ERROR_CODE to errorCode),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class ApyClicked(
|
||||||
|
val token: String,
|
||||||
|
val blockchain: String,
|
||||||
|
val action: String,
|
||||||
|
val state: String,
|
||||||
|
) : MainScreenAnalyticsEvent(
|
||||||
|
event = "APY Clicked",
|
||||||
|
params = mapOf(
|
||||||
|
TOKEN_PARAM to token,
|
||||||
|
BLOCKCHAIN to blockchain,
|
||||||
|
ACTION to action,
|
||||||
|
STATE to state,
|
||||||
|
),
|
||||||
|
)
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import com.tangem.datasource.api.tangemTech.models.YieldSupplyMarketTokenDto
|
||||||
import com.tangem.datasource.api.tangemTech.models.YieldTokenChartResponse
|
import com.tangem.datasource.api.tangemTech.models.YieldTokenChartResponse
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Header
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
|
|
@ -34,6 +35,7 @@ interface YieldSupplyApi {
|
||||||
@POST("api/v1/module/activate")
|
@POST("api/v1/module/activate")
|
||||||
suspend fun activateYieldModule(
|
suspend fun activateYieldModule(
|
||||||
@Body body: YieldSupplyChangeTokenStatusBody,
|
@Body body: YieldSupplyChangeTokenStatusBody,
|
||||||
|
@Header("userWalletId") userWalletId: String,
|
||||||
): ApiResponse<YieldModuleStatusResponse>
|
): ApiResponse<YieldModuleStatusResponse>
|
||||||
|
|
||||||
@POST("api/v1/module/deactivate")
|
@POST("api/v1/module/deactivate")
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.core.ui.components.token.internal
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -60,7 +61,11 @@ private fun ContentTitle(state: TokenTitleState.Content, modifier: Modifier = Mo
|
||||||
YieldSupplyApyLabel(
|
YieldSupplyApyLabel(
|
||||||
apy = state.earnApy,
|
apy = state.earnApy,
|
||||||
isActive = state.earnApyIsActive,
|
isActive = state.earnApyIsActive,
|
||||||
modifier = Modifier.align(alignment = Alignment.CenterVertically),
|
modifier = Modifier
|
||||||
|
.align(alignment = Alignment.CenterVertically)
|
||||||
|
.clickable {
|
||||||
|
state.onApyLabelClick?.invoke()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ sealed class TokenItemState {
|
||||||
/** Callback which will be called when an item is long clicked */
|
/** Callback which will be called when an item is long clicked */
|
||||||
abstract val onItemLongClick: ((TokenItemState) -> Unit)?
|
abstract val onItemLongClick: ((TokenItemState) -> Unit)?
|
||||||
|
|
||||||
|
/** Callback which will be called when an apy label is clicked */
|
||||||
|
abstract val onApyLabelClick: ((TokenItemState) -> Unit)?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading token state
|
* Loading token state
|
||||||
*
|
*
|
||||||
|
|
@ -58,6 +61,7 @@ sealed class TokenItemState {
|
||||||
override val subtitle2State: Subtitle2State = Subtitle2State.Loading
|
override val subtitle2State: Subtitle2State = Subtitle2State.Loading
|
||||||
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
||||||
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
|
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
|
||||||
|
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -73,6 +77,7 @@ sealed class TokenItemState {
|
||||||
override val subtitle2State: Subtitle2State = Subtitle2State.Locked
|
override val subtitle2State: Subtitle2State = Subtitle2State.Locked
|
||||||
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
||||||
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
|
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
|
||||||
|
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -96,6 +101,7 @@ sealed class TokenItemState {
|
||||||
override val subtitle2State: Subtitle2State?,
|
override val subtitle2State: Subtitle2State?,
|
||||||
override val onItemClick: ((TokenItemState) -> Unit)?,
|
override val onItemClick: ((TokenItemState) -> Unit)?,
|
||||||
override val onItemLongClick: ((TokenItemState) -> Unit)?,
|
override val onItemLongClick: ((TokenItemState) -> Unit)?,
|
||||||
|
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null,
|
||||||
) : TokenItemState()
|
) : TokenItemState()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -116,6 +122,7 @@ sealed class TokenItemState {
|
||||||
override val fiatAmountState: FiatAmountState? = null
|
override val fiatAmountState: FiatAmountState? = null
|
||||||
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
||||||
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
|
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
|
||||||
|
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -133,6 +140,7 @@ sealed class TokenItemState {
|
||||||
override val iconState: CurrencyIconState,
|
override val iconState: CurrencyIconState,
|
||||||
override val titleState: TitleState,
|
override val titleState: TitleState,
|
||||||
override val subtitleState: SubtitleState? = null,
|
override val subtitleState: SubtitleState? = null,
|
||||||
|
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null,
|
||||||
override val onItemClick: ((TokenItemState) -> Unit)?,
|
override val onItemClick: ((TokenItemState) -> Unit)?,
|
||||||
override val onItemLongClick: ((TokenItemState) -> Unit)?,
|
override val onItemLongClick: ((TokenItemState) -> Unit)?,
|
||||||
) : TokenItemState() {
|
) : TokenItemState() {
|
||||||
|
|
@ -159,6 +167,7 @@ sealed class TokenItemState {
|
||||||
override val fiatAmountState: FiatAmountState? = null
|
override val fiatAmountState: FiatAmountState? = null
|
||||||
override val subtitle2State: Subtitle2State? = null
|
override val subtitle2State: Subtitle2State? = null
|
||||||
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
override val onItemClick: ((TokenItemState) -> Unit)? = null
|
||||||
|
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
@ -170,6 +179,7 @@ sealed class TokenItemState {
|
||||||
val isAvailable: Boolean = true,
|
val isAvailable: Boolean = true,
|
||||||
val earnApy: TextReference? = null,
|
val earnApy: TextReference? = null,
|
||||||
val earnApyIsActive: Boolean = false,
|
val earnApyIsActive: Boolean = false,
|
||||||
|
val onApyLabelClick: (() -> Unit)? = null,
|
||||||
) : TitleState()
|
) : TitleState()
|
||||||
|
|
||||||
data object Loading : TitleState()
|
data object Loading : TitleState()
|
||||||
|
|
|
||||||
|
|
@ -76,18 +76,22 @@ internal class DefaultYieldSupplyRepository(
|
||||||
(walletManager as? YieldSupplyProvider)?.isSupported() ?: false
|
(walletManager as? YieldSupplyProvider)?.isSupported() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun activateProtocol(cryptoCurrencyToken: CryptoCurrency.Token, address: String): Boolean =
|
override suspend fun activateProtocol(
|
||||||
withContext(dispatchers.io) {
|
userWalletId: UserWalletId,
|
||||||
val chainId = Blockchain.fromNetworkId(cryptoCurrencyToken.network.backendId)?.getChainId()
|
cryptoCurrencyToken: CryptoCurrency.Token,
|
||||||
?: error("Chain id is required for evm's")
|
address: String,
|
||||||
yieldSupplyApi.activateYieldModule(
|
): Boolean = withContext(dispatchers.io) {
|
||||||
YieldSupplyChangeTokenStatusBody(
|
val chainId = Blockchain.fromNetworkId(cryptoCurrencyToken.network.backendId)?.getChainId()
|
||||||
tokenAddress = cryptoCurrencyToken.contractAddress,
|
?: error("Chain id is required for evm's")
|
||||||
chainId = chainId,
|
yieldSupplyApi.activateYieldModule(
|
||||||
userAddress = address,
|
body = YieldSupplyChangeTokenStatusBody(
|
||||||
),
|
tokenAddress = cryptoCurrencyToken.contractAddress,
|
||||||
).getOrThrow().isActive
|
chainId = chainId,
|
||||||
}
|
userAddress = address,
|
||||||
|
),
|
||||||
|
userWalletId = userWalletId.stringValue,
|
||||||
|
).getOrThrow().isActive
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun deactivateProtocol(cryptoCurrencyToken: CryptoCurrency.Token, address: String): Boolean =
|
override suspend fun deactivateProtocol(cryptoCurrencyToken: CryptoCurrency.Token, address: String): Boolean =
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
|
|
@ -103,14 +107,18 @@ internal class DefaultYieldSupplyRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun saveTokenProtocolStatus(
|
override suspend fun saveTokenProtocolStatus(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
yieldSupplyEnterStatus: YieldSupplyEnterStatus,
|
yieldSupplyEnterStatus: YieldSupplyEnterStatus,
|
||||||
) {
|
) {
|
||||||
statusMap[cryptoCurrency.id.value] = yieldSupplyEnterStatus
|
statusMap["${userWalletId}_${cryptoCurrency.id.value}"] = yieldSupplyEnterStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTokenProtocolStatus(cryptoCurrency: CryptoCurrency): YieldSupplyEnterStatus? {
|
override fun getTokenProtocolStatus(
|
||||||
return statusMap[cryptoCurrency.id.value]
|
userWalletId: UserWalletId,
|
||||||
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
): YieldSupplyEnterStatus? {
|
||||||
|
return statusMap["${userWalletId}_${cryptoCurrency.id.value}"]
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<YieldMarketToken>.enrichNetworkIds(): List<YieldMarketToken> {
|
private fun List<YieldMarketToken>.enrichNetworkIds(): List<YieldMarketToken> {
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,10 @@ internal class FeedbackDataBuilder {
|
||||||
|
|
||||||
fun addBlockchainError(info: BlockchainInfo, error: BlockchainErrorInfo) {
|
fun addBlockchainError(info: BlockchainInfo, error: BlockchainErrorInfo) {
|
||||||
builder.appendKeyValue("Blockchain", info.blockchain)
|
builder.appendKeyValue("Blockchain", info.blockchain)
|
||||||
|
builder.appendAddresses(
|
||||||
|
key = "Explorer link${info.explorerLinks.isMultiple(suffix = "s")}",
|
||||||
|
addresses = info.explorerLinks,
|
||||||
|
)
|
||||||
builder.appendKeyValue("Derivation path", info.derivationPath)
|
builder.appendKeyValue("Derivation path", info.derivationPath)
|
||||||
builder.appendKeyValue("Host", info.host)
|
builder.appendKeyValue("Host", info.host)
|
||||||
builder.appendKeyValue("Token", error.tokenSymbol)
|
builder.appendKeyValue("Token", error.tokenSymbol)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.tangem.domain.tokens.model.details
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed class NavigationAction {
|
||||||
|
data object Staking : NavigationAction()
|
||||||
|
data class YieldSupply(val isActive: Boolean) : NavigationAction()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.tangem.domain.yield.supply.models
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
data class YieldSupplyMaxFee(
|
||||||
|
val nativeMaxFee: BigDecimal,
|
||||||
|
val tokenMaxFee: BigDecimal,
|
||||||
|
val fiatMaxFee: BigDecimal,
|
||||||
|
)
|
||||||
|
|
@ -46,7 +46,11 @@ interface YieldSupplyRepository {
|
||||||
* May throw on network/backend errors or if required chain id cannot be resolved.
|
* May throw on network/backend errors or if required chain id cannot be resolved.
|
||||||
*/
|
*/
|
||||||
@Throws
|
@Throws
|
||||||
suspend fun activateProtocol(cryptoCurrencyToken: CryptoCurrency.Token, address: String): Boolean
|
suspend fun activateProtocol(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
cryptoCurrencyToken: CryptoCurrency.Token,
|
||||||
|
address: String,
|
||||||
|
): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deactivate yield protocol for the specified token.
|
* Deactivate yield protocol for the specified token.
|
||||||
|
|
@ -59,25 +63,32 @@ interface YieldSupplyRepository {
|
||||||
suspend fun deactivateProtocol(cryptoCurrencyToken: CryptoCurrency.Token, address: String): Boolean
|
suspend fun deactivateProtocol(cryptoCurrencyToken: CryptoCurrency.Token, address: String): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the last user-initiated yield protocol action for the given currency.
|
* Save the last user‑initiated yield protocol action for the given wallet and currency.
|
||||||
*
|
*
|
||||||
* The saved value helps the UI render an intermediate "processing" state while waiting
|
* The saved value helps the UI render an intermediate "processing" state while waiting
|
||||||
* for the definitive protocol status to be fetched from the network. This information
|
* for the definitive protocol status to be fetched from the network. This information
|
||||||
* is transient and not intended to be persisted across app restarts.
|
* is transient (in‑memory only) and is not persisted across app restarts.
|
||||||
*
|
*
|
||||||
|
* @param userWalletId the wallet the action was performed with
|
||||||
* @param cryptoCurrency the currency or token the action relates to
|
* @param cryptoCurrency the currency or token the action relates to
|
||||||
* @param yieldSupplyEnterStatus the last action intent: [YieldSupplyEnterStatus.Enter] or [YieldSupplyEnterStatus.Exit]
|
* @param yieldSupplyEnterStatus the last action intent: [YieldSupplyEnterStatus.Enter] or [YieldSupplyEnterStatus.Exit]
|
||||||
*/
|
*/
|
||||||
suspend fun saveTokenProtocolStatus(cryptoCurrency: CryptoCurrency, yieldSupplyEnterStatus: YieldSupplyEnterStatus)
|
suspend fun saveTokenProtocolStatus(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
yieldSupplyEnterStatus: YieldSupplyEnterStatus,
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the last saved user-initiated yield protocol action for the given currency, if any.
|
* Get the last saved user‑initiated yield protocol action for the given wallet and currency, if any.
|
||||||
*
|
*
|
||||||
* Used to determine whether the UI should display an intermediate "processing" state
|
* Used to determine whether the UI should display an intermediate "processing" state
|
||||||
* until the protocol status retrieved from backend reflects the change.
|
* until the protocol status retrieved from backend reflects the change. The value is
|
||||||
|
* transient (in‑memory only) and is not persisted across app restarts.
|
||||||
*
|
*
|
||||||
|
* @param userWalletId the wallet to query
|
||||||
* @param cryptoCurrency the currency or token to query
|
* @param cryptoCurrency the currency or token to query
|
||||||
* @return the last action intent or null if nothing has been recorded
|
* @return the last action intent or null if nothing has been recorded
|
||||||
*/
|
*/
|
||||||
fun getTokenProtocolStatus(cryptoCurrency: CryptoCurrency): YieldSupplyEnterStatus?
|
fun getTokenProtocolStatus(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): YieldSupplyEnterStatus?
|
||||||
}
|
}
|
||||||
|
|
@ -2,15 +2,23 @@ package com.tangem.domain.yield.supply.usecase
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.yield.supply.YieldSupplyRepository
|
import com.tangem.domain.yield.supply.YieldSupplyRepository
|
||||||
|
|
||||||
class YieldSupplyActivateUseCase(
|
class YieldSupplyActivateUseCase(
|
||||||
private val yieldSupplyRepository: YieldSupplyRepository,
|
private val yieldSupplyRepository: YieldSupplyRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(cryptoCurrency: CryptoCurrency, address: String): Either<Throwable, Boolean> =
|
suspend operator fun invoke(
|
||||||
Either.catch {
|
userWalletId: UserWalletId,
|
||||||
val token = cryptoCurrency as? CryptoCurrency.Token ?: error("Token expected")
|
cryptoCurrency: CryptoCurrency,
|
||||||
yieldSupplyRepository.activateProtocol(token, address)
|
address: String,
|
||||||
}
|
): Either<Throwable, Boolean> = Either.catch {
|
||||||
|
val token = cryptoCurrency as? CryptoCurrency.Token ?: error("Token expected")
|
||||||
|
yieldSupplyRepository.activateProtocol(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
cryptoCurrencyToken = token,
|
||||||
|
address = address,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,16 +10,17 @@ import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.quotes.QuotesRepository
|
import com.tangem.domain.quotes.QuotesRepository
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||||
import com.tangem.domain.yield.supply.YieldSupplyRepository
|
import com.tangem.domain.yield.supply.YieldSupplyRepository
|
||||||
|
import com.tangem.domain.yield.supply.models.YieldSupplyMaxFee
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the max allowed network fee for a Yield Supply transaction.
|
* Calculates the max allowed network fee for a Yield Supply transaction.
|
||||||
*
|
*
|
||||||
* Result is a Pair<BigDecimal, BigDecimal> where:
|
* Returns [Either] of [YieldSupplyMaxFee] with:
|
||||||
* - first: fee in native coin units (maxFeeNative)
|
* - nativeMaxFee: fee in native coin units
|
||||||
* - second: the same fee converted to token units using the fiat-rate ratio
|
* - tokenMaxFee: fee converted to token units using the native/token rate ratio
|
||||||
* (nativeFiatRate / tokenFiatRate).
|
* - tokenFiatMaxFee: fee value in fiat
|
||||||
*
|
*
|
||||||
* Uses YieldMarketToken.maxFeeNative and the same conversion logic as
|
* Uses YieldMarketToken.maxFeeNative and the same conversion logic as
|
||||||
* [YieldSupplyGetCurrentFeeUseCase].
|
* [YieldSupplyGetCurrentFeeUseCase].
|
||||||
|
|
@ -33,7 +34,7 @@ class YieldSupplyGetMaxFeeUseCase(
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||||
): Either<Throwable, Pair<BigDecimal, BigDecimal>> = catch {
|
): Either<Throwable, YieldSupplyMaxFee> = catch {
|
||||||
val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token
|
val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token
|
||||||
?: error("CryptoCurrency must be token for max fee calculation")
|
?: error("CryptoCurrency must be token for max fee calculation")
|
||||||
|
|
||||||
|
|
@ -60,6 +61,7 @@ class YieldSupplyGetMaxFeeUseCase(
|
||||||
.firstOrNull { it.yieldSupplyKey == token.yieldSupplyKey() }
|
.firstOrNull { it.yieldSupplyKey == token.yieldSupplyKey() }
|
||||||
val marketToken = cachedMarketToken ?: yieldSupplyRepository.getTokenStatus(token)
|
val marketToken = cachedMarketToken ?: yieldSupplyRepository.getTokenStatus(token)
|
||||||
val maxFeeNative = marketToken.maxFeeNative
|
val maxFeeNative = marketToken.maxFeeNative
|
||||||
|
val maxFeeToken = maxFeeNative.multiply(nativeFiatRate)
|
||||||
|
|
||||||
val rateRatio = nativeFiatRate.divide(
|
val rateRatio = nativeFiatRate.divide(
|
||||||
fiatRate,
|
fiatRate,
|
||||||
|
|
@ -69,6 +71,10 @@ class YieldSupplyGetMaxFeeUseCase(
|
||||||
|
|
||||||
val tokenValue = rateRatio.multiply(maxFeeNative)
|
val tokenValue = rateRatio.multiply(maxFeeNative)
|
||||||
|
|
||||||
maxFeeNative to tokenValue.stripTrailingZeros()
|
YieldSupplyMaxFee(
|
||||||
|
nativeMaxFee = maxFeeNative,
|
||||||
|
tokenMaxFee = maxFeeToken,
|
||||||
|
fiatMaxFee = tokenValue.stripTrailingZeros(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,12 +4,14 @@ import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||||
|
|
||||||
interface TokenDetailsComponent : ComposableContentComponent {
|
interface TokenDetailsComponent : ComposableContentComponent {
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
val userWalletId: UserWalletId,
|
val userWalletId: UserWalletId,
|
||||||
val currency: CryptoCurrency,
|
val currency: CryptoCurrency,
|
||||||
|
val navigationAction: NavigationAction? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
interface Factory : ComponentFactory<Params, TokenDetailsComponent>
|
interface Factory : ComponentFactory<Params, TokenDetailsComponent>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import com.tangem.core.decompose.model.getOrCreateModel
|
||||||
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
||||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
|
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
|
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
|
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
|
||||||
|
|
@ -76,6 +77,8 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
||||||
params = YieldSupplyComponent.Params(
|
params = YieldSupplyComponent.Params(
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
cryptoCurrency = params.currency,
|
cryptoCurrency = params.currency,
|
||||||
|
handleNavigation = (params.navigationAction as? NavigationAction.YieldSupply)
|
||||||
|
?.isActive,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ import com.tangem.domain.tokens.model.TokenActionsState
|
||||||
import com.tangem.domain.tokens.model.analytics.*
|
import com.tangem.domain.tokens.model.analytics.*
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
|
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.DetailsScreenOpened.TokenBalance
|
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.DetailsScreenOpened.TokenBalance
|
||||||
|
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||||
import com.tangem.domain.tokens.model.details.TokenAction
|
import com.tangem.domain.tokens.model.details.TokenAction
|
||||||
import com.tangem.domain.transaction.error.AssociateAssetError
|
import com.tangem.domain.transaction.error.AssociateAssetError
|
||||||
import com.tangem.domain.transaction.error.IncompleteTransactionError
|
import com.tangem.domain.transaction.error.IncompleteTransactionError
|
||||||
|
|
@ -222,6 +223,7 @@ internal class TokenDetailsModel @Inject constructor(
|
||||||
updateContent()
|
updateContent()
|
||||||
handleBalanceHiding()
|
handleBalanceHiding()
|
||||||
checkForActionUpdates()
|
checkForActionUpdates()
|
||||||
|
handleNavigationParam()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onResume() {
|
fun onResume() {
|
||||||
|
|
@ -1246,6 +1248,12 @@ internal class TokenDetailsModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleNavigationParam() {
|
||||||
|
if (params.navigationAction is NavigationAction.Staking) {
|
||||||
|
openStaking()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L
|
const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,12 @@ package com.tangem.feature.wallet.child.wallet.model.intents
|
||||||
import arrow.core.getOrElse
|
import arrow.core.getOrElse
|
||||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
|
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||||
import com.tangem.core.decompose.di.ModelScoped
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
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.staking.YieldBalance
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.models.wallet.isLocked
|
import com.tangem.domain.models.wallet.isLocked
|
||||||
|
|
@ -14,6 +17,7 @@ import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
|
||||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||||
import com.tangem.domain.tokens.model.TokenActionsState
|
import com.tangem.domain.tokens.model.TokenActionsState
|
||||||
|
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||||
|
|
@ -45,6 +49,8 @@ internal interface WalletContentClickIntents {
|
||||||
|
|
||||||
fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||||
|
|
||||||
|
fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus)
|
||||||
|
|
||||||
fun onAccountExpandClick(account: Account)
|
fun onAccountExpandClick(account: Account)
|
||||||
|
|
||||||
fun onAccountCollapseClick(account: Account)
|
fun onAccountCollapseClick(account: Account)
|
||||||
|
|
@ -154,6 +160,42 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
|
||||||
|
val navigationAction = if (currencyStatus.currency is CryptoCurrency.Token) {
|
||||||
|
NavigationAction.YieldSupply(currencyStatus.value.yieldSupplyStatus?.isActive == true)
|
||||||
|
} else {
|
||||||
|
NavigationAction.Staking
|
||||||
|
}
|
||||||
|
val event = when (navigationAction) {
|
||||||
|
is NavigationAction.YieldSupply -> {
|
||||||
|
MainScreenAnalyticsEvent.ApyClicked(
|
||||||
|
token = currencyStatus.currency.symbol,
|
||||||
|
blockchain = currencyStatus.currency.network.name,
|
||||||
|
action = "Earning",
|
||||||
|
state = if (currencyStatus.value.yieldSupplyStatus?.isActive == true) {
|
||||||
|
"Enabled"
|
||||||
|
} else {
|
||||||
|
"Disabled"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is NavigationAction.Staking -> {
|
||||||
|
MainScreenAnalyticsEvent.ApyClicked(
|
||||||
|
token = currencyStatus.currency.symbol,
|
||||||
|
blockchain = currencyStatus.currency.network.name,
|
||||||
|
action = "Staking",
|
||||||
|
state = if (currencyStatus.value.yieldBalance is YieldBalance.Data) {
|
||||||
|
"Enabled"
|
||||||
|
} else {
|
||||||
|
"Disabled"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
analyticsEventHandler.send(event)
|
||||||
|
router.openTokenDetails(userWalletId, currencyStatus, navigationAction)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onAccountExpandClick(account: Account) {
|
override fun onAccountExpandClick(account: Account) {
|
||||||
val userWalletId = stateHolder.getSelectedWalletId()
|
val userWalletId = stateHolder.getSelectedWalletId()
|
||||||
accountDependencies.expandedAccountsHolder.expandAccount(userWalletId, account.accountId)
|
accountDependencies.expandedAccountsHolder.expandAccount(userWalletId, account.accountId)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||||
import com.tangem.domain.redux.ReduxStateHolder
|
import com.tangem.domain.redux.ReduxStateHolder
|
||||||
import com.tangem.domain.redux.StateDialog
|
import com.tangem.domain.redux.StateDialog
|
||||||
|
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||||
import com.tangem.domain.tokens.model.details.TokenAction
|
import com.tangem.domain.tokens.model.details.TokenAction
|
||||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||||
|
|
@ -66,13 +67,18 @@ internal class DefaultWalletRouter @Inject constructor(
|
||||||
urlOpener.openUrl(url)
|
urlOpener.openUrl(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
|
override fun openTokenDetails(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
currencyStatus: CryptoCurrencyStatus,
|
||||||
|
navigationAction: NavigationAction?,
|
||||||
|
) {
|
||||||
val networkAddress = currencyStatus.value.networkAddress
|
val networkAddress = currencyStatus.value.networkAddress
|
||||||
if (networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()) {
|
if (networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()) {
|
||||||
router.push(
|
router.push(
|
||||||
AppRoute.CurrencyDetails(
|
AppRoute.CurrencyDetails(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
currency = currencyStatus.currency,
|
currency = currencyStatus.currency,
|
||||||
|
navigationAction = navigationAction,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||||
|
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||||
import com.tangem.domain.tokens.model.details.TokenAction
|
import com.tangem.domain.tokens.model.details.TokenAction
|
||||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||||
|
|
@ -42,7 +43,11 @@ internal interface InnerWalletRouter {
|
||||||
fun openUrl(url: String)
|
fun openUrl(url: String)
|
||||||
|
|
||||||
/** Open token details screen */
|
/** Open token details screen */
|
||||||
fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus)
|
fun openTokenDetails(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
currencyStatus: CryptoCurrencyStatus,
|
||||||
|
navigationAction: NavigationAction? = null,
|
||||||
|
)
|
||||||
|
|
||||||
/** Open stories screen */
|
/** Open stories screen */
|
||||||
fun openStoriesScreen()
|
fun openStoriesScreen()
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,17 @@ internal class TokenListStateConverter(
|
||||||
clickIntents.onTokenItemLongClick(selectedWallet.walletId, currencyStatus)
|
clickIntents.onTokenItemLongClick(selectedWallet.walletId, currencyStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val onApyLabelClick: (currencyStatus: CryptoCurrencyStatus) -> Unit = { currencyStatus ->
|
||||||
|
clickIntents.onApyLabelClick(selectedWallet.walletId, currencyStatus)
|
||||||
|
}
|
||||||
|
|
||||||
private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter(
|
private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter(
|
||||||
appCurrency = appCurrency,
|
appCurrency = appCurrency,
|
||||||
yieldModuleApyMap = yieldModuleApyMap,
|
yieldModuleApyMap = yieldModuleApyMap,
|
||||||
stakingApyMap = stakingApyMap,
|
stakingApyMap = stakingApyMap,
|
||||||
onItemClick = { _, status -> onTokenClick(accountId, status) },
|
onItemClick = { _, status -> onTokenClick(accountId, status) },
|
||||||
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
|
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
|
||||||
|
onApyLabelClick = { status -> onApyLabelClick(status) },
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun convert(value: WalletTokensListState): WalletTokensListState {
|
override fun convert(value: WalletTokensListState): WalletTokensListState {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ interface YieldSupplyComponent : ComposableContentComponent {
|
||||||
data class Params(
|
data class Params(
|
||||||
val userWalletId: UserWalletId,
|
val userWalletId: UserWalletId,
|
||||||
val cryptoCurrency: CryptoCurrency,
|
val cryptoCurrency: CryptoCurrency,
|
||||||
|
val handleNavigation: Boolean? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
interface Factory : ComponentFactory<Params, YieldSupplyComponent>
|
interface Factory : ComponentFactory<Params, YieldSupplyComponent>
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,15 @@ sealed class YieldSupplyAnalytics(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
data object FundsEarned : YieldSupplyAnalytics(
|
data class FundsEarned(
|
||||||
|
val token: String,
|
||||||
|
val blockchain: String,
|
||||||
|
) : YieldSupplyAnalytics(
|
||||||
event = "Funds Earned",
|
event = "Funds Earned",
|
||||||
|
params = mapOf(
|
||||||
|
TOKEN_PARAM to token,
|
||||||
|
BLOCKCHAIN to blockchain,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class FundsWithdrawn(
|
data class FundsWithdrawn(
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ internal sealed class YieldSupplyFeeUM {
|
||||||
data class Content(
|
data class Content(
|
||||||
val transactionDataList: ImmutableList<TransactionData.Uncompiled>,
|
val transactionDataList: ImmutableList<TransactionData.Uncompiled>,
|
||||||
val feeFiatValue: TextReference,
|
val feeFiatValue: TextReference,
|
||||||
|
// TODO move to FeePolicyUM
|
||||||
|
val estimatedFiatValue: TextReference,
|
||||||
val tokenFeeFiatValue: TextReference,
|
val tokenFeeFiatValue: TextReference,
|
||||||
val maxNetworkFeeFiatValue: TextReference,
|
val maxNetworkFeeFiatValue: TextReference,
|
||||||
val minTopUpFiatValue: TextReference,
|
val minTopUpFiatValue: TextReference,
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,7 @@ private class YieldSupplyActionContentPreviewProvider : PreviewParameterProvider
|
||||||
maxNetworkFeeFiatValue = stringReference("$8.50"),
|
maxNetworkFeeFiatValue = stringReference("$8.50"),
|
||||||
minTopUpFiatValue = stringReference("$50"),
|
minTopUpFiatValue = stringReference("$50"),
|
||||||
feeNoteValue = TextReference.EMPTY,
|
feeNoteValue = TextReference.EMPTY,
|
||||||
|
estimatedFiatValue = TextReference.EMPTY,
|
||||||
),
|
),
|
||||||
isPrimaryButtonEnabled = false,
|
isPrimaryButtonEnabled = false,
|
||||||
isTransactionSending = false,
|
isTransactionSending = false,
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,16 @@ internal class YieldSupplyModel @Inject constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkIfYieldSupplyIsAvailable()
|
checkIfYieldSupplyIsAvailable()
|
||||||
|
params.handleNavigation?.let { handle ->
|
||||||
|
if (handle) {
|
||||||
|
modelScope.launch {
|
||||||
|
delay(timeMillis = 1000)
|
||||||
|
bottomSheetNavigation.activate(Unit)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onStartEarningClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkIfYieldSupplyIsAvailable() {
|
private fun checkIfYieldSupplyIsAvailable() {
|
||||||
|
|
@ -185,7 +195,10 @@ internal class YieldSupplyModel @Inject constructor(
|
||||||
@Suppress("MaximumLineLength")
|
@Suppress("MaximumLineLength")
|
||||||
private fun onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
private fun onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||||
val yieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
|
val yieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
|
||||||
val tokenProtocolStatus = yieldSupplyRepository.getTokenProtocolStatus(cryptoCurrency)
|
val tokenProtocolStatus = yieldSupplyRepository.getTokenProtocolStatus(
|
||||||
|
userWallet.walletId,
|
||||||
|
cryptoCurrency,
|
||||||
|
)
|
||||||
val isActive = yieldSupplyStatus?.isActive == true
|
val isActive = yieldSupplyStatus?.isActive == true
|
||||||
val isCryptoCurrencyStatusFromCache = cryptoCurrencyStatus.value.sources.networkSource != StatusSource.ACTUAL
|
val isCryptoCurrencyStatusFromCache = cryptoCurrencyStatus.value.sources.networkSource != StatusSource.ACTUAL
|
||||||
val processing = uiState.value is YieldSupplyUM.Processing
|
val processing = uiState.value is YieldSupplyUM.Processing
|
||||||
|
|
@ -298,7 +311,11 @@ internal class YieldSupplyModel @Inject constructor(
|
||||||
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value ?: return
|
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value ?: return
|
||||||
modelScope.launch(dispatchers.default) {
|
modelScope.launch(dispatchers.default) {
|
||||||
if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive == true) {
|
if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive == true) {
|
||||||
yieldSupplyActivateUseCase(token, address).onRight {
|
yieldSupplyActivateUseCase(
|
||||||
|
userWalletId = userWallet.walletId,
|
||||||
|
cryptoCurrency = token,
|
||||||
|
address = address,
|
||||||
|
).onRight {
|
||||||
lastYieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
|
lastYieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
||||||
cryptoCurrencyStatus = cryptoStatus,
|
cryptoCurrencyStatus = cryptoStatus,
|
||||||
appCurrency = appCurrency,
|
appCurrency = appCurrency,
|
||||||
feeValue = currentFee,
|
feeValue = currentFee,
|
||||||
maxNetworkFee = maxFee.second,
|
maxNetworkFee = maxFee,
|
||||||
analyticsHandler = analyticsHandler,
|
analyticsHandler = analyticsHandler,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.ui.format.bigdecimal.format
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
|
import com.tangem.domain.yield.supply.models.YieldSupplyMaxFee
|
||||||
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
|
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
|
||||||
import com.tangem.features.yield.supply.impl.R
|
import com.tangem.features.yield.supply.impl.R
|
||||||
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
|
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
|
||||||
|
|
@ -23,7 +24,7 @@ internal class YieldSupplyActiveFeeContentTransformer(
|
||||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||||
private val appCurrency: AppCurrency,
|
private val appCurrency: AppCurrency,
|
||||||
private val feeValue: BigDecimal,
|
private val feeValue: BigDecimal,
|
||||||
private val maxNetworkFee: BigDecimal,
|
private val maxNetworkFee: YieldSupplyMaxFee,
|
||||||
private val analyticsHandler: AnalyticsEventHandler,
|
private val analyticsHandler: AnalyticsEventHandler,
|
||||||
) : Transformer<YieldSupplyActiveContentUM> {
|
) : Transformer<YieldSupplyActiveContentUM> {
|
||||||
|
|
||||||
|
|
@ -35,9 +36,12 @@ internal class YieldSupplyActiveFeeContentTransformer(
|
||||||
val tokenFiatFee = tokenFiatRate?.let(feeValue::multiply)
|
val tokenFiatFee = tokenFiatRate?.let(feeValue::multiply)
|
||||||
val tokenFiatFeeValueText = tokenFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
|
val tokenFiatFeeValueText = tokenFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||||
|
|
||||||
val maxFeeCryptoValueText = maxNetworkFee.format { crypto(cryptoCurrency) }
|
val maxFeeCryptoValueText = maxNetworkFee.tokenMaxFee.format { crypto(cryptoCurrency) }
|
||||||
val maxFiatFee = tokenFiatRate?.let { rate -> maxNetworkFee.multiply(rate) }
|
val maxFiatFeeValueText = maxNetworkFee.fiatMaxFee.format { fiat(
|
||||||
val maxFiatFeeValueText = maxFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
|
appCurrency.code,
|
||||||
|
appCurrency
|
||||||
|
.symbol,
|
||||||
|
) }
|
||||||
|
|
||||||
val feeNoteValue: TextReference = resourceReference(
|
val feeNoteValue: TextReference = resourceReference(
|
||||||
id = R.string.yield_module_fee_policy_sheet_fee_note,
|
id = R.string.yield_module_fee_policy_sheet_fee_note,
|
||||||
|
|
@ -49,7 +53,7 @@ internal class YieldSupplyActiveFeeContentTransformer(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
val isHighFee = feeValue > maxNetworkFee
|
val isHighFee = feeValue > maxNetworkFee.tokenMaxFee
|
||||||
|
|
||||||
if (isHighFee) {
|
if (isHighFee) {
|
||||||
analyticsHandler.send(
|
analyticsHandler.send(
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,7 @@ internal class YieldSupplyApproveModel @Inject constructor(
|
||||||
maxNetworkFeeFiatValue = TextReference.EMPTY,
|
maxNetworkFeeFiatValue = TextReference.EMPTY,
|
||||||
minTopUpFiatValue = TextReference.EMPTY,
|
minTopUpFiatValue = TextReference.EMPTY,
|
||||||
feeNoteValue = TextReference.EMPTY,
|
feeNoteValue = TextReference.EMPTY,
|
||||||
|
estimatedFiatValue = TextReference.EMPTY,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ internal fun YieldSupplyFeePolicyContent(
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
) {
|
) {
|
||||||
val currentFee = when (yieldSupplyFeeUM) {
|
val currentFee = when (yieldSupplyFeeUM) {
|
||||||
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.tokenFeeFiatValue
|
is YieldSupplyFeeUM.Content -> yieldSupplyFeeUM.estimatedFiatValue
|
||||||
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
|
YieldSupplyFeeUM.Error -> stringReference(StringsSigns.DASH_SIGN)
|
||||||
YieldSupplyFeeUM.Loading -> null
|
YieldSupplyFeeUM.Loading -> null
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +184,7 @@ private fun YieldSupplyFeePolicyContent_Preview() {
|
||||||
stringReference("2.46 USDT"),
|
stringReference("2.46 USDT"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
estimatedFiatValue = stringReference("$8.50"),
|
||||||
),
|
),
|
||||||
tokenSymbol = "USDT",
|
tokenSymbol = "USDT",
|
||||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
||||||
private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase,
|
private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase,
|
||||||
private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase,
|
private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase,
|
||||||
private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase,
|
private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase,
|
||||||
|
private val yieldSupplyGetCurrentFeeUseCase: YieldSupplyGetCurrentFeeUseCase,
|
||||||
private val yieldSupplyRepository: YieldSupplyRepository,
|
private val yieldSupplyRepository: YieldSupplyRepository,
|
||||||
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
|
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
|
||||||
|
|
||||||
|
|
@ -130,10 +131,6 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getMaxFeePair(): Pair<BigDecimal, BigDecimal>? {
|
|
||||||
return yieldSupplyGetMaxFeeUseCase(userWallet, cryptoCurrencyStatus).getOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun onLoadFee() {
|
private suspend fun onLoadFee() {
|
||||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Loading || uiState.value.isTransactionSending) return
|
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Loading || uiState.value.isTransactionSending) return
|
||||||
|
|
||||||
|
|
@ -141,15 +138,13 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
||||||
it.copy(yieldSupplyFeeUM = YieldSupplyFeeUM.Loading)
|
it.copy(yieldSupplyFeeUM = YieldSupplyFeeUM.Loading)
|
||||||
}
|
}
|
||||||
|
|
||||||
val maxFeePair = getMaxFeePair() ?: return
|
val maxFee = yieldSupplyGetMaxFeeUseCase(userWallet, cryptoCurrencyStatus).getOrNull() ?: return
|
||||||
|
val estimatedFee = yieldSupplyGetCurrentFeeUseCase(userWallet, cryptoCurrencyStatus).getOrNull() ?: return
|
||||||
val maxFee = maxFeePair.first
|
|
||||||
val maxFeeFiat = maxFeePair.second
|
|
||||||
|
|
||||||
val transactionListData = yieldSupplyStartEarningUseCase(
|
val transactionListData = yieldSupplyStartEarningUseCase(
|
||||||
userWalletId = userWallet.walletId,
|
userWalletId = userWallet.walletId,
|
||||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||||
maxNetworkFee = maxFee,
|
maxNetworkFee = maxFee.nativeMaxFee,
|
||||||
).getOrNull()
|
).getOrNull()
|
||||||
|
|
||||||
if (transactionListData == null) {
|
if (transactionListData == null) {
|
||||||
|
|
@ -194,7 +189,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
||||||
updatedTransactionList = updatedTransactionList,
|
updatedTransactionList = updatedTransactionList,
|
||||||
feeValue = feeSum,
|
feeValue = feeSum,
|
||||||
maxNetworkFee = maxFee,
|
maxNetworkFee = maxFee,
|
||||||
maxNetworkFeeFiat = maxFeeFiat,
|
estimatedFeeValue = estimatedFee,
|
||||||
minAmount = minAmount,
|
minAmount = minAmount,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -249,35 +244,52 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
ifRight = {
|
ifRight = {
|
||||||
yieldSupplyRepository.saveTokenProtocolStatus(cryptoCurrency, YieldSupplyEnterStatus.Enter)
|
onStartEarningTransactionSuccess(yieldSupplyFeeUM)
|
||||||
val event = AnalyticsParam.TxSentFrom.Earning(
|
|
||||||
blockchain = cryptoCurrency.network.name,
|
|
||||||
token = cryptoCurrency.symbol,
|
|
||||||
feeType = AnalyticsParam.FeeType.Normal,
|
|
||||||
)
|
|
||||||
analytics.send(YieldSupplyAnalytics.FundsEarned)
|
|
||||||
yieldSupplyFeeUM.transactionDataList.forEach {
|
|
||||||
analytics.send(
|
|
||||||
Basic.TransactionSent(
|
|
||||||
sentFrom = event,
|
|
||||||
memoType = Basic.TransactionSent.MemoType.Null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
|
|
||||||
if (address != null) {
|
|
||||||
yieldSupplyActivateUseCase(cryptoCurrency, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
modelScope.launch {
|
|
||||||
params.callback.onTransactionSent()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun onStartEarningTransactionSuccess(yieldSupplyFeeUM: YieldSupplyFeeUM.Content) {
|
||||||
|
yieldSupplyRepository.saveTokenProtocolStatus(
|
||||||
|
userWallet.walletId,
|
||||||
|
cryptoCurrency,
|
||||||
|
YieldSupplyEnterStatus.Enter,
|
||||||
|
)
|
||||||
|
val event = AnalyticsParam.TxSentFrom.Earning(
|
||||||
|
blockchain = cryptoCurrency.network.name,
|
||||||
|
token = cryptoCurrency.symbol,
|
||||||
|
feeType = AnalyticsParam.FeeType.Normal,
|
||||||
|
)
|
||||||
|
analytics.send(
|
||||||
|
YieldSupplyAnalytics.FundsEarned(
|
||||||
|
blockchain = cryptoCurrency.network.name,
|
||||||
|
token = cryptoCurrency.symbol,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
yieldSupplyFeeUM.transactionDataList.forEach {
|
||||||
|
analytics.send(
|
||||||
|
Basic.TransactionSent(
|
||||||
|
sentFrom = event,
|
||||||
|
memoType = Basic.TransactionSent.MemoType.Null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
|
||||||
|
if (address != null) {
|
||||||
|
yieldSupplyActivateUseCase(
|
||||||
|
userWalletId = userWallet.walletId,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
address = address,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
modelScope.launch {
|
||||||
|
params.callback.onTransactionSent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun subscribeOnCurrencyStatusUpdates() {
|
private fun subscribeOnCurrencyStatusUpdates() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
getUserWalletUseCase(params.userWalletId).fold(
|
getUserWalletUseCase(params.userWalletId).fold(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
import com.tangem.core.ui.format.bigdecimal.format
|
import com.tangem.core.ui.format.bigdecimal.format
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
|
import com.tangem.domain.yield.supply.models.YieldSupplyMaxFee
|
||||||
import com.tangem.features.yield.supply.impl.R
|
import com.tangem.features.yield.supply.impl.R
|
||||||
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyActionUM
|
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyActionUM
|
||||||
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyFeeUM
|
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyFeeUM
|
||||||
|
|
@ -24,8 +25,8 @@ internal class YieldSupplyStartEarningFeeContentTransformer(
|
||||||
private val appCurrency: AppCurrency,
|
private val appCurrency: AppCurrency,
|
||||||
private val updatedTransactionList: List<TransactionData.Uncompiled>,
|
private val updatedTransactionList: List<TransactionData.Uncompiled>,
|
||||||
private val feeValue: BigDecimal,
|
private val feeValue: BigDecimal,
|
||||||
private val maxNetworkFee: BigDecimal,
|
private val estimatedFeeValue: BigDecimal,
|
||||||
private val maxNetworkFeeFiat: BigDecimal,
|
private val maxNetworkFee: YieldSupplyMaxFee,
|
||||||
private val minAmount: BigDecimal,
|
private val minAmount: BigDecimal,
|
||||||
) : Transformer<YieldSupplyActionUM> {
|
) : Transformer<YieldSupplyActionUM> {
|
||||||
override fun transform(prevState: YieldSupplyActionUM): YieldSupplyActionUM {
|
override fun transform(prevState: YieldSupplyActionUM): YieldSupplyActionUM {
|
||||||
|
|
@ -36,15 +37,17 @@ internal class YieldSupplyStartEarningFeeContentTransformer(
|
||||||
val feeFiat = feeFiatRate?.let(feeValue::multiply)
|
val feeFiat = feeFiatRate?.let(feeValue::multiply)
|
||||||
val feeFiatValueText = feeFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
|
val feeFiatValueText = feeFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||||
|
|
||||||
|
val estimatedFeeFiat = estimatedFeeValue
|
||||||
|
val estimatedFeeFiatValueText = estimatedFeeFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||||
|
|
||||||
val tokenCryptoFee = tokenFiatRate?.let { rate ->
|
val tokenCryptoFee = tokenFiatRate?.let { rate ->
|
||||||
feeFiat?.divide(rate, cryptoCurrency.decimals, RoundingMode.HALF_UP)
|
estimatedFeeFiat.divide(rate, cryptoCurrency.decimals, RoundingMode.HALF_UP)
|
||||||
}
|
}
|
||||||
val tokenCryptoFeeValueText = tokenCryptoFee.format { crypto(cryptoCurrency) }
|
val tokenCryptoFeeValueText = tokenCryptoFee.format { crypto(cryptoCurrency) }
|
||||||
val tokenFiatFee = feeFiat // same fiat amount
|
val tokenFiatFeeValueText = estimatedFeeFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||||
val tokenFiatFeeValueText = tokenFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
|
|
||||||
|
|
||||||
val maxFeeCryptoValueText = maxNetworkFee.format { crypto(cryptoCurrency) }
|
val maxFeeCryptoValueText = maxNetworkFee.tokenMaxFee.format { crypto(cryptoCurrency) }
|
||||||
val maxFiatFeeValueText = maxNetworkFeeFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
|
val maxFiatFeeValueText = maxNetworkFee.fiatMaxFee.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||||
|
|
||||||
val minAmountCryptoText = minAmount.format { crypto(cryptoCurrency) }
|
val minAmountCryptoText = minAmount.format { crypto(cryptoCurrency) }
|
||||||
val minAmountFiat = tokenFiatRate?.let(minAmount::multiply)
|
val minAmountFiat = tokenFiatRate?.let(minAmount::multiply)
|
||||||
|
|
@ -80,6 +83,7 @@ internal class YieldSupplyStartEarningFeeContentTransformer(
|
||||||
minTopUpFiatValue = stringReference(minAmountFiatText),
|
minTopUpFiatValue = stringReference(minAmountFiatText),
|
||||||
feeNoteValue = feeNoteValue,
|
feeNoteValue = feeNoteValue,
|
||||||
minFeeNoteValue = minFeeNoteValue,
|
minFeeNoteValue = minFeeNoteValue,
|
||||||
|
estimatedFiatValue = stringReference(estimatedFeeFiatValueText),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,37 +165,45 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
ifRight = {
|
ifRight = {
|
||||||
yieldSupplyRepository.saveTokenProtocolStatus(cryptoCurrency, YieldSupplyEnterStatus.Exit)
|
onStopEarningTransactionSuccess()
|
||||||
analytics.send(
|
|
||||||
YieldSupplyAnalytics.FundsWithdrawn(
|
|
||||||
token = cryptoCurrency.symbol,
|
|
||||||
blockchain = cryptoCurrency.network.name,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val event = AnalyticsParam.TxSentFrom.Earning(
|
|
||||||
blockchain = cryptoCurrency.network.name,
|
|
||||||
token = cryptoCurrency.symbol,
|
|
||||||
feeType = AnalyticsParam.FeeType.Normal,
|
|
||||||
)
|
|
||||||
analytics.send(
|
|
||||||
Basic.TransactionSent(
|
|
||||||
sentFrom = event,
|
|
||||||
memoType = Basic.TransactionSent.MemoType.Null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
|
|
||||||
if (address != null) {
|
|
||||||
yieldSupplyDeactivateUseCase(cryptoCurrency, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
modelScope.launch {
|
|
||||||
params.callback.onTransactionSent()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun onStopEarningTransactionSuccess() {
|
||||||
|
yieldSupplyRepository.saveTokenProtocolStatus(
|
||||||
|
userWallet.walletId,
|
||||||
|
cryptoCurrency,
|
||||||
|
YieldSupplyEnterStatus.Exit,
|
||||||
|
)
|
||||||
|
analytics.send(
|
||||||
|
YieldSupplyAnalytics.FundsWithdrawn(
|
||||||
|
token = cryptoCurrency.symbol,
|
||||||
|
blockchain = cryptoCurrency.network.name,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val event = AnalyticsParam.TxSentFrom.Earning(
|
||||||
|
blockchain = cryptoCurrency.network.name,
|
||||||
|
token = cryptoCurrency.symbol,
|
||||||
|
feeType = AnalyticsParam.FeeType.Normal,
|
||||||
|
)
|
||||||
|
analytics.send(
|
||||||
|
Basic.TransactionSent(
|
||||||
|
sentFrom = event,
|
||||||
|
memoType = Basic.TransactionSent.MemoType.Null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
|
||||||
|
if (address != null) {
|
||||||
|
yieldSupplyDeactivateUseCase(cryptoCurrency, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
modelScope.launch {
|
||||||
|
params.callback.onTransactionSent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun subscribeOnCurrencyStatusUpdates() {
|
private fun subscribeOnCurrencyStatusUpdates() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
feeCryptoCurrencyStatusFlow.update {
|
feeCryptoCurrencyStatusFlow.update {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ internal class YieldSupplyStopEarningFeeContentTransformer(
|
||||||
maxNetworkFeeFiatValue = TextReference.EMPTY,
|
maxNetworkFeeFiatValue = TextReference.EMPTY,
|
||||||
minTopUpFiatValue = TextReference.EMPTY,
|
minTopUpFiatValue = TextReference.EMPTY,
|
||||||
feeNoteValue = TextReference.EMPTY,
|
feeNoteValue = TextReference.EMPTY,
|
||||||
|
estimatedFiatValue = TextReference.EMPTY,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue