Updated on 2026-08-14
This commit is contained in:
commit
72a3eb6d7f
6 changed files with 91 additions and 73 deletions
|
|
@ -8,6 +8,7 @@ ENV LC_ALL=en_US.UTF-8
|
|||
ENV LANG=en_US.UTF-8
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
locales \
|
||||
openjdk-17-jdk \
|
||||
wget \
|
||||
unzip \
|
||||
|
|
@ -16,13 +17,15 @@ RUN apt-get update && apt-get install -y \
|
|||
ruby \
|
||||
ruby-dev \
|
||||
build-essential \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& update-locale LANG=en_US.UTF-8 \
|
||||
&& apt-get clean
|
||||
|
||||
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
|
||||
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \
|
||||
apt-get update && apt-get install -y gh && \
|
||||
apt-get clean
|
||||
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \
|
||||
apt-get update && apt-get install -y gh && \
|
||||
apt-get clean
|
||||
|
||||
RUN mkdir -p $ANDROID_HOME/cmdline-tools/latest && \
|
||||
wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O /tmp/cmdline-tools.zip && \
|
||||
|
|
|
|||
|
|
@ -1,19 +1,26 @@
|
|||
FROM ubuntu:24.04
|
||||
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
locales \
|
||||
curl \
|
||||
git \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& update-locale LANG=en_US.UTF-8 \
|
||||
&& apt-get clean
|
||||
|
||||
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
|
||||
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \
|
||||
apt-get update && apt-get install -y gh && \
|
||||
apt-get clean
|
||||
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \
|
||||
apt-get update && apt-get install -y gh && \
|
||||
apt-get clean
|
||||
|
||||
RUN curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash && \
|
||||
apt-get install -y git-lfs && \
|
||||
git lfs install && \
|
||||
apt-get clean
|
||||
apt-get install -y git-lfs && \
|
||||
git lfs install && \
|
||||
apt-get clean
|
||||
|
||||
CMD ["bash"]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import arrow.core.Either
|
|||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.toNonEmptyListOrNull
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -30,20 +29,6 @@ class AddCryptoCurrenciesUseCase(
|
|||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Adds a [currency] to the wallet identified by [userWalletId].
|
||||
*
|
||||
* After successfully adding a currency, it also refreshes the networks for tokens
|
||||
* that are being added and have corresponding coins in the existing currencies list.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param currency Cryptocurrency to add.
|
||||
* @return Either an [Throwable] or [Unit] indicating the success of the operation.
|
||||
*/
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, currency: CryptoCurrency): Either<Throwable, Unit> {
|
||||
return invoke(userWalletId, listOf(currency))
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a [cryptoCurrency] token with specific [network] and derivation to the wallet identified by [userWalletId].
|
||||
*
|
||||
|
|
@ -61,34 +46,32 @@ class AddCryptoCurrenciesUseCase(
|
|||
network: Network,
|
||||
): Either<Throwable, Unit> = either {
|
||||
val tokenToAdd = currenciesRepository.createTokenCurrency(cryptoCurrency = cryptoCurrency, network = network)
|
||||
invoke(userWalletId = userWalletId, currencies = listOf(tokenToAdd))
|
||||
invoke(userWalletId = userWalletId, currency = tokenToAdd)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of [currencies] to the wallet identified by [userWalletId].
|
||||
* Adds a [currency] to the wallet identified by [userWalletId].
|
||||
*
|
||||
* After successfully adding currencies, it also refreshes the networks for tokens
|
||||
* After successfully adding a currency, it also refreshes the networks for tokens
|
||||
* that are being added and have corresponding coins in the existing currencies list.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param currencies The list of cryptocurrencies to add.
|
||||
* @param currency Cryptocurrency to add.
|
||||
* @return Either an [Throwable] or [Unit] indicating the success of the operation.
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
): Either<Throwable, Unit> = either {
|
||||
val existingCurrencies = catch({ currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId) }) {
|
||||
raise(it)
|
||||
}
|
||||
val currenciesToAdd = currencies
|
||||
.filterNot(existingCurrencies::contains)
|
||||
.toNonEmptyListOrNull()
|
||||
?: return@either
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, currency: CryptoCurrency): Either<Throwable, Unit> =
|
||||
either {
|
||||
val existingCurrencies = catch(
|
||||
block = { currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId) },
|
||||
catch = ::raise,
|
||||
)
|
||||
val currencyToAdd = currency.takeUnless(existingCurrencies::contains) ?: return@either
|
||||
|
||||
addCurrencies(userWalletId, currenciesToAdd)
|
||||
refreshUpdatedNetworks(userWalletId, currenciesToAdd, existingCurrencies)
|
||||
}
|
||||
addCurrencies(userWalletId, currencyToAdd)
|
||||
refreshUpdatedNetworks(userWalletId, currencyToAdd, existingCurrencies)
|
||||
refreshUpdatedYieldBalances(userWalletId, currencyToAdd)
|
||||
refreshUpdatedQuotes(currencyToAdd)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -110,10 +93,10 @@ class AddCryptoCurrenciesUseCase(
|
|||
return@either foundToken
|
||||
}
|
||||
val tokenToAdd = createTokenCurrency(userWalletId, contractAddress, networkId)
|
||||
addCurrencies(userWalletId, listOf(tokenToAdd))
|
||||
refreshUpdatedNetworks(userWalletId, listOf(tokenToAdd), existingCurrencies)
|
||||
refreshUpdatedYieldBalances(userWalletId, existingCurrencies)
|
||||
refreshUpdatedQuotes(existingCurrencies)
|
||||
addCurrencies(userWalletId, tokenToAdd)
|
||||
refreshUpdatedNetworks(userWalletId, tokenToAdd, existingCurrencies)
|
||||
refreshUpdatedYieldBalances(userWalletId, tokenToAdd)
|
||||
refreshUpdatedQuotes(tokenToAdd)
|
||||
tokenToAdd
|
||||
}
|
||||
|
||||
|
|
@ -123,23 +106,24 @@ class AddCryptoCurrenciesUseCase(
|
|||
*/
|
||||
private suspend fun Raise<Throwable>.refreshUpdatedNetworks(
|
||||
userWalletId: UserWalletId,
|
||||
currenciesToAdd: List<CryptoCurrency>,
|
||||
currencyToAdd: CryptoCurrency,
|
||||
existingCurrencies: List<CryptoCurrency>,
|
||||
) {
|
||||
val networksToUpdate = currenciesToAdd
|
||||
.asSequence()
|
||||
.filterIsInstance<CryptoCurrency.Token>()
|
||||
.filter { hasCoinForToken(existingCurrencies, it) }
|
||||
.mapTo(hashSetOf(), CryptoCurrency.Token::network)
|
||||
val networksToUpdate = currencyToAdd.takeIf { currency ->
|
||||
currency is CryptoCurrency.Token && hasCoinForToken(existingCurrencies, currency)
|
||||
}
|
||||
?.network
|
||||
|
||||
val networkToUpdate = currenciesToAdd.map { it.network }
|
||||
.subtract(existingCurrencies.map { it.network }.toSet())
|
||||
val networkToUpdate = currencyToAdd.takeIf {
|
||||
!existingCurrencies.map(CryptoCurrency::network).contains(it.network)
|
||||
}
|
||||
?.network
|
||||
|
||||
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
|
||||
multiNetworkStatusFetcher(
|
||||
MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = networksToUpdate + networkToUpdate,
|
||||
networks = setOfNotNull(networksToUpdate, networkToUpdate),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -147,7 +131,7 @@ class AddCryptoCurrenciesUseCase(
|
|||
{
|
||||
networksRepository.getNetworkStatusesSync(
|
||||
userWalletId = userWalletId,
|
||||
networks = networksToUpdate + networkToUpdate,
|
||||
networks = setOfNotNull(networksToUpdate, networkToUpdate),
|
||||
refresh = true,
|
||||
)
|
||||
},
|
||||
|
|
@ -157,20 +141,17 @@ class AddCryptoCurrenciesUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshUpdatedYieldBalances(
|
||||
userWalletId: UserWalletId,
|
||||
existingCurrencies: List<CryptoCurrency>,
|
||||
) {
|
||||
stakingRepository.fetchMultiYieldBalance(
|
||||
private suspend fun refreshUpdatedYieldBalances(userWalletId: UserWalletId, addedCurrency: CryptoCurrency) {
|
||||
stakingRepository.fetchSingleYieldBalance(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = existingCurrencies,
|
||||
cryptoCurrency = addedCurrency,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
|
||||
private suspend fun refreshUpdatedQuotes(currencyToAdd: CryptoCurrency) {
|
||||
quotesRepository.fetchQuotes(
|
||||
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
|
||||
currenciesIds = setOfNotNull(currencyToAdd.id.rawCurrencyId),
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -194,9 +175,9 @@ class AddCryptoCurrenciesUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun Raise<Throwable>.addCurrencies(userWalletId: UserWalletId, tokens: List<CryptoCurrency>) {
|
||||
private suspend fun Raise<Throwable>.addCurrencies(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||
catch(
|
||||
{ currenciesRepository.addCurrencies(userWalletId, tokens) },
|
||||
{ currenciesRepository.addCurrencies(userWalletId, listOf(currency)) },
|
||||
) {
|
||||
raise(it)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.DelicateDecomposeApi
|
||||
import com.arkivanov.decompose.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
|
|
@ -13,6 +14,7 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent
|
||||
import com.tangem.features.managetokens.component.AddCustomTokenComponent
|
||||
import com.tangem.features.managetokens.component.CustomTokenFormComponent
|
||||
|
|
@ -171,6 +173,7 @@ internal class DefaultAddCustomTokenComponent @AssistedInject constructor(
|
|||
showForm(derivationPath = derivationPath)
|
||||
}
|
||||
|
||||
@OptIn(DelicateDecomposeApi::class)
|
||||
private fun showDerivationPathSelector(formValues: CustomTokenFormValues) {
|
||||
val currentConfig = contentStack.value.active.configuration
|
||||
|
||||
|
|
@ -181,6 +184,7 @@ internal class DefaultAddCustomTokenComponent @AssistedInject constructor(
|
|||
navigation.push(config)
|
||||
}
|
||||
|
||||
@OptIn(DelicateDecomposeApi::class)
|
||||
private fun showNetworkSelector(formValues: CustomTokenFormValues) {
|
||||
val currentConfig = contentStack.value.active.configuration
|
||||
|
||||
|
|
@ -194,14 +198,35 @@ internal class DefaultAddCustomTokenComponent @AssistedInject constructor(
|
|||
private fun showForm(network: SelectedNetwork? = null, derivationPath: SelectedDerivationPath? = null) {
|
||||
val currentConfig = contentStack.value.active.configuration
|
||||
|
||||
val selectedNetwork = network ?: currentConfig.selectedNetwork
|
||||
val selectedDerivationPath = derivationPath ?: currentConfig.selectedDerivationPath
|
||||
|
||||
val config = currentConfig.copy(
|
||||
step = AddCustomTokenConfig.Step.FORM,
|
||||
selectedNetwork = network ?: currentConfig.selectedNetwork,
|
||||
selectedDerivationPath = derivationPath ?: currentConfig.selectedDerivationPath,
|
||||
selectedNetwork = selectedNetwork,
|
||||
selectedDerivationPath = defineSelectedDerivationPath(selectedNetwork, selectedDerivationPath),
|
||||
)
|
||||
|
||||
navigation.replaceAll(config)
|
||||
}
|
||||
|
||||
private fun defineSelectedDerivationPath(
|
||||
selectedNetwork: SelectedNetwork?,
|
||||
selectedDerivationPath: SelectedDerivationPath?,
|
||||
): SelectedDerivationPath? {
|
||||
// if default derivation path
|
||||
if (selectedDerivationPath?.value == selectedNetwork?.derivationPath) return null
|
||||
|
||||
// map to custom derivation path
|
||||
return if (selectedDerivationPath != null && selectedDerivationPath.value.value != null) {
|
||||
selectedDerivationPath.copy(
|
||||
value = Network.DerivationPath.Custom(value = requireNotNull(selectedDerivationPath.value.value)),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun dismissAndNotify() {
|
||||
dismiss()
|
||||
params.onCurrencyAdded()
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ internal class OnrampAddToPortfolioModel @Inject constructor(
|
|||
|
||||
addCryptoCurrenciesUseCase(
|
||||
userWalletId = params.userWalletId,
|
||||
currencies = listOfNotNull(params.cryptoCurrency),
|
||||
currency = params.cryptoCurrency,
|
||||
)
|
||||
.onRight { params.onSuccessAdding(params.cryptoCurrency.id) }
|
||||
.onLeft { changeAddButtonProgressStatus(isProgress = false) }
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ internal class ReferralInteractorImpl(
|
|||
}
|
||||
|
||||
val cryptoCurrency = repository.getCryptoCurrency(userWalletId = userWallet.walletId, tokenData = tokenData)
|
||||
?: error("Failed to create crypto currency")
|
||||
|
||||
derivePublicKeysUseCase(userWallet.walletId, listOfNotNull(cryptoCurrency)).getOrElse {
|
||||
Timber.e("Failed to derive public keys: $it")
|
||||
throw it.mapToDomainError()
|
||||
|
|
@ -47,12 +49,12 @@ internal class ReferralInteractorImpl(
|
|||
|
||||
addCryptoCurrenciesUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currencies = listOfNotNull(cryptoCurrency),
|
||||
currency = cryptoCurrency,
|
||||
)
|
||||
|
||||
val publicAddress = userWalletManager.getWalletAddress(
|
||||
networkId = tokenData.networkId,
|
||||
derivationPath = cryptoCurrency?.network?.derivationPath?.value,
|
||||
derivationPath = cryptoCurrency.network.derivationPath.value,
|
||||
)
|
||||
|
||||
return repository.startReferral(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue