Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-31 16:58:05 +03:00
parent dc4e8c740b
commit fefe37a890
23 changed files with 285 additions and 140 deletions

View file

@ -190,6 +190,7 @@ class TapApplication : Application(), ImageLoaderFactory {
scanCardProcessor = scanCardProcessor,
appCurrencyRepository = appCurrencyRepository,
walletManagersFacade = walletManagersFacade,
appStateHolder = appStateHolder,
),
),
)

View file

@ -131,6 +131,8 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
sellService = makeSellExchangeService(config),
primaryRules = CardExchangeRules(cardProvider),
)
// TODO: for refactoring (after remove old design refactor CurrencyExchangeManager and use 1 instance)
store.state.daggerGraphState.get(DaggerGraphState::appStateHolder).exchangeService = exchangeManager
store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager))
store.dispatchOnMain(GlobalAction.ExchangeManager.Update)
}

View file

@ -3,8 +3,11 @@ package com.tangem.tap.di
import android.content.Context
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.exchange.RampStateManager
import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
import com.tangem.tap.network.exchangeServices.DefaultRampManager
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.userTokensRepository
import dagger.Module
import dagger.Provides
@ -40,4 +43,10 @@ internal object ActivityModule {
),
)
}
@Provides
@Singleton
fun provideDefaultRampManager(appStateHolder: AppStateHolder): RampStateManager {
return DefaultRampManager(appStateHolder.exchangeService)
}
}

View file

@ -1,7 +1,9 @@
package com.tangem.tap.di.domain
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -108,8 +110,10 @@ internal object TokensDomainModule {
@Provides
@ViewModelScoped
fun provideGetCryptoCurrencyActionsUseCase(
rampStateManager: RampStateManager,
marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository,
dispatchers: CoroutineDispatcherProvider,
): GetCryptoCurrencyActionsUseCase {
return GetCryptoCurrencyActionsUseCase(dispatchers)
return GetCryptoCurrencyActionsUseCase(rampStateManager, marketCryptoCurrencyRepository, dispatchers)
}
}

View file

@ -1,105 +0,0 @@
package com.tangem.tap.domain.tokens
import com.squareup.moshi.JsonAdapter
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.services.Result
import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
import com.tangem.datasource.asset.AssetReader
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
class LoadAvailableCoinsService(
private val tangemTechApi: TangemTechApi,
private val dispatchers: CoroutineDispatcherProvider,
private val assetReader: AssetReader,
) {
private val currenciesAdapter: JsonAdapter<CurrenciesFromJson> =
MoshiConverter.networkMoshi.adapter(CurrenciesFromJson::class.java)
suspend fun getSupportedTokens(
isTestNet: Boolean,
supportedBlockchains: List<Blockchain>,
page: Int,
searchInput: String?,
): Result<LoadedCoins> {
if (isTestNet) {
return Result.Success(
LoadedCoins(
currencies = getTestnetCoins().filter(searchInput),
moreAvailable = false,
),
)
}
val offset = page * LOAD_PER_PAGE
return when (val result = loadCoins(supportedBlockchains, offset, searchInput)) {
is Result.Success -> {
val data = result.data
Result.Success(
LoadedCoins(
currencies = data.coins.map {
Currency.fromCoinResponse(currency = it, imageHost = data.imageHost)
},
moreAvailable = data.total > offset + LOAD_PER_PAGE,
),
)
}
is Result.Failure -> {
Result.Failure(result.error)
}
}
}
private suspend fun loadCoins(
supportedBlockchains: List<Blockchain>,
offset: Int,
searchInput: String?,
): Result<CoinsResponse> {
return withContext(dispatchers.io) {
runCatching {
tangemTechApi.getCoins(
networkIds = supportedBlockchains.joinToString(
separator = ",",
transform = Blockchain::toNetworkId,
),
active = true,
searchText = searchInput,
offset = offset,
limit = LOAD_PER_PAGE,
)
}.fold(
onSuccess = { Result.Success(it) },
onFailure = { Result.Failure(it) },
)
}
}
private fun getTestnetCoins(): List<Currency> {
val json = assetReader.readJson(FILE_NAME_TESTNET_COINS)
return currenciesAdapter.fromJson(json)!!.coins
.map { Currency.fromJsonObject(it) }
}
private fun List<Currency>.filter(searchInput: String?): List<Currency> {
if (searchInput.isNullOrBlank()) return this
return filter { currency ->
currency.symbol.contains(searchInput, ignoreCase = true) ||
currency.name.contains(searchInput, ignoreCase = true)
}
}
private companion object {
const val LOAD_PER_PAGE = 100
const val FILE_NAME_TESTNET_COINS = "testnet_tokens"
}
}
data class LoadedCoins(
val currencies: List<Currency>,
val moreAvailable: Boolean,
)

View file

@ -1,13 +1,15 @@
package com.tangem.tap.features.wallet.converters
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.store
import com.tangem.utils.converter.Converter
import com.tangem.utils.converter.TwoWayConverter
internal class CryptoCurrencyConverter : Converter<Currency, CryptoCurrency> {
internal class CryptoCurrencyConverter : TwoWayConverter<Currency, CryptoCurrency> {
private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory() }
@ -40,4 +42,26 @@ internal class CryptoCurrencyConverter : Converter<Currency, CryptoCurrency> {
)
}
}
override fun convertBack(value: CryptoCurrency): Currency {
val blockchain = Blockchain.fromId(value.network.id.value)
if (blockchain == Blockchain.Unknown) error("CryptoCurrencyConverter convertBack Unknown blockchain")
return when (value) {
is CryptoCurrency.Coin -> Currency.Blockchain(
blockchain = blockchain,
derivationPath = value.derivationPath,
)
is CryptoCurrency.Token -> Currency.Token(
token = Token(
name = value.name,
symbol = value.symbol,
contractAddress = value.contractAddress,
decimals = value.decimals,
id = value.id.value,
),
blockchain = blockchain,
derivationPath = value.derivationPath,
)
}
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.tap.network.exchangeServices
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.tap.features.wallet.converters.CryptoCurrencyConverter
class DefaultRampManager(private val exchangeService: ExchangeService?) : RampStateManager {
private val cryptoCurrencyConverter = CryptoCurrencyConverter()
override fun availableForBuy(cryptoCurrency: CryptoCurrency): Boolean {
return exchangeService?.availableForBuy(
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
) ?: false
}
override fun availableForSell(cryptoCurrency: CryptoCurrency): Boolean {
return exchangeService?.availableForSell(
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
) ?: false
}
}

View file

@ -14,6 +14,7 @@ import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.domain.tokens.UserTokensRepository
import com.tangem.tap.domain.walletStores.WalletStoresManager
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.network.exchangeServices.ExchangeService
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import org.rekotlin.Action
@ -45,6 +46,7 @@ class AppStateHolder @Inject constructor() : WalletsStateHolder, ReduxNavControl
var tangemSdkManager: TangemSdkManager? = null
var walletStoresManager: WalletStoresManager? = null
var appFiatCurrency: FiatCurrency = FiatCurrency.Default
var exchangeService: ExchangeService? = null
fun getActualCard(): CardDTO? {
return scanResponse?.card

View file

@ -16,6 +16,7 @@ import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
import com.tangem.tap.proxy.AppStateHolder
import org.rekotlin.StateType
data class DaggerGraphState(
@ -35,6 +36,7 @@ data class DaggerGraphState(
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
val appCurrencyRepository: AppCurrencyRepository? = null,
val walletManagersFacade: WalletManagersFacade? = null,
val appStateHolder: AppStateHolder? = null,
) : StateType {
inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T {