Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-14 15:45:21 +04:00
parent 5f619a56fe
commit 9a0fbcbe1b
22 changed files with 324 additions and 254 deletions

View file

@ -2,21 +2,12 @@ package com.tangem.features.commonfeatures.api.addtoportfolio
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.currency.CryptoCurrency
interface AddToPortfolioComponent : ComposableBottomSheetComponent {
data class Params(
val addToPortfolioManager: AddToPortfolioManager,
val callback: Callback,
val shouldSkipTokenActionsScreen: Boolean = false,
)
interface Callback {
fun onDismiss()
// todo swap add new onSuccess with full data of added token
fun onSuccess(addedToken: CryptoCurrency)
}
interface Factory : ComponentFactory<Params, AddToPortfolioComponent>
}

View file

@ -2,43 +2,90 @@ package com.tangem.features.commonfeatures.api.addtoportfolio
import com.tangem.domain.markets.TokenMarketInfo
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.account.PortfolioFetcher
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.AnalyticsParams
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable
interface AddToPortfolioManager {
interface AddToPortfolioManager : AddToPortfolioManagerInternal {
val onDismiss: Channel<Unit>
val onSuccessAdded: Channel<Result>
// todo swap make updatable
val token: TokenMarketParams
val analyticsParams: AnalyticsParams?
val portfolioFetcher: PortfolioFetcher
val state: StateFlow<State>
val allAvailableNetworks: Flow<List<TokenMarketInfo.Network>>
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>)
fun setTokenParams(token: TokenMarketParams)
sealed interface State {
data object Init : State
data class AvailableToAdd(
data object Loading : State
data class Ready(
val availableToAddData: AvailableToAddData,
) : State
data object NothingToAdd : State
) : State {
val isAvailableToAdd: Boolean get() = availableToAddData.isAvailableToAdd
val isSinglePortfolio: Boolean get() = availableToAddData.isSinglePortfolio
}
}
@Serializable
data class AnalyticsParams(
val source: String,
val source: String?,
)
interface Factory {
fun create(
scope: CoroutineScope,
token: TokenMarketParams,
analyticsParams: AnalyticsParams?,
): AddToPortfolioManager
fun create(scope: CoroutineScope, settings: Settings, analyticsParams: AnalyticsParams): AddToPortfolioManager
}
/**
* Immutable settings
*/
data class Settings(
val shouldSkipTokenActionsScreen: Boolean = false,
) {
companion object {
val DefaultMarket = Settings(
shouldSkipTokenActionsScreen = false,
)
val ChooseToken = Settings(
shouldSkipTokenActionsScreen = true,
)
}
}
/**
* Mutable parameters
* Updates may trigger reload [State]
*/
data class Params(
val networks: List<TokenMarketInfo.Network>,
val token: TokenMarketParams,
)
data class Result(
val wallet: UserWallet,
val account: AccountStatus.CryptoPortfolio,
val addedCurrency: CryptoCurrencyStatus,
)
}
/**
* primary for internal impl usage, but you can also use it externally
*/
interface AddToPortfolioManagerInternal {
val paramsFlow: SharedFlow<AddToPortfolioManager.Params>
val settings: AddToPortfolioManager.Settings
val analyticsParams: AnalyticsParams
suspend fun token(): TokenMarketParams = paramsFlow.first().token
fun onDismiss()
fun onSuccessAdded(result: AddToPortfolioManager.Result)
}

View file

@ -36,7 +36,7 @@ internal class AddTokenComponent @AssistedInject constructor(
}
data class Params(
val eventBuilder: PortfolioAnalyticsEvent.EventBuilder,
val eventBuilder: Flow<PortfolioAnalyticsEvent.EventBuilder>,
val selectedPortfolio: Flow<SelectedPortfolio>,
val selectedNetwork: Flow<SelectedNetwork>,
val callbacks: Callbacks,

View file

@ -71,7 +71,7 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
}
override fun dismiss() {
params.callback.onDismiss()
model.addToPortfolioManager.onDismiss()
}
@Composable

View file

@ -10,12 +10,13 @@ import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.account.PortfolioSelectorComponent
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioPreselectedDataModel
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioRoutes
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.flowOf
internal class DefaultAddToPortfolioPreselectedDataComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@ -37,7 +38,7 @@ internal class DefaultAddToPortfolioPreselectedDataComponent @AssistedInject con
private val addTokenComponent: AddTokenComponent = addTokenComponentFactory.create(
context = child("addTokenComponent"),
params = AddTokenComponent.Params(
eventBuilder = model.eventBuilder,
eventBuilder = flowOf(model.eventBuilder),
callbacks = model,
selectedPortfolio = model.selectedPortfolio,
selectedNetwork = model.selectedNetwork,

View file

@ -63,7 +63,7 @@ internal class TokenActionsComponent @AssistedInject constructor(
)
data class Params(
val eventBuilder: PortfolioAnalyticsEvent.EventBuilder,
val eventBuilder: Flow<PortfolioAnalyticsEvent.EventBuilder>,
val data: Flow<CryptoCurrencyData>,
val callbacks: Callbacks,
)

View file

@ -19,6 +19,7 @@ import com.tangem.domain.markets.TokenMarketInfo
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.account.PortfolioFetcher
import com.tangem.features.account.PortfolioSelectorController
import com.tangem.features.commonfeatures.api.addtoportfolio.*
import com.tangem.features.commonfeatures.impl.R
@ -63,12 +64,9 @@ internal class AddToPortfolioModel @Inject constructor(
val selectedPortfolio: MutableSharedFlow<SelectedPortfolio> = replayMutableSharedFlow()
val tokenActionsData: MutableSharedFlow<CryptoCurrencyData> = replayMutableSharedFlow()
private val addToPortfolioManager = params.addToPortfolioManager
val portfolioFetcher = addToPortfolioManager.portfolioFetcher
val eventBuilder = PortfolioAnalyticsEvent.EventBuilder(
tokenSymbol = addToPortfolioManager.token.symbol,
source = addToPortfolioManager.analyticsParams?.source,
)
val addToPortfolioManager: AddToPortfolioManager = params.addToPortfolioManager
val portfolioFetcher: PortfolioFetcher = addToPortfolioManager.portfolioFetcher
val eventBuilder: MutableSharedFlow<PortfolioAnalyticsEvent.EventBuilder> = replayMutableSharedFlow()
val featureData: Flow<AddToPortfolioManager.State> = combineFeatureData()
@ -85,32 +83,27 @@ internal class AddToPortfolioModel @Inject constructor(
@Suppress("LongMethod")
private fun startAddToPortfolioFlow() {
channelFlow<Unit> {
fun finishFlow() {
params.callback.onDismiss()
channel.close()
}
fun finishSuccessFlow(addedToken: CryptoCurrencyStatus) {
params.callback.onSuccess(addedToken.currency)
fun finishSuccessFlow(result: AddToPortfolioManager.Result) {
addToPortfolioManager.onSuccessAdded(result)
channel.close()
}
val featureDataFlow: StateFlow<AvailableToAddData> = featureData
.filterIsInstance<AddToPortfolioManager.State.AvailableToAdd>()
.filterIsInstance<AddToPortfolioManager.State.Ready>()
.map { it.availableToAddData }
.distinctUntilChanged()
.stateIn(this)
val isAccountMode = portfolioSelectorController.isAccountModeSync()
val tokenMarketParams = addToPortfolioManager.paramsFlow.first().token
val eb = PortfolioAnalyticsEvent.EventBuilder(
tokenSymbol = tokenMarketParams.symbol,
source = addToPortfolioManager.analyticsParams.source,
)
eventBuilder.tryEmit(eb)
// use snapshot data, looks like we dont need to remap at runtime
val data = featureDataFlow.value
// you must control it via [AddToPortfolioManager.state]
if (!data.isAvailableToAdd) {
finishFlow()
return@channelFlow
}
// init data flows, emits on user/code selection, updates state holder
val firstSelectedPortfolio = setupPortfolioFlow(data)
.onEach { selectedPortfolio.emit(it) }
@ -157,7 +150,7 @@ internal class AddToPortfolioModel @Inject constructor(
// line of navigation to AddToken screen is finished; cancel the job, select a new root screen
firstPartOfNavigation.cancel()
analyticsEventHandler.send(event = eventBuilder.popupToConfirm())
analyticsEventHandler.send(event = eventBuilder.first().popupToConfirm())
navigation.replaceAll(AddToPortfolioRoutes.AddToken)
var middleNavigationJob: Job? = null
@ -185,34 +178,39 @@ internal class AddToPortfolioModel @Inject constructor(
val addedToken = callbackDelegate.onTokenAdded.receiveAsFlow().first()
middleNavigationJob?.cancel()
val selectedPortfolio = selectedPortfolio.first()
val result = AddToPortfolioManager.Result(
wallet = selectedPortfolio.userWallet,
account = selectedPortfolio.account.account,
addedCurrency = addedToken,
)
messageSender.send(ToastMessage(message = resourceReference(R.string.markets_token_added)))
if (params.shouldSkipTokenActionsScreen) {
finishSuccessFlow(addedToken)
if (addToPortfolioManager.settings.shouldSkipTokenActionsScreen) {
finishSuccessFlow(result)
} else {
setupTokenActionsFlow(selectedPortfolio, addedToken)
.onEach { cryptoCurrencyData ->
tokenActionsData.emit(cryptoCurrencyData)
navigation.replaceAll(AddToPortfolioRoutes.TokenActions)
}
.onEmpty { finishFlow() }
.onEmpty { finishSuccessFlow(result) }
.launchIn(this)
}
callbackDelegate.onLaterClick.receiveAsFlow().first()
finishFlow()
finishSuccessFlow(result)
}
.catch { throwable ->
TangemLogger.e("Error", throwable)
params.callback.onDismiss()
addToPortfolioManager.onDismiss()
}
.launchIn(modelScope)
}
private fun logAccountSelector(isAccountMode: Boolean) {
private suspend fun logAccountSelector(isAccountMode: Boolean) {
if (isAccountMode) {
analyticsEventHandler.send(eventBuilder.popupToChooseAccount())
analyticsEventHandler.send(eventBuilder.first().popupToChooseAccount())
}
}
@ -291,7 +289,7 @@ internal class AddToPortfolioModel @Inject constructor(
data.availableToAddWallets[selectedAccountId.userWalletId] ?: return@combine null
val availableToAddAccount =
availableToAddWallets.availableToAddAccounts[selectedAccountId] ?: return@combine null
if (!isAccountMode) analyticsEventHandler.send(eventBuilder.addToPortfolioWalletChanged())
if (!isAccountMode) analyticsEventHandler.send(eventBuilder.first().addToPortfolioWalletChanged())
SelectedPortfolio(
isAccountMode = isAccountMode,
userWallet = availableToAddWallets.userWallet,
@ -327,7 +325,7 @@ internal class AddToPortfolioModel @Inject constructor(
val accountIndex = account.account.account.derivationIndex
return getTokenMarketCryptoCurrency(
userWalletId = userWallet.walletId,
tokenMarketParams = addToPortfolioManager.token,
tokenMarketParams = addToPortfolioManager.token(),
network = network,
accountIndex = accountIndex,
)
@ -339,7 +337,7 @@ internal class AddToPortfolioModel @Inject constructor(
private fun combineFeatureData() = addToPortfolioManager.state.onEach { state ->
when (state) {
is AddToPortfolioManager.State.AvailableToAdd ->
is AddToPortfolioManager.State.Ready ->
portfolioSelectorController.isEnabled.value = isEnabled@{ userWallet, accountStatus ->
val availableWallet = state.availableToAddData.availableToAddWallets[userWallet.walletId]
?: return@isEnabled false
@ -348,8 +346,7 @@ internal class AddToPortfolioModel @Inject constructor(
?.isAvailableToAdd == true
return@isEnabled isAvailableAccount
}
AddToPortfolioManager.State.Init,
AddToPortfolioManager.State.NothingToAdd,
AddToPortfolioManager.State.Loading,
-> Unit
}
}

View file

@ -12,11 +12,11 @@ import com.tangem.core.ui.message.ToastMessage
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenUiBuilder.Companion.toggleProgress
import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedNetwork
import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedPortfolio
import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenUiBuilder.Companion.toggleProgress
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
@ -40,7 +40,6 @@ internal class AddTokenModel @Inject constructor(
) : Model() {
private val params = paramsContainer.require<AddTokenComponent.Params>()
private val analyticsEventBuilder = params.eventBuilder
private val addTokenJob = JobHolder()
val uiState: StateFlow<AddTokenUM?>
@ -86,10 +85,11 @@ internal class AddTokenModel @Inject constructor(
uiState.value = um.toggleProgress(true)
val blockchainNames = listOf(selectedNetwork.selectedNetwork)
.mapNotNull { BlockchainUtils.getNetworkInfo(it.networkId)?.name }
val analyticsEventBuilder = params.eventBuilder.first()
analyticsEventHandler.send(analyticsEventBuilder.addToPortfolioContinue(blockchainNames))
analyticsEventHandler.send(analyticsEventBuilder.addButtonClick())
manageCryptoCurrenciesUseCase(accountId = accountId, add = cryptoCurrency)
manageCryptoCurrenciesUseCase.invokeAndAwait(accountId = accountId, add = listOf(cryptoCurrency))
.onLeft { throwable ->
processError(error = throwable)
uiState.value = um.toggleProgress(false)
@ -100,7 +100,8 @@ internal class AddTokenModel @Inject constructor(
userWalletId = accountId.userWalletId,
currencyId = cryptoCurrency.id,
network = cryptoCurrency.network,
).firstOrNull()
)
.firstOrNull()
if (status == null) {
processError(error = null)
} else {

View file

@ -18,6 +18,7 @@ import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@ -52,18 +53,18 @@ internal class TokenActionsModel @Inject constructor(
val bottomSheetNavigation: SlotNavigation<TokenReceiveConfig> = SlotNavigation()
val uiState: StateFlow<TokenActionsUM?> = params.data
.mapLatest { uiBuilder.build(it, tokenActionsHandler) }
.mapLatest { uiBuilder.build(it, tokenActionsHandler, analyticsEventBuilder.first()) }
.stateIn(
scope = modelScope,
started = SharingStarted.Eagerly,
initialValue = null,
)
private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction) {
val event = analyticsEventBuilder.getTokenActionClick(actionUM = handledAction.action)
private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction) = modelScope.launch {
val event = analyticsEventBuilder.first().getTokenActionClick(actionUM = handledAction.action)
analyticsEventHandler.send(event)
val isReceive = handledAction.action == TokenActionsBSContentUM.Action.Receive
if (!isReceive) return
if (!isReceive) return@launch
modelScope.launch {
val tokenConfig = receiveAddressesFactory.create(
status = handledAction.cryptoCurrencyData.status,

View file

@ -10,6 +10,7 @@ import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIco
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.stringReference
import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.PortfolioAnalyticsEvent
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM
import javax.inject.Inject
@ -20,7 +21,11 @@ internal class TokenActionsUiBuilder @Inject constructor(
) {
private val params = paramsContainer.require<TokenActionsComponent.Params>()
fun build(data: CryptoCurrencyData, tokenActionsHandler: TokenActionsHandler): TokenActionsUM {
fun build(
data: CryptoCurrencyData,
tokenActionsHandler: TokenActionsHandler,
eventBuilder: PortfolioAnalyticsEvent.EventBuilder,
): TokenActionsUM {
val status = data.status
val tokenUM = TokenItemState.Content(
id = status.currency.id.value,
@ -35,7 +40,7 @@ internal class TokenActionsUiBuilder @Inject constructor(
return TokenActionsUM(
token = tokenUM,
onLaterClick = {
analyticsEventHandler.send(params.eventBuilder.getTokenLater())
analyticsEventHandler.send(eventBuilder.getTokenLater())
params.callbacks.onLaterClick()
},
quickActions = quickActions(data, tokenActionsHandler),

View file

@ -3,69 +3,109 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.ui
import com.tangem.domain.markets.TokenMarketInfo
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.account.PortfolioFetcher
import com.tangem.features.commonfeatures.impl.addtoportfolio.converter.AvailableToAddDataConverter
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.AnalyticsParams
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.Settings
import com.tangem.features.commonfeatures.impl.addtoportfolio.converter.AvailableToAddDataConverter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
internal class DefaultAddToPortfolioManager @AssistedInject constructor(
private val availableToAddDataConverter: AvailableToAddDataConverter,
@Assisted override val token: TokenMarketParams,
@Assisted override val analyticsParams: AddToPortfolioManager.AnalyticsParams?,
@Assisted override val settings: Settings,
@Assisted override val analyticsParams: AnalyticsParams,
@Assisted val scope: CoroutineScope,
dispatchers: CoroutineDispatcherProvider,
portfolioFetcherFactory: PortfolioFetcher.Factory,
) : AddToPortfolioManager {
private val _allAvailableNetworks = MutableSharedFlow<List<TokenMarketInfo.Network>>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
override val onDismiss: Channel<Unit> = Channel()
override val onSuccessAdded: Channel<AddToPortfolioManager.Result> = Channel()
override val allAvailableNetworks: Flow<List<TokenMarketInfo.Network>> = _allAvailableNetworks.asSharedFlow()
override val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create(
mode = PortfolioFetcher.Mode.All(isOnlyMultiCurrency = true),
scope = scope,
)
override val state: StateFlow<AddToPortfolioManager.State> =
combine(
flow = portfolioFetcher.data.map { it.balances }.distinctUntilChanged(),
flow2 = allAvailableNetworks.map { it.toSet() }.distinctUntilChanged(),
) { balances, availableNetworks ->
val data = availableToAddDataConverter.convert(
balances = balances,
availableNetworks = availableNetworks,
marketParams = token,
private val internalParamsFlow = MutableStateFlow(ParamsInternal())
override val paramsFlow = internalParamsFlow
.transform { internalParams ->
val fullParams = AddToPortfolioManager.Params(
networks = internalParams.networks ?: return@transform,
token = internalParams.token ?: return@transform,
)
if (data.isAvailableToAdd) {
AddToPortfolioManager.State.AvailableToAdd(data)
} else {
AddToPortfolioManager.State.NothingToAdd
}
emit(fullParams)
}
.distinctUntilChanged()
.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
override val state: MutableStateFlow<AddToPortfolioManager.State> =
MutableStateFlow(AddToPortfolioManager.State.Loading)
init {
buildFlow()
.onEach { newState -> state.update { newState } }
.flowOn(dispatchers.default)
.stateIn(
scope = scope,
started = SharingStarted.Eagerly,
initialValue = AddToPortfolioManager.State.Init,
)
.launchIn(scope)
}
override fun onDismiss() {
onDismiss.trySend(Unit)
}
override fun onSuccessAdded(result: AddToPortfolioManager.Result) {
onSuccessAdded.trySend(result)
}
override fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
_allAvailableNetworks.tryEmit(networks)
updateInternal(networks = networks)
}
override fun setTokenParams(token: TokenMarketParams) {
updateInternal(token = token)
}
private fun updateInternal(networks: List<TokenMarketInfo.Network>? = null, token: TokenMarketParams? = null) {
internalParamsFlow.update { prev ->
val newParams = ParamsInternal(
networks = networks ?: prev.networks,
token = token ?: prev.token,
)
val shouldReload = prev.networks != newParams.networks || prev.token != newParams.token
if (shouldReload) state.update { AddToPortfolioManager.State.Loading }
return@update newParams
}
}
private fun buildFlow(): Flow<AddToPortfolioManager.State> = combine(
flow = portfolioFetcher.data.map { it.balances }.distinctUntilChanged(),
flow2 = paramsFlow,
) { balances, (availableNetworks, token) ->
val data = availableToAddDataConverter.convert(
balances = balances,
availableNetworks = availableNetworks.toSet(),
marketParams = token,
)
AddToPortfolioManager.State.Ready(data)
}
@AssistedFactory
interface Factory : AddToPortfolioManager.Factory {
override fun create(
scope: CoroutineScope,
token: TokenMarketParams,
analyticsParams: AddToPortfolioManager.AnalyticsParams?,
settings: Settings,
analyticsParams: AnalyticsParams,
): DefaultAddToPortfolioManager
}
private data class ParamsInternal(
val networks: List<TokenMarketInfo.Network>? = null,
val token: TokenMarketParams? = null,
)
}