Updated on 2026-08-14
This commit is contained in:
parent
e87d946e40
commit
a3a18854c8
48 changed files with 544 additions and 328 deletions
|
|
@ -1,68 +1,14 @@
|
||||||
package com.tangem.tap.network.auth
|
package com.tangem.tap.network.auth
|
||||||
|
|
||||||
import com.tangem.common.CardIdRangeDec
|
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|
||||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
|
||||||
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
|
||||||
import com.tangem.lib.auth.ExpressAuthProvider
|
import com.tangem.lib.auth.ExpressAuthProvider
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
internal class DefaultExpressAuthProvider(
|
internal class DefaultExpressAuthProvider : ExpressAuthProvider {
|
||||||
private val userWalletsStore: UserWalletsStore,
|
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
|
||||||
) : ExpressAuthProvider {
|
|
||||||
|
|
||||||
private var uuid = AtomicReference(UUID.randomUUID())
|
private var uuid = AtomicReference(UUID.randomUUID())
|
||||||
|
|
||||||
override fun getUserId(): String {
|
|
||||||
return userWalletsStore.selectedUserWalletOrNull?.walletId?.stringValue ?: error("No user id provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSessionId(): String {
|
override fun getSessionId(): String {
|
||||||
return uuid.get().toString()
|
return uuid.get().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRefCode(): String {
|
|
||||||
val selectedUserWallet = userWalletsStore.selectedUserWalletOrNull ?: error("Can not get selected user wallet")
|
|
||||||
|
|
||||||
return when {
|
|
||||||
isRing(selectedUserWallet) -> "ring"
|
|
||||||
isChangeNow(selectedUserWallet) -> "ChangeNow"
|
|
||||||
isPartner(selectedUserWallet) -> "partner"
|
|
||||||
else -> ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isRing(selectedUserWallet: UserWallet): Boolean {
|
|
||||||
val addedWalletsWithRings = runBlocking {
|
|
||||||
appPreferencesStore.getSyncOrDefault(
|
|
||||||
key = PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY,
|
|
||||||
default = emptySet(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addedWalletsWithRings.contains(selectedUserWallet.walletId.stringValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isChangeNow(selectedUserWallet: UserWallet): Boolean {
|
|
||||||
val changeNowRange = CardIdRangeDec(
|
|
||||||
start = "AF99001800554008",
|
|
||||||
end = "AF99001800559994",
|
|
||||||
)
|
|
||||||
val card = selectedUserWallet.scanResponse.card
|
|
||||||
return card.batchId == BATCH_ID_CHANGENOW || changeNowRange?.contains(card.cardId) == true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isPartner(selectedUserWallet: UserWallet): Boolean {
|
|
||||||
return selectedUserWallet.scanResponse.card.batchId == BATCH_ID_PARTNER
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
const val BATCH_ID_CHANGENOW = "BB000013"
|
|
||||||
const val BATCH_ID_PARTNER = "AF990015"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,8 +2,6 @@ package com.tangem.tap.network.auth.di
|
||||||
|
|
||||||
import com.tangem.datasource.api.common.AuthProvider
|
import com.tangem.datasource.api.common.AuthProvider
|
||||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
|
||||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
import com.tangem.lib.auth.ExpressAuthProvider
|
import com.tangem.lib.auth.ExpressAuthProvider
|
||||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||||
|
|
@ -30,14 +28,8 @@ internal class AuthModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideExpressAuthProvider(
|
fun provideExpressAuthProvider(): ExpressAuthProvider {
|
||||||
userWalletsStore: UserWalletsStore,
|
return DefaultExpressAuthProvider()
|
||||||
appPreferencesStore: AppPreferencesStore,
|
|
||||||
): ExpressAuthProvider {
|
|
||||||
return DefaultExpressAuthProvider(
|
|
||||||
userWalletsStore = userWalletsStore,
|
|
||||||
appPreferencesStore = appPreferencesStore,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
||||||
|
|
@ -48,10 +48,8 @@ internal class Express(
|
||||||
|
|
||||||
private fun createHeaders(isProd: Boolean) = buildMap {
|
private fun createHeaders(isProd: Boolean) = buildMap {
|
||||||
put(key = "api-key", value = ProviderSuspend { getApiKey(isProd) })
|
put(key = "api-key", value = ProviderSuspend { getApiKey(isProd) })
|
||||||
put(key = "user-id", value = ProviderSuspend(expressAuthProvider::getUserId))
|
|
||||||
put(key = "session-id", value = ProviderSuspend(expressAuthProvider::getSessionId))
|
put(key = "session-id", value = ProviderSuspend(expressAuthProvider::getSessionId))
|
||||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider).values)
|
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider).values)
|
||||||
put(key = "refcode", value = ProviderSuspend(expressAuthProvider::getRefCode))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getApiKey(isProd: Boolean): String {
|
private fun getApiKey(isProd: Boolean): String {
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@ import com.tangem.datasource.api.express.models.request.AssetsRequestBody
|
||||||
import com.tangem.datasource.api.express.models.request.ExchangeSentRequestBody
|
import com.tangem.datasource.api.express.models.request.ExchangeSentRequestBody
|
||||||
import com.tangem.datasource.api.express.models.request.PairsRequestBody
|
import com.tangem.datasource.api.express.models.request.PairsRequestBody
|
||||||
import com.tangem.datasource.api.express.models.response.*
|
import com.tangem.datasource.api.express.models.response.*
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.*
|
||||||
import retrofit2.http.GET
|
|
||||||
import retrofit2.http.POST
|
|
||||||
import retrofit2.http.Query
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface of Tangem Express API (new swap mechanism)
|
* Interface of Tangem Express API (new swap mechanism)
|
||||||
|
|
@ -17,16 +14,29 @@ import retrofit2.http.Query
|
||||||
interface TangemExpressApi {
|
interface TangemExpressApi {
|
||||||
|
|
||||||
@POST("assets")
|
@POST("assets")
|
||||||
suspend fun getAssets(@Body body: AssetsRequestBody): ApiResponse<List<Asset>>
|
suspend fun getAssets(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
@Body body: AssetsRequestBody,
|
||||||
|
): ApiResponse<List<Asset>>
|
||||||
|
|
||||||
@POST("pairs")
|
@POST("pairs")
|
||||||
suspend fun getPairs(@Body body: PairsRequestBody): ApiResponse<List<SwapPair>>
|
suspend fun getPairs(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
@Body body: PairsRequestBody,
|
||||||
|
): ApiResponse<List<SwapPair>>
|
||||||
|
|
||||||
@GET("providers")
|
@GET("providers")
|
||||||
suspend fun getProviders(): ApiResponse<List<ExchangeProvider>>
|
suspend fun getProviders(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
): ApiResponse<List<ExchangeProvider>>
|
||||||
|
|
||||||
@GET("exchange-quote")
|
@GET("exchange-quote")
|
||||||
suspend fun getExchangeQuote(
|
suspend fun getExchangeQuote(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
@Query("fromContractAddress") fromContractAddress: String,
|
@Query("fromContractAddress") fromContractAddress: String,
|
||||||
@Query("fromNetwork") fromNetwork: String,
|
@Query("fromNetwork") fromNetwork: String,
|
||||||
@Query("toContractAddress") toContractAddress: String,
|
@Query("toContractAddress") toContractAddress: String,
|
||||||
|
|
@ -40,6 +50,8 @@ interface TangemExpressApi {
|
||||||
|
|
||||||
@GET("exchange-data")
|
@GET("exchange-data")
|
||||||
suspend fun getExchangeData(
|
suspend fun getExchangeData(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
@Query("fromContractAddress") fromContractAddress: String,
|
@Query("fromContractAddress") fromContractAddress: String,
|
||||||
@Query("fromNetwork") fromNetwork: String,
|
@Query("fromNetwork") fromNetwork: String,
|
||||||
@Query("toContractAddress") toContractAddress: String,
|
@Query("toContractAddress") toContractAddress: String,
|
||||||
|
|
@ -57,8 +69,16 @@ interface TangemExpressApi {
|
||||||
): ApiResponse<ExchangeDataResponse>
|
): ApiResponse<ExchangeDataResponse>
|
||||||
|
|
||||||
@GET("exchange-status")
|
@GET("exchange-status")
|
||||||
suspend fun getExchangeStatus(@Query("txId") txId: String): ApiResponse<ExchangeStatusResponse>
|
suspend fun getExchangeStatus(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
@Query("txId") txId: String,
|
||||||
|
): ApiResponse<ExchangeStatusResponse>
|
||||||
|
|
||||||
@POST("exchange-sent")
|
@POST("exchange-sent")
|
||||||
suspend fun exchangeSent(@Body body: ExchangeSentRequestBody): ApiResponse<ExchangeSentResponseBody>
|
suspend fun exchangeSent(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
@Body body: ExchangeSentRequestBody,
|
||||||
|
): ApiResponse<ExchangeSentResponseBody>
|
||||||
}
|
}
|
||||||
|
|
@ -3,37 +3,52 @@ package com.tangem.datasource.api.onramp
|
||||||
import com.tangem.datasource.api.common.response.ApiResponse
|
import com.tangem.datasource.api.common.response.ApiResponse
|
||||||
import com.tangem.datasource.api.onramp.models.request.OnrampPairsRequest
|
import com.tangem.datasource.api.onramp.models.request.OnrampPairsRequest
|
||||||
import com.tangem.datasource.api.onramp.models.response.OnrampDataResponse
|
import com.tangem.datasource.api.onramp.models.response.OnrampDataResponse
|
||||||
import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO
|
|
||||||
import com.tangem.datasource.api.onramp.models.response.OnrampQuoteResponse
|
import com.tangem.datasource.api.onramp.models.response.OnrampQuoteResponse
|
||||||
import com.tangem.datasource.api.onramp.models.response.OnrampStatusResponse
|
import com.tangem.datasource.api.onramp.models.response.OnrampStatusResponse
|
||||||
import com.tangem.datasource.api.onramp.models.response.model.OnrampCountryDTO
|
import com.tangem.datasource.api.onramp.models.response.model.OnrampCountryDTO
|
||||||
import com.tangem.datasource.api.onramp.models.response.model.OnrampCurrencyDTO
|
import com.tangem.datasource.api.onramp.models.response.model.OnrampCurrencyDTO
|
||||||
|
import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO
|
||||||
import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO
|
import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.*
|
||||||
import retrofit2.http.GET
|
|
||||||
import retrofit2.http.POST
|
|
||||||
import retrofit2.http.Query
|
|
||||||
|
|
||||||
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
|
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
|
||||||
interface OnrampApi {
|
interface OnrampApi {
|
||||||
|
|
||||||
@GET("currencies")
|
@GET("currencies")
|
||||||
suspend fun getCurrencies(): ApiResponse<List<OnrampCurrencyDTO>>
|
suspend fun getCurrencies(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
): ApiResponse<List<OnrampCurrencyDTO>>
|
||||||
|
|
||||||
@GET("countries")
|
@GET("countries")
|
||||||
suspend fun getCountries(): ApiResponse<List<OnrampCountryDTO>>
|
suspend fun getCountries(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
): ApiResponse<List<OnrampCountryDTO>>
|
||||||
|
|
||||||
@GET("country-by-ip")
|
@GET("country-by-ip")
|
||||||
suspend fun getCountryByIp(): ApiResponse<OnrampCountryDTO>
|
suspend fun getCountryByIp(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
): ApiResponse<OnrampCountryDTO>
|
||||||
|
|
||||||
@GET("payment-methods")
|
@GET("payment-methods")
|
||||||
suspend fun getPaymentMethods(): ApiResponse<List<PaymentMethodDTO>>
|
suspend fun getPaymentMethods(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
): ApiResponse<List<PaymentMethodDTO>>
|
||||||
|
|
||||||
@POST("onramp-pairs")
|
@POST("onramp-pairs")
|
||||||
suspend fun getPairs(@Body body: OnrampPairsRequest): ApiResponse<List<OnrampPairDTO>>
|
suspend fun getPairs(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
@Body body: OnrampPairsRequest,
|
||||||
|
): ApiResponse<List<OnrampPairDTO>>
|
||||||
|
|
||||||
@GET("onramp-quote")
|
@GET("onramp-quote")
|
||||||
suspend fun getQuote(
|
suspend fun getQuote(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
||||||
@Query("fromPrecision") fromPrecision: Int,
|
@Query("fromPrecision") fromPrecision: Int,
|
||||||
@Query("toContractAddress") toContractAddress: String,
|
@Query("toContractAddress") toContractAddress: String,
|
||||||
|
|
@ -47,6 +62,8 @@ interface OnrampApi {
|
||||||
|
|
||||||
@GET("onramp-data")
|
@GET("onramp-data")
|
||||||
suspend fun getData(
|
suspend fun getData(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
||||||
@Query("fromPrecision") fromPrecision: Int,
|
@Query("fromPrecision") fromPrecision: Int,
|
||||||
@Query("toContractAddress") toContractAddress: String,
|
@Query("toContractAddress") toContractAddress: String,
|
||||||
|
|
@ -64,5 +81,9 @@ interface OnrampApi {
|
||||||
): ApiResponse<OnrampDataResponse>
|
): ApiResponse<OnrampDataResponse>
|
||||||
|
|
||||||
@GET("onramp-status")
|
@GET("onramp-status")
|
||||||
suspend fun getStatus(@Query("txId") txId: String): ApiResponse<OnrampStatusResponse>
|
suspend fun getStatus(
|
||||||
|
@Header("user-id") userWalletId: String,
|
||||||
|
@Header("refcode") refCode: String,
|
||||||
|
@Query("txId") txId: String,
|
||||||
|
): ApiResponse<OnrampStatusResponse>
|
||||||
}
|
}
|
||||||
|
|
@ -5,11 +5,14 @@ import com.tangem.datasource.api.express.TangemExpressApi
|
||||||
import com.tangem.datasource.api.express.models.request.AssetsRequestBody
|
import com.tangem.datasource.api.express.models.request.AssetsRequestBody
|
||||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||||
import com.tangem.datasource.api.express.models.response.Asset
|
import com.tangem.datasource.api.express.models.response.Asset
|
||||||
|
import com.tangem.datasource.exchangeservice.swap.ExpressUtils.getRefCode
|
||||||
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
import com.tangem.datasource.local.token.ExpressAssetsStore
|
import com.tangem.datasource.local.token.ExpressAssetsStore
|
||||||
import com.tangem.domain.core.lce.Lce
|
import com.tangem.domain.core.lce.Lce
|
||||||
import com.tangem.domain.core.utils.lceContent
|
import com.tangem.domain.core.utils.lceContent
|
||||||
import com.tangem.domain.core.utils.lceError
|
import com.tangem.domain.core.utils.lceError
|
||||||
import com.tangem.domain.core.utils.lceLoading
|
import com.tangem.domain.core.utils.lceLoading
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -33,31 +36,34 @@ typealias InitializationStatusFlow = MutableStateFlow<Lce<Throwable, List<Asset>
|
||||||
internal class DefaultExpressServiceLoader @Inject constructor(
|
internal class DefaultExpressServiceLoader @Inject constructor(
|
||||||
private val tangemExpressApi: TangemExpressApi,
|
private val tangemExpressApi: TangemExpressApi,
|
||||||
private val expressAssetsStore: ExpressAssetsStore,
|
private val expressAssetsStore: ExpressAssetsStore,
|
||||||
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : ExpressServiceLoader {
|
) : ExpressServiceLoader {
|
||||||
|
|
||||||
private val initializationStatuses =
|
private val initializationStatuses =
|
||||||
MutableStateFlow<Map<UserWalletId, InitializationStatusFlow>>(value = emptyMap())
|
MutableStateFlow<Map<UserWalletId, InitializationStatusFlow>>(value = emptyMap())
|
||||||
|
|
||||||
override suspend fun update(userWalletId: UserWalletId, userTokens: List<LeastTokenInfo>) {
|
override suspend fun update(userWallet: UserWallet, userTokens: List<LeastTokenInfo>) {
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
val initializationStatus = getInitializationStatusInternal(userWalletId)
|
val initializationStatus = getInitializationStatusInternal(userWallet.walletId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (userTokens.isNotEmpty()) {
|
if (userTokens.isNotEmpty()) {
|
||||||
val response = tangemExpressApi.getAssets(
|
val response = tangemExpressApi.getAssets(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = getRefCode(userWallet, appPreferencesStore),
|
||||||
body = AssetsRequestBody(tokensList = userTokens),
|
body = AssetsRequestBody(tokensList = userTokens),
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
|
|
||||||
expressAssetsStore.store(userWalletId, response)
|
expressAssetsStore.store(userWallet.walletId, response)
|
||||||
|
|
||||||
initializationStatus.update { response.lceContent() }
|
initializationStatus.update { response.lceContent() }
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (expressAssetsStore.getSyncOrNull(userWalletId) == null) {
|
if (expressAssetsStore.getSyncOrNull(userWallet.walletId) == null) {
|
||||||
initializationStatus.update { e.lceError() }
|
initializationStatus.update { e.lceError() }
|
||||||
}
|
}
|
||||||
Timber.e(e, "Unable to fetch assets for: ${userWalletId.stringValue}")
|
Timber.e(e, "Unable to fetch assets for: ${userWallet.walletId.stringValue}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.datasource.exchangeservice.swap
|
||||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||||
import com.tangem.datasource.api.express.models.response.Asset
|
import com.tangem.datasource.api.express.models.response.Asset
|
||||||
import com.tangem.domain.core.lce.Lce
|
import com.tangem.domain.core.lce.Lce
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|
@ -13,8 +14,8 @@ import kotlinx.coroutines.flow.Flow
|
||||||
*/
|
*/
|
||||||
interface ExpressServiceLoader {
|
interface ExpressServiceLoader {
|
||||||
|
|
||||||
/** Update service using [userWalletId] and [userTokens] */
|
/** Update service using [userWallet] and [userTokens] */
|
||||||
suspend fun update(userWalletId: UserWalletId, userTokens: List<LeastTokenInfo>)
|
suspend fun update(userWallet: UserWallet, userTokens: List<LeastTokenInfo>)
|
||||||
|
|
||||||
/** Get initialization status by [userWalletId] */
|
/** Get initialization status by [userWalletId] */
|
||||||
fun getInitializationStatus(userWalletId: UserWalletId): Flow<Lce<Throwable, List<Asset>>>
|
fun getInitializationStatus(userWalletId: UserWalletId): Flow<Lce<Throwable, List<Asset>>>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.tangem.datasource.exchangeservice.swap
|
||||||
|
|
||||||
|
import com.tangem.common.CardIdRangeDec
|
||||||
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
|
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||||
|
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
object ExpressUtils {
|
||||||
|
private const val BATCH_ID_CHANGENOW = "BB000013"
|
||||||
|
private const val BATCH_ID_PARTNER = "AF990015"
|
||||||
|
|
||||||
|
fun getRefCode(userWallet: UserWallet, appPreferencesStore: AppPreferencesStore): String {
|
||||||
|
return when {
|
||||||
|
isRing(userWallet, appPreferencesStore) -> "ring"
|
||||||
|
isChangeNow(userWallet) -> "ChangeNow"
|
||||||
|
isPartner(userWallet) -> "partner"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isRing(selectedUserWallet: UserWallet, appPreferencesStore: AppPreferencesStore): Boolean {
|
||||||
|
val addedWalletsWithRings = runBlocking {
|
||||||
|
appPreferencesStore.getSyncOrDefault(
|
||||||
|
key = PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY,
|
||||||
|
default = emptySet(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return addedWalletsWithRings.contains(selectedUserWallet.walletId.stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isChangeNow(selectedUserWallet: UserWallet): Boolean {
|
||||||
|
val changeNowRange = CardIdRangeDec(
|
||||||
|
start = "AF99001800554008",
|
||||||
|
end = "AF99001800559994",
|
||||||
|
)
|
||||||
|
val card = selectedUserWallet.scanResponse.card
|
||||||
|
return card.batchId == BATCH_ID_CHANGENOW || changeNowRange?.contains(card.cardId) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isPartner(selectedUserWallet: UserWallet): Boolean {
|
||||||
|
return selectedUserWallet.scanResponse.card.batchId == BATCH_ID_PARTNER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,9 +50,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
every { appVersionProvider.versionName } returns VERSION_NAME
|
every { appVersionProvider.versionName } returns VERSION_NAME
|
||||||
every { expressAuthProvider.getUserId() } returns EXPRESS_USER_ID
|
|
||||||
every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID
|
every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID
|
||||||
every { expressAuthProvider.getRefCode() } returns EXPRESS_REF_CODE
|
|
||||||
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
|
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
|
||||||
every { appAuthProvider.getCardId() } returns APP_CARD_ID
|
every { appAuthProvider.getCardId() } returns APP_CARD_ID
|
||||||
every { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
|
every { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
|
||||||
|
|
@ -131,9 +129,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
||||||
MockEnvironmentConfigStorage.EXPRESS_DEV_API_KEY
|
MockEnvironmentConfigStorage.EXPRESS_DEV_API_KEY
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user-id" to ProviderSuspend { EXPRESS_USER_ID },
|
|
||||||
"session-id" to ProviderSuspend { EXPRESS_SESSION_ID },
|
"session-id" to ProviderSuspend { EXPRESS_SESSION_ID },
|
||||||
"refcode" to ProviderSuspend { EXPRESS_REF_CODE },
|
|
||||||
"version" to ProviderSuspend { VERSION_NAME },
|
"version" to ProviderSuspend { VERSION_NAME },
|
||||||
"platform" to ProviderSuspend { "android" },
|
"platform" to ProviderSuspend { "android" },
|
||||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import com.tangem.datasource.api.onramp.models.response.model.OnrampCountryDTO
|
||||||
import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO
|
import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO
|
||||||
import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO
|
import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO
|
||||||
import com.tangem.datasource.crypto.DataSignatureVerifier
|
import com.tangem.datasource.crypto.DataSignatureVerifier
|
||||||
|
import com.tangem.datasource.exchangeservice.swap.ExpressUtils
|
||||||
import com.tangem.datasource.local.onramp.countries.OnrampCountriesStore
|
import com.tangem.datasource.local.onramp.countries.OnrampCountriesStore
|
||||||
import com.tangem.datasource.local.onramp.currencies.OnrampCurrenciesStore
|
import com.tangem.datasource.local.onramp.currencies.OnrampCurrenciesStore
|
||||||
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
|
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
|
||||||
|
|
@ -43,6 +44,7 @@ import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
import com.tangem.domain.tokens.model.Amount
|
import com.tangem.domain.tokens.model.Amount
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
|
|
@ -82,10 +84,16 @@ internal class DefaultOnrampRepository(
|
||||||
|
|
||||||
override fun getCurrencies(): Flow<List<OnrampCurrency>> = currenciesStore.get(CURRENCIES_KEY)
|
override fun getCurrencies(): Flow<List<OnrampCurrency>> = currenciesStore.get(CURRENCIES_KEY)
|
||||||
|
|
||||||
override suspend fun fetchCurrencies() = withContext(dispatchers.io) {
|
override suspend fun fetchCurrencies(userWallet: UserWallet) = withContext(dispatchers.io) {
|
||||||
if (!currenciesStore.getSyncOrNull(CURRENCIES_KEY).isNullOrEmpty()) return@withContext
|
if (!currenciesStore.getSyncOrNull(CURRENCIES_KEY).isNullOrEmpty()) return@withContext
|
||||||
|
|
||||||
val result = onrampApi.getCurrencies()
|
val result = onrampApi.getCurrencies(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.map(currencyConverter::convert)
|
.map(currencyConverter::convert)
|
||||||
|
|
||||||
|
|
@ -98,10 +106,16 @@ internal class DefaultOnrampRepository(
|
||||||
return countriesStore.getSyncOrNull(COUNTRIES_KEY)
|
return countriesStore.getSyncOrNull(COUNTRIES_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchCountries(): List<OnrampCountry> = withContext(dispatchers.io) {
|
override suspend fun fetchCountries(userWallet: UserWallet): List<OnrampCountry> = withContext(dispatchers.io) {
|
||||||
if (!countriesStore.getSyncOrNull(COUNTRIES_KEY).isNullOrEmpty()) return@withContext emptyList()
|
if (!countriesStore.getSyncOrNull(COUNTRIES_KEY).isNullOrEmpty()) return@withContext emptyList()
|
||||||
|
|
||||||
val result = onrampApi.getCountries()
|
val result = onrampApi.getCountries(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.map(countryConverter::convert)
|
.map(countryConverter::convert)
|
||||||
|
|
||||||
|
|
@ -110,14 +124,27 @@ internal class DefaultOnrampRepository(
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getCountryByIp(): OnrampCountry = withContext(dispatchers.io) {
|
override suspend fun getCountryByIp(userWallet: UserWallet): OnrampCountry = withContext(dispatchers.io) {
|
||||||
onrampApi.getCountryByIp()
|
onrampApi.getCountryByIp(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.let(countryConverter::convert)
|
.let(countryConverter::convert)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getStatus(txId: String): OnrampStatus = withContext(dispatchers.io) {
|
override suspend fun getStatus(userWallet: UserWallet, txId: String): OnrampStatus = withContext(dispatchers.io) {
|
||||||
onrampApi.getStatus(txId)
|
onrampApi.getStatus(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
txId = txId,
|
||||||
|
)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.let(statusConverter::convert)
|
.let(statusConverter::convert)
|
||||||
}
|
}
|
||||||
|
|
@ -161,11 +188,19 @@ internal class DefaultOnrampRepository(
|
||||||
.map { it?.let(countryConverter::convert) }
|
.map { it?.let(countryConverter::convert) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchPaymentMethodsIfAbsent() = withContext(dispatchers.io) {
|
override suspend fun fetchPaymentMethodsIfAbsent(userWallet: UserWallet) = withContext(dispatchers.io) {
|
||||||
if (paymentMethodsStore.contains(PAYMENT_METHODS_KEY)) return@withContext
|
if (paymentMethodsStore.contains(PAYMENT_METHODS_KEY)) return@withContext
|
||||||
|
|
||||||
val response = safeApiCall(
|
val response = safeApiCall(
|
||||||
call = { onrampApi.getPaymentMethods().bind() },
|
call = {
|
||||||
|
onrampApi.getPaymentMethods(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
).bind()
|
||||||
|
},
|
||||||
onError = {
|
onError = {
|
||||||
Timber.w(it, "Unable to fetch onramp payment methods")
|
Timber.w(it, "Unable to fetch onramp payment methods")
|
||||||
throw it
|
throw it
|
||||||
|
|
@ -174,100 +209,123 @@ internal class DefaultOnrampRepository(
|
||||||
paymentMethodsStore.store(PAYMENT_METHODS_KEY, response.removeApplePay())
|
paymentMethodsStore.store(PAYMENT_METHODS_KEY, response.removeApplePay())
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchPairs(currency: OnrampCurrency, country: OnrampCountry, cryptoCurrency: CryptoCurrency) =
|
override suspend fun fetchPairs(
|
||||||
withContext(dispatchers.io) {
|
userWallet: UserWallet,
|
||||||
val onrampPairs = async {
|
currency: OnrampCurrency,
|
||||||
safeApiCall(
|
country: OnrampCountry,
|
||||||
call = {
|
cryptoCurrency: CryptoCurrency,
|
||||||
onrampApi.getPairs(
|
) = withContext(dispatchers.io) {
|
||||||
body = OnrampPairsRequest(
|
val onrampPairs = async {
|
||||||
fromCurrencyCode = currency.code,
|
safeApiCall(
|
||||||
countryCode = country.code,
|
call = {
|
||||||
to = listOf(
|
onrampApi.getPairs(
|
||||||
OnrampDestinationDTO(
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
contractAddress = cryptoCurrency.getContractAddress(),
|
refCode = ExpressUtils.getRefCode(
|
||||||
network = cryptoCurrency.network.backendId,
|
userWallet = userWallet,
|
||||||
),
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
body = OnrampPairsRequest(
|
||||||
|
fromCurrencyCode = currency.code,
|
||||||
|
countryCode = country.code,
|
||||||
|
to = listOf(
|
||||||
|
OnrampDestinationDTO(
|
||||||
|
contractAddress = cryptoCurrency.getContractAddress(),
|
||||||
|
network = cryptoCurrency.network.backendId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).bind()
|
),
|
||||||
},
|
).bind()
|
||||||
onError = {
|
},
|
||||||
Timber.w(it, "Unable to fetch onramp pairs")
|
onError = {
|
||||||
throw it
|
Timber.w(it, "Unable to fetch onramp pairs")
|
||||||
},
|
throw it
|
||||||
)
|
},
|
||||||
}
|
)
|
||||||
val providers = async {
|
|
||||||
safeApiCall(
|
|
||||||
call = { expressApi.getProviders().bind() },
|
|
||||||
onError = {
|
|
||||||
Timber.w(it, "Unable to fetch express providers")
|
|
||||||
throw it
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
storeOnrampPairs(pairs = onrampPairs.await(), providers = providers.await())
|
|
||||||
}
|
}
|
||||||
|
val providers = async {
|
||||||
|
safeApiCall(
|
||||||
|
call = {
|
||||||
|
expressApi.getProviders(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
).bind()
|
||||||
|
},
|
||||||
|
onError = {
|
||||||
|
Timber.w(it, "Unable to fetch express providers")
|
||||||
|
throw it
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
storeOnrampPairs(pairs = onrampPairs.await(), providers = providers.await())
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun fetchQuotes(cryptoCurrency: CryptoCurrency, amount: Amount) = withContext(dispatchers.io) {
|
override suspend fun fetchQuotes(userWallet: UserWallet, cryptoCurrency: CryptoCurrency, amount: Amount) =
|
||||||
val pairs = requireNotNull(pairsStore.getSyncOrNull(PAIRS_KEY)) {
|
withContext(dispatchers.io) {
|
||||||
"Unable to get pairs. At this point they must not be null."
|
val pairs = requireNotNull(pairsStore.getSyncOrNull(PAIRS_KEY)) {
|
||||||
}
|
"Unable to get pairs. At this point they must not be null."
|
||||||
val amountValue = requireNotNull(amount.value) { "Amount value must not be null" }
|
}
|
||||||
val fromAmount = amountValue.movePointRight(amount.decimals).toString()
|
val amountValue = requireNotNull(amount.value) { "Amount value must not be null" }
|
||||||
val currency = requireNotNull(getDefaultCurrencySync()) { "Default currency must not be null" }
|
val fromAmount = amountValue.movePointRight(amount.decimals).toString()
|
||||||
val country = requireNotNull(getDefaultCountrySync()) { "Default country must not be null" }
|
val currency = requireNotNull(getDefaultCurrencySync()) { "Default currency must not be null" }
|
||||||
val fromOnrampAmount = OnrampAmount(
|
val country = requireNotNull(getDefaultCountrySync()) { "Default country must not be null" }
|
||||||
value = fromAmount
|
val fromOnrampAmount = OnrampAmount(
|
||||||
.toBigDecimalOrDefault()
|
value = fromAmount
|
||||||
.movePointLeft(currency.precision),
|
.toBigDecimalOrDefault()
|
||||||
decimals = currency.precision,
|
.movePointLeft(currency.precision),
|
||||||
symbol = amount.currencySymbol,
|
decimals = currency.precision,
|
||||||
)
|
symbol = amount.currencySymbol,
|
||||||
val quotes: List<OnrampQuote> = pairs.flatMap { pair ->
|
)
|
||||||
pair.providers.flatMap { provider ->
|
val quotes: List<OnrampQuote> = pairs.flatMap { pair ->
|
||||||
provider.paymentMethods.map { paymentMethod ->
|
pair.providers.flatMap { provider ->
|
||||||
async {
|
provider.paymentMethods.map { paymentMethod ->
|
||||||
safeApiCall(
|
async {
|
||||||
call = {
|
safeApiCall(
|
||||||
val response = onrampApi.getQuote(
|
call = {
|
||||||
fromCurrencyCode = currency.code,
|
val response = onrampApi.getQuote(
|
||||||
fromPrecision = currency.precision,
|
fromCurrencyCode = currency.code,
|
||||||
toContractAddress = cryptoCurrency.getContractAddress(),
|
fromPrecision = currency.precision,
|
||||||
toNetwork = cryptoCurrency.network.backendId,
|
toContractAddress = cryptoCurrency.getContractAddress(),
|
||||||
paymentMethod = paymentMethod.id,
|
toNetwork = cryptoCurrency.network.backendId,
|
||||||
countryCode = country.code,
|
paymentMethod = paymentMethod.id,
|
||||||
fromAmount = fromAmount,
|
countryCode = country.code,
|
||||||
toDecimals = cryptoCurrency.decimals,
|
fromAmount = fromAmount,
|
||||||
providerId = provider.id,
|
toDecimals = cryptoCurrency.decimals,
|
||||||
).bind()
|
providerId = provider.id,
|
||||||
OnrampQuote.Data(
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
fromAmount = fromOnrampAmount,
|
refCode = ExpressUtils.getRefCode(
|
||||||
toAmount = convertToAmount(response.toAmount, cryptoCurrency),
|
userWallet = userWallet,
|
||||||
minFromAmount = convertToAmount(response.minFromAmount, cryptoCurrency),
|
appPreferencesStore = appPreferencesStore,
|
||||||
maxFromAmount = convertToAmount(response.maxFromAmount, cryptoCurrency),
|
),
|
||||||
paymentMethod = paymentMethod,
|
).bind()
|
||||||
provider = provider,
|
OnrampQuote.Data(
|
||||||
countryCode = response.countryCode,
|
fromAmount = fromOnrampAmount,
|
||||||
)
|
toAmount = convertToAmount(response.toAmount, cryptoCurrency),
|
||||||
},
|
minFromAmount = convertToAmount(response.minFromAmount, cryptoCurrency),
|
||||||
onError = { error ->
|
maxFromAmount = convertToAmount(response.maxFromAmount, cryptoCurrency),
|
||||||
convertQuoteError(
|
paymentMethod = paymentMethod,
|
||||||
error = error,
|
provider = provider,
|
||||||
paymentMethod = paymentMethod,
|
countryCode = response.countryCode,
|
||||||
provider = provider,
|
)
|
||||||
fromOnrampAmount = fromOnrampAmount,
|
},
|
||||||
countryCode = country.code,
|
onError = { error ->
|
||||||
)
|
convertQuoteError(
|
||||||
},
|
error = error,
|
||||||
)
|
paymentMethod = paymentMethod,
|
||||||
|
provider = provider,
|
||||||
|
fromOnrampAmount = fromOnrampAmount,
|
||||||
|
countryCode = country.code,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}.awaitAll().filterNotNull()
|
||||||
}.awaitAll().filterNotNull()
|
quotesStore.store(QUOTES_KEY, quotes)
|
||||||
quotesStore.store(QUOTES_KEY, quotes)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun getQuotes(): Flow<List<OnrampQuote>> {
|
override fun getQuotes(): Flow<List<OnrampQuote>> {
|
||||||
return quotesStore.get(QUOTES_KEY)
|
return quotesStore.get(QUOTES_KEY)
|
||||||
|
|
@ -278,14 +336,14 @@ internal class DefaultOnrampRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getOnrampData(
|
override suspend fun getOnrampData(
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
quote: OnrampProviderWithQuote.Data,
|
quote: OnrampProviderWithQuote.Data,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
): OnrampTransaction = withContext(dispatchers.io) {
|
): OnrampTransaction = withContext(dispatchers.io) {
|
||||||
try {
|
try {
|
||||||
val address = requireNotNull(
|
val address = requireNotNull(
|
||||||
value = walletManagersFacade.getDefaultAddress(userWalletId, cryptoCurrency.network),
|
value = walletManagersFacade.getDefaultAddress(userWallet.walletId, cryptoCurrency.network),
|
||||||
lazyMessage = { "Address must not be null" },
|
lazyMessage = { "Address must not be null" },
|
||||||
)
|
)
|
||||||
val fromAmountString = quote.fromAmount.value.movePointRight(quote.fromAmount.decimals).toString()
|
val fromAmountString = quote.fromAmount.value.movePointRight(quote.fromAmount.decimals).toString()
|
||||||
|
|
@ -309,6 +367,11 @@ internal class DefaultOnrampRepository(
|
||||||
language = null,
|
language = null,
|
||||||
theme = getTheme(isDarkTheme),
|
theme = getTheme(isDarkTheme),
|
||||||
requestId = requestId,
|
requestId = requestId,
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
).bind()
|
).bind()
|
||||||
},
|
},
|
||||||
onError = { e ->
|
onError = { e ->
|
||||||
|
|
@ -326,7 +389,7 @@ internal class DefaultOnrampRepository(
|
||||||
txId = data.txId,
|
txId = data.txId,
|
||||||
quote = quote,
|
quote = quote,
|
||||||
onrampDataJson = dataJson,
|
onrampDataJson = dataJson,
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWallet.walletId,
|
||||||
currency = currency,
|
currency = currency,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
residency = country.name,
|
residency = country.name,
|
||||||
|
|
|
||||||
|
|
@ -618,7 +618,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
launch { expressServiceLoader.update(userWalletId, tokens) }
|
launch { expressServiceLoader.update(getUserWallet(userWalletId), tokens) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -639,7 +639,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
skipCache = refresh,
|
skipCache = refresh,
|
||||||
block = {
|
block = {
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
launch { expressServiceLoader.update(userWalletId, tokens) }
|
launch { expressServiceLoader.update(getUserWallet(userWalletId), tokens) }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -677,13 +677,13 @@ internal class DefaultCurrenciesRepository(
|
||||||
isSortedByBalance = false,
|
isSortedByBalance = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet {
|
private fun getUserWallet(userWalletId: UserWalletId): UserWallet {
|
||||||
return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||||
"Unable to find a user wallet with provided ID: $userWalletId"
|
"Unable to find a user wallet with provided ID: $userWalletId"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun ensureIsCorrectUserWallet(userWalletId: UserWalletId, isMultiCurrencyWalletExpected: Boolean) {
|
private fun ensureIsCorrectUserWallet(userWalletId: UserWalletId, isMultiCurrencyWalletExpected: Boolean) {
|
||||||
val userWallet = getUserWallet(userWalletId)
|
val userWallet = getUserWallet(userWalletId)
|
||||||
|
|
||||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected)
|
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected)
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,31 @@ import com.tangem.domain.onramp.model.OnrampCountry
|
||||||
import com.tangem.domain.onramp.model.error.OnrampError
|
import com.tangem.domain.onramp.model.error.OnrampError
|
||||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
class CheckOnrampAvailabilityUseCase(
|
class CheckOnrampAvailabilityUseCase(
|
||||||
private val repository: OnrampRepository,
|
private val repository: OnrampRepository,
|
||||||
private val errorResolver: OnrampErrorResolver,
|
private val errorResolver: OnrampErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(): Either<OnrampError, OnrampAvailability> {
|
suspend operator fun invoke(userWallet: UserWallet): Either<OnrampError, OnrampAvailability> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
repository.fetchPaymentMethodsIfAbsent()
|
repository.fetchPaymentMethodsIfAbsent(userWallet)
|
||||||
val savedCountry = repository.getDefaultCountrySync()
|
val savedCountry = repository.getDefaultCountrySync()
|
||||||
if (savedCountry != null) {
|
if (savedCountry != null) {
|
||||||
proceedWithSavedCountry(savedCountry = savedCountry)
|
proceedWithSavedCountry(userWallet = userWallet, savedCountry = savedCountry)
|
||||||
} else {
|
} else {
|
||||||
val detectedCountry = repository.getCountryByIp()
|
val detectedCountry = repository.getCountryByIp(userWallet)
|
||||||
OnrampAvailability.ConfirmResidency(detectedCountry)
|
OnrampAvailability.ConfirmResidency(detectedCountry)
|
||||||
}
|
}
|
||||||
}.mapLeft(errorResolver::resolve)
|
}.mapLeft(errorResolver::resolve)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun proceedWithSavedCountry(savedCountry: OnrampCountry): OnrampAvailability {
|
private suspend fun proceedWithSavedCountry(
|
||||||
val countries = repository.fetchCountries()
|
userWallet: UserWallet,
|
||||||
|
savedCountry: OnrampCountry,
|
||||||
|
): OnrampAvailability {
|
||||||
|
val countries = repository.fetchCountries(userWallet)
|
||||||
val updatedCountry = countries.find { it.id == savedCountry.id } ?: savedCountry
|
val updatedCountry = countries.find { it.id == savedCountry.id } ?: savedCountry
|
||||||
return if (updatedCountry.onrampAvailable) {
|
return if (updatedCountry.onrampAvailable) {
|
||||||
val currency = repository.getDefaultCurrencySync() ?: run {
|
val currency = repository.getDefaultCurrencySync() ?: run {
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ import arrow.core.Either
|
||||||
import com.tangem.domain.onramp.model.error.OnrampError
|
import com.tangem.domain.onramp.model.error.OnrampError
|
||||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
class FetchOnrampCountriesUseCase(
|
class FetchOnrampCountriesUseCase(
|
||||||
private val repository: OnrampRepository,
|
private val repository: OnrampRepository,
|
||||||
private val errorResolver: OnrampErrorResolver,
|
private val errorResolver: OnrampErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(): Either<OnrampError, Unit> {
|
suspend operator fun invoke(userWallet: UserWallet): Either<OnrampError, Unit> {
|
||||||
return Either.catch<Unit> { repository.fetchCountries() }.mapLeft(errorResolver::resolve)
|
return Either.catch<Unit> { repository.fetchCountries(userWallet) }.mapLeft(errorResolver::resolve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,13 +4,14 @@ import arrow.core.Either
|
||||||
import com.tangem.domain.onramp.model.error.OnrampError
|
import com.tangem.domain.onramp.model.error.OnrampError
|
||||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
class FetchOnrampCurrenciesUseCase(
|
class FetchOnrampCurrenciesUseCase(
|
||||||
private val repository: OnrampRepository,
|
private val repository: OnrampRepository,
|
||||||
private val errorResolver: OnrampErrorResolver,
|
private val errorResolver: OnrampErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(): Either<OnrampError, Unit> {
|
suspend operator fun invoke(userWallet: UserWallet): Either<OnrampError, Unit> {
|
||||||
return Either.catch { repository.fetchCurrencies() }.mapLeft(errorResolver::resolve)
|
return Either.catch { repository.fetchCurrencies(userWallet) }.mapLeft(errorResolver::resolve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import com.tangem.domain.onramp.model.OnrampCountry
|
||||||
import com.tangem.domain.onramp.model.error.OnrampError
|
import com.tangem.domain.onramp.model.error.OnrampError
|
||||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
@ -24,8 +25,8 @@ class GetOnrampCountryUseCase(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun invokeSync(): Either<OnrampError, OnrampCountry> {
|
suspend fun invokeSync(userWallet: UserWallet): Either<OnrampError, OnrampCountry> {
|
||||||
return Either.catch { repository.getDefaultCountrySync() ?: repository.getCountryByIp() }
|
return Either.catch { repository.getDefaultCountrySync() ?: repository.getCountryByIp(userWallet) }
|
||||||
.mapLeft(errorResolver::resolve)
|
.mapLeft(errorResolver::resolve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
import com.tangem.domain.onramp.repositories.OnrampTransactionRepository
|
import com.tangem.domain.onramp.repositories.OnrampTransactionRepository
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
class GetOnrampRedirectUrlUseCase(
|
class GetOnrampRedirectUrlUseCase(
|
||||||
private val repository: OnrampRepository,
|
private val repository: OnrampRepository,
|
||||||
|
|
@ -16,14 +16,14 @@ class GetOnrampRedirectUrlUseCase(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
quote: OnrampProviderWithQuote.Data,
|
quote: OnrampProviderWithQuote.Data,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
): Either<OnrampError, String> {
|
): Either<OnrampError, String> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
val transaction = repository.getOnrampData(
|
val transaction = repository.getOnrampData(
|
||||||
userWalletId = userWalletId,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
quote = quote,
|
quote = quote,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,16 @@ import com.tangem.domain.onramp.model.OnrampStatus
|
||||||
import com.tangem.domain.onramp.model.error.OnrampError
|
import com.tangem.domain.onramp.model.error.OnrampError
|
||||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
class GetOnrampStatusUseCase(
|
class GetOnrampStatusUseCase(
|
||||||
private val onrampRepository: OnrampRepository,
|
private val onrampRepository: OnrampRepository,
|
||||||
private val errorResolver: OnrampErrorResolver,
|
private val errorResolver: OnrampErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(txId: String): Either<OnrampError, OnrampStatus> {
|
suspend operator fun invoke(userWallet: UserWallet, txId: String): Either<OnrampError, OnrampStatus> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
onrampRepository.getStatus(txId)
|
onrampRepository.getStatus(userWallet = userWallet, txId = txId)
|
||||||
}.mapLeft(errorResolver::resolve)
|
}.mapLeft(errorResolver::resolve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,17 +5,23 @@ import com.tangem.domain.onramp.model.error.OnrampError
|
||||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
class OnrampFetchPairsUseCase(
|
class OnrampFetchPairsUseCase(
|
||||||
private val repository: OnrampRepository,
|
private val repository: OnrampRepository,
|
||||||
private val errorResolver: OnrampErrorResolver,
|
private val errorResolver: OnrampErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(cryptoCurrency: CryptoCurrency): Either<OnrampError, Unit> {
|
suspend operator fun invoke(userWallet: UserWallet, cryptoCurrency: CryptoCurrency): Either<OnrampError, Unit> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
val country = requireNotNull(repository.getDefaultCountrySync()) { "Country must not be null" }
|
val country = requireNotNull(repository.getDefaultCountrySync()) { "Country must not be null" }
|
||||||
val currency = requireNotNull(repository.getDefaultCurrencySync()) { "Currency must not be null" }
|
val currency = requireNotNull(repository.getDefaultCurrencySync()) { "Currency must not be null" }
|
||||||
repository.fetchPairs(currency = currency, country = country, cryptoCurrency = cryptoCurrency)
|
repository.fetchPairs(
|
||||||
|
userWallet = userWallet,
|
||||||
|
currency = currency,
|
||||||
|
country = country,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
)
|
||||||
}.mapLeft(errorResolver::resolve)
|
}.mapLeft(errorResolver::resolve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,14 +5,16 @@ import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||||
import com.tangem.domain.tokens.model.Amount
|
import com.tangem.domain.tokens.model.Amount
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
class OnrampFetchQuotesUseCase(
|
class OnrampFetchQuotesUseCase(
|
||||||
private val repository: OnrampRepository,
|
private val repository: OnrampRepository,
|
||||||
private val errorResolver: OnrampErrorResolver,
|
private val errorResolver: OnrampErrorResolver,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(amount: Amount, cryptoCurrency: CryptoCurrency) = Either.catch {
|
suspend operator fun invoke(userWallet: UserWallet, amount: Amount, cryptoCurrency: CryptoCurrency) = Either.catch {
|
||||||
repository.fetchQuotes(
|
repository.fetchQuotes(
|
||||||
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
amount = amount,
|
amount = amount,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import com.tangem.domain.onramp.model.*
|
||||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||||
import com.tangem.domain.tokens.model.Amount
|
import com.tangem.domain.tokens.model.Amount
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
|
|
@ -13,15 +13,21 @@ interface OnrampRepository {
|
||||||
fun getCurrencies(): Flow<List<OnrampCurrency>>
|
fun getCurrencies(): Flow<List<OnrampCurrency>>
|
||||||
fun getCountries(): Flow<List<OnrampCountry>>
|
fun getCountries(): Flow<List<OnrampCountry>>
|
||||||
suspend fun getCountriesSync(): List<OnrampCountry>?
|
suspend fun getCountriesSync(): List<OnrampCountry>?
|
||||||
suspend fun getCountryByIp(): OnrampCountry
|
suspend fun getCountryByIp(userWallet: UserWallet): OnrampCountry
|
||||||
suspend fun getStatus(txId: String): OnrampStatus
|
suspend fun getStatus(userWallet: UserWallet, txId: String): OnrampStatus
|
||||||
suspend fun fetchCurrencies()
|
suspend fun fetchCurrencies(userWallet: UserWallet)
|
||||||
suspend fun fetchCountries(): List<OnrampCountry>
|
suspend fun fetchCountries(userWallet: UserWallet): List<OnrampCountry>
|
||||||
suspend fun fetchPaymentMethodsIfAbsent()
|
suspend fun fetchPaymentMethodsIfAbsent(userWallet: UserWallet)
|
||||||
suspend fun fetchPairs(currency: OnrampCurrency, country: OnrampCountry, cryptoCurrency: CryptoCurrency)
|
suspend fun fetchPairs(
|
||||||
suspend fun fetchQuotes(cryptoCurrency: CryptoCurrency, amount: Amount)
|
userWallet: UserWallet,
|
||||||
|
currency: OnrampCurrency,
|
||||||
|
country: OnrampCountry,
|
||||||
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun fetchQuotes(userWallet: UserWallet, cryptoCurrency: CryptoCurrency, amount: Amount)
|
||||||
suspend fun getOnrampData(
|
suspend fun getOnrampData(
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
quote: OnrampProviderWithQuote.Data,
|
quote: OnrampProviderWithQuote.Data,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||||
import com.tangem.domain.onramp.model.OnrampCountry
|
import com.tangem.domain.onramp.model.OnrampCountry
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
internal interface ConfirmResidencyComponent : ComposableBottomSheetComponent {
|
internal interface ConfirmResidencyComponent : ComposableBottomSheetComponent {
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
val cryptoCurrency: CryptoCurrency,
|
val cryptoCurrency: CryptoCurrency,
|
||||||
val country: OnrampCountry,
|
val country: OnrampCountry,
|
||||||
val onDismiss: () -> Unit,
|
val onDismiss: () -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ internal class DefaultConfirmResidencyComponent @AssistedInject constructor(
|
||||||
is ConfirmResidencyBottomSheetConfig.SelectCountry -> selectCountryComponentFactory.create(
|
is ConfirmResidencyBottomSheetConfig.SelectCountry -> selectCountryComponentFactory.create(
|
||||||
context = childByContext(componentContext),
|
context = childByContext(componentContext),
|
||||||
params = SelectCountryComponent.Params(
|
params = SelectCountryComponent.Params(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
onDismiss = { isCountrySelected ->
|
onDismiss = { isCountrySelected ->
|
||||||
// Dismiss country select sheet
|
// Dismiss country select sheet
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
||||||
is OnrampMainBottomSheetConfig.ConfirmResidency -> confirmResidencyComponentFactory.create(
|
is OnrampMainBottomSheetConfig.ConfirmResidency -> confirmResidencyComponentFactory.create(
|
||||||
context = childByContext(componentContext),
|
context = childByContext(componentContext),
|
||||||
params = ConfirmResidencyComponent.Params(
|
params = ConfirmResidencyComponent.Params(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
country = config.country,
|
country = config.country,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
|
|
@ -65,6 +66,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
||||||
is OnrampMainBottomSheetConfig.CurrenciesList -> selectCurrencyComponentFactory.create(
|
is OnrampMainBottomSheetConfig.CurrenciesList -> selectCurrencyComponentFactory.create(
|
||||||
context = childByContext(componentContext),
|
context = childByContext(componentContext),
|
||||||
params = SelectCurrencyComponent.Params(
|
params = SelectCurrencyComponent.Params(
|
||||||
|
userWallet = model.userWallet,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
) : Model(), OnrampIntents {
|
) : Model(), OnrampIntents {
|
||||||
|
|
||||||
private val params: OnrampMainComponent.Params = paramsContainer.require()
|
private val params: OnrampMainComponent.Params = paramsContainer.require()
|
||||||
|
|
||||||
|
val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||||
|
|
||||||
private val stateFactory = OnrampStateFactory(
|
private val stateFactory = OnrampStateFactory(
|
||||||
currentStateProvider = Provider { _state.value },
|
currentStateProvider = Provider { _state.value },
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
|
|
@ -68,7 +71,6 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
onrampIntents = this,
|
onrampIntents = this,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
)
|
)
|
||||||
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
|
||||||
private val _state: MutableStateFlow<OnrampMainComponentUM> = MutableStateFlow(
|
private val _state: MutableStateFlow<OnrampMainComponentUM> = MutableStateFlow(
|
||||||
value = stateFactory.getInitialState(
|
value = stateFactory.getInitialState(
|
||||||
currency = params.cryptoCurrency.name,
|
currency = params.cryptoCurrency.name,
|
||||||
|
|
@ -107,7 +109,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
|
|
||||||
private fun checkResidenceCountry() {
|
private fun checkResidenceCountry() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
checkOnrampAvailabilityUseCase()
|
checkOnrampAvailabilityUseCase(userWallet)
|
||||||
.onRight(::handleOnrampAvailability)
|
.onRight(::handleOnrampAvailability)
|
||||||
.onLeft(::handleOnrampError)
|
.onLeft(::handleOnrampError)
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +160,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
if (!state?.amountBlockState?.amountFieldModel?.fiatValue.isNullOrEmpty()) {
|
if (!state?.amountBlockState?.amountFieldModel?.fiatValue.isNullOrEmpty()) {
|
||||||
_state.update { amountStateFactory.getAmountSecondaryLoadingState() }
|
_state.update { amountStateFactory.getAmountSecondaryLoadingState() }
|
||||||
}
|
}
|
||||||
fetchPairsUseCase.invoke(params.cryptoCurrency).fold(
|
fetchPairsUseCase.invoke(userWallet, params.cryptoCurrency).fold(
|
||||||
ifLeft = ::handleOnrampError,
|
ifLeft = ::handleOnrampError,
|
||||||
ifRight = { _state.update { amountStateFactory.getAmountSecondaryResetState() } },
|
ifRight = { _state.update { amountStateFactory.getAmountSecondaryResetState() } },
|
||||||
)
|
)
|
||||||
|
|
@ -184,6 +186,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
val content = state.value as? OnrampMainComponentUM.Content ?: return@runCatching
|
val content = state.value as? OnrampMainComponentUM.Content ?: return@runCatching
|
||||||
if (content.amountBlockState.amountFieldModel.fiatAmount.value.isNullOrZero()) return@runCatching
|
if (content.amountBlockState.amountFieldModel.fiatAmount.value.isNullOrZero()) return@runCatching
|
||||||
fetchQuotesUseCase.invoke(
|
fetchQuotesUseCase.invoke(
|
||||||
|
userWallet = userWallet,
|
||||||
amount = content.amountBlockState.amountFieldModel.fiatAmount,
|
amount = content.amountBlockState.amountFieldModel.fiatAmount,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
).onLeft(::handleOnrampError)
|
).onLeft(::handleOnrampError)
|
||||||
|
|
@ -216,7 +219,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBuyClick(quote: OnrampProviderWithQuote.Data) {
|
override fun onBuyClick(quote: OnrampProviderWithQuote.Data) {
|
||||||
if (isDemoCardUseCase.invoke(selectedUserWallet.cardId)) {
|
if (isDemoCardUseCase.invoke(userWallet.cardId)) {
|
||||||
showDemoWarning()
|
showDemoWarning()
|
||||||
} else {
|
} else {
|
||||||
val currentContentState = state.value as? OnrampMainComponentUM.Content ?: return
|
val currentContentState = state.value as? OnrampMainComponentUM.Content ?: return
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import com.tangem.core.ui.extensions.wrappedList
|
||||||
import com.tangem.core.ui.message.DialogMessage
|
import com.tangem.core.ui.message.DialogMessage
|
||||||
import com.tangem.domain.onramp.GetOnrampRedirectUrlUseCase
|
import com.tangem.domain.onramp.GetOnrampRedirectUrlUseCase
|
||||||
import com.tangem.domain.onramp.model.error.OnrampError
|
import com.tangem.domain.onramp.model.error.OnrampError
|
||||||
|
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||||
import com.tangem.features.onramp.impl.R
|
import com.tangem.features.onramp.impl.R
|
||||||
import com.tangem.features.onramp.redirect.OnrampRedirectComponent
|
import com.tangem.features.onramp.redirect.OnrampRedirectComponent
|
||||||
import com.tangem.features.onramp.redirect.entity.OnrampRedirectTopBarUM
|
import com.tangem.features.onramp.redirect.entity.OnrampRedirectTopBarUM
|
||||||
|
|
@ -29,6 +30,7 @@ internal class OnrampRedirectModel @Inject constructor(
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val urlOpener: UrlOpener,
|
private val urlOpener: UrlOpener,
|
||||||
private val getOnrampRedirectUrlUseCase: GetOnrampRedirectUrlUseCase,
|
private val getOnrampRedirectUrlUseCase: GetOnrampRedirectUrlUseCase,
|
||||||
|
private val getWalletsUseCase: GetWalletsUseCase,
|
||||||
private val messageSender: UiMessageSender,
|
private val messageSender: UiMessageSender,
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
router: Router,
|
router: Router,
|
||||||
|
|
@ -36,6 +38,8 @@ internal class OnrampRedirectModel @Inject constructor(
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
private val params: OnrampRedirectComponent.Params = paramsContainer.require()
|
private val params: OnrampRedirectComponent.Params = paramsContainer.require()
|
||||||
|
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||||
|
|
||||||
val state = OnrampRedirectUM(
|
val state = OnrampRedirectUM(
|
||||||
topBarConfig = OnrampRedirectTopBarUM(
|
topBarConfig = OnrampRedirectTopBarUM(
|
||||||
title = combinedReference(
|
title = combinedReference(
|
||||||
|
|
@ -62,7 +66,7 @@ internal class OnrampRedirectModel @Inject constructor(
|
||||||
fun getRedirectUrl(isDarkTheme: Boolean) {
|
fun getRedirectUrl(isDarkTheme: Boolean) {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
getOnrampRedirectUrlUseCase.invoke(
|
getOnrampRedirectUrlUseCase.invoke(
|
||||||
userWalletId = params.userWalletId,
|
userWallet = selectedUserWallet,
|
||||||
quote = params.onrampProviderWithQuote,
|
quote = params.onrampProviderWithQuote,
|
||||||
cryptoCurrency = params.cryptoCurrency,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,9 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
|
||||||
OnrampChild.Settings -> settingsComponentFactory.create(
|
OnrampChild.Settings -> settingsComponentFactory.create(
|
||||||
context = childByContext(componentContext),
|
context = childByContext(componentContext),
|
||||||
params = OnrampSettingsComponent.Params(
|
params = OnrampSettingsComponent.Params(
|
||||||
params.cryptoCurrency,
|
userWalletId = params.userWalletId,
|
||||||
navigation::pop,
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
|
onBack = navigation::pop,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
OnrampChild.Main -> onrampMainComponentFactory.create(
|
OnrampChild.Main -> onrampMainComponentFactory.create(
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,15 @@ package com.tangem.features.onramp.selectcountry
|
||||||
import com.tangem.core.decompose.factory.ComponentFactory
|
import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
internal interface SelectCountryComponent : ComposableBottomSheetComponent {
|
internal interface SelectCountryComponent : ComposableBottomSheetComponent {
|
||||||
|
|
||||||
data class Params(val cryptoCurrency: CryptoCurrency, val onDismiss: (Boolean) -> Unit)
|
data class Params(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
val cryptoCurrency: CryptoCurrency,
|
||||||
|
val onDismiss: (Boolean) -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
interface Factory : ComponentFactory<Params, SelectCountryComponent>
|
interface Factory : ComponentFactory<Params, SelectCountryComponent>
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.onramp.GetOnrampCountryUseCase
|
||||||
import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase
|
import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase
|
||||||
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
|
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
|
||||||
import com.tangem.domain.onramp.model.OnrampCountry
|
import com.tangem.domain.onramp.model.OnrampCountry
|
||||||
|
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||||
import com.tangem.features.onramp.impl.R
|
import com.tangem.features.onramp.impl.R
|
||||||
import com.tangem.features.onramp.selectcountry.SelectCountryComponent
|
import com.tangem.features.onramp.selectcountry.SelectCountryComponent
|
||||||
import com.tangem.features.onramp.selectcountry.entity.CountryItemState
|
import com.tangem.features.onramp.selectcountry.entity.CountryItemState
|
||||||
|
|
@ -44,11 +45,15 @@ internal class OnrampSelectCountryModel @Inject constructor(
|
||||||
private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase,
|
private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase,
|
||||||
private val getOnrampCountryUseCase: GetOnrampCountryUseCase,
|
private val getOnrampCountryUseCase: GetOnrampCountryUseCase,
|
||||||
private val fetchOnrampCountriesUseCase: FetchOnrampCountriesUseCase,
|
private val fetchOnrampCountriesUseCase: FetchOnrampCountriesUseCase,
|
||||||
|
getWalletsUseCase: GetWalletsUseCase,
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
val state: StateFlow<CountryListUM> get() = controller.state
|
|
||||||
private val params: SelectCountryComponent.Params = paramsContainer.require()
|
private val params: SelectCountryComponent.Params = paramsContainer.require()
|
||||||
|
private val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||||
|
|
||||||
|
val state: StateFlow<CountryListUM> get() = controller.state
|
||||||
|
|
||||||
private val controller = CountryListUMController(
|
private val controller = CountryListUMController(
|
||||||
searchBarUM = createSearchBarUM(),
|
searchBarUM = createSearchBarUM(),
|
||||||
loadingItems = loadingItems,
|
loadingItems = loadingItems,
|
||||||
|
|
@ -103,7 +108,7 @@ internal class OnrampSelectCountryModel @Inject constructor(
|
||||||
|
|
||||||
private fun updateCountriesList() {
|
private fun updateCountriesList() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
fetchOnrampCountriesUseCase().onLeft {
|
fetchOnrampCountriesUseCase(userWallet).onLeft {
|
||||||
controller.update(UpdateCountryItemsErrorTransformer(onRetry = ::onRetry))
|
controller.update(UpdateCountryItemsErrorTransformer(onRetry = ::onRetry))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,15 @@ package com.tangem.features.onramp.selectcurrency
|
||||||
import com.tangem.core.decompose.factory.ComponentFactory
|
import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
||||||
internal interface SelectCurrencyComponent : ComposableBottomSheetComponent {
|
internal interface SelectCurrencyComponent : ComposableBottomSheetComponent {
|
||||||
|
|
||||||
data class Params(val cryptoCurrency: CryptoCurrency, val onDismiss: () -> Unit)
|
data class Params(
|
||||||
|
val userWallet: UserWallet,
|
||||||
|
val cryptoCurrency: CryptoCurrency,
|
||||||
|
val onDismiss: () -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
interface Factory : ComponentFactory<Params, SelectCurrencyComponent>
|
interface Factory : ComponentFactory<Params, SelectCurrencyComponent>
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ internal class OnrampSelectCurrencyModel @Inject constructor(
|
||||||
|
|
||||||
private fun updateCurrenciesList() {
|
private fun updateCurrenciesList() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
fetchOnrampCurrenciesUseCase().onLeft {
|
fetchOnrampCurrenciesUseCase(userWallet = params.userWallet).onLeft {
|
||||||
controller.update(UpdateCurrencyItemsErrorTransformer(onRetry = ::onRetry))
|
controller.update(UpdateCurrencyItemsErrorTransformer(onRetry = ::onRetry))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ internal class DefaultOnrampSettingsComponent @AssistedInject constructor(
|
||||||
OnrampSettingsConfig.SelectCountry -> selectCountryComponentFactory.create(
|
OnrampSettingsConfig.SelectCountry -> selectCountryComponentFactory.create(
|
||||||
context = childByContext(componentContext),
|
context = childByContext(componentContext),
|
||||||
params = SelectCountryComponent.Params(
|
params = SelectCountryComponent.Params(
|
||||||
params.cryptoCurrency,
|
userWalletId = params.userWalletId,
|
||||||
|
cryptoCurrency = params.cryptoCurrency,
|
||||||
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,15 @@ package com.tangem.features.onramp.settings
|
||||||
import com.tangem.core.decompose.factory.ComponentFactory
|
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.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
internal interface OnrampSettingsComponent : ComposableContentComponent {
|
internal interface OnrampSettingsComponent : ComposableContentComponent {
|
||||||
|
|
||||||
data class Params(val cryptoCurrency: CryptoCurrency, val onBack: () -> Unit)
|
data class Params(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
val cryptoCurrency: CryptoCurrency,
|
||||||
|
val onBack: () -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
interface Factory : ComponentFactory<Params, OnrampSettingsComponent>
|
interface Factory : ComponentFactory<Params, OnrampSettingsComponent>
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
getOnrampStatusUseCase(txId = transaction.txId)
|
getOnrampStatusUseCase(userWallet = userWallet, txId = transaction.txId)
|
||||||
.fold(
|
.fold(
|
||||||
ifLeft = { error ->
|
ifLeft = { error ->
|
||||||
analyticsEventHandler.sendOnrampErrorEvent(
|
analyticsEventHandler.sendOnrampErrorEvent(
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import com.tangem.domain.tokens.GetTokenListUseCase
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.tokens.model.TokenList
|
import com.tangem.domain.tokens.model.TokenList
|
||||||
|
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||||
import com.tangem.feature.swap.domain.GetAvailablePairsUseCase
|
import com.tangem.feature.swap.domain.GetAvailablePairsUseCase
|
||||||
import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo
|
import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo
|
||||||
import com.tangem.feature.swap.domain.models.domain.SwapPairLeast
|
import com.tangem.feature.swap.domain.models.domain.SwapPairLeast
|
||||||
|
|
@ -52,11 +53,13 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
||||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||||
private val getAvailablePairsUseCase: GetAvailablePairsUseCase,
|
private val getAvailablePairsUseCase: GetAvailablePairsUseCase,
|
||||||
|
private val getWalletsUseCase: GetWalletsUseCase,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
val state: StateFlow<TokenListUM> = tokenListUMController.state
|
val state: StateFlow<TokenListUM> = tokenListUMController.state
|
||||||
|
|
||||||
private var params: AvailableSwapPairsComponent.Params = paramsContainer.require()
|
private var params: AvailableSwapPairsComponent.Params = paramsContainer.require()
|
||||||
|
private val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||||
|
|
||||||
private val tokenListFlow = getTokenListUseCaseFlow()
|
private val tokenListFlow = getTokenListUseCaseFlow()
|
||||||
|
|
||||||
|
|
@ -212,6 +215,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
||||||
availablePairsByNetworkFlow.update(networkInfo = networkInfo, state = lceLoading())
|
availablePairsByNetworkFlow.update(networkInfo = networkInfo, state = lceLoading())
|
||||||
|
|
||||||
getAvailablePairsUseCase(
|
getAvailablePairsUseCase(
|
||||||
|
userWallet = userWallet,
|
||||||
initialCurrency = networkInfo,
|
initialCurrency = networkInfo,
|
||||||
currencies = statuses.map(CryptoCurrencyStatus::currency),
|
currencies = statuses.map(CryptoCurrencyStatus::currency),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,12 @@ import com.tangem.datasource.api.express.models.response.SwapPair
|
||||||
import com.tangem.datasource.api.express.models.response.SwapPairsWithProviders
|
import com.tangem.datasource.api.express.models.response.SwapPairsWithProviders
|
||||||
import com.tangem.datasource.api.express.models.response.TxDetails
|
import com.tangem.datasource.api.express.models.response.TxDetails
|
||||||
import com.tangem.datasource.crypto.DataSignatureVerifier
|
import com.tangem.datasource.crypto.DataSignatureVerifier
|
||||||
|
import com.tangem.datasource.exchangeservice.swap.ExpressUtils
|
||||||
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.feature.swap.converters.*
|
import com.tangem.feature.swap.converters.*
|
||||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||||
|
|
@ -48,6 +51,7 @@ internal class DefaultSwapRepository(
|
||||||
private val userWalletsListManager: UserWalletsListManager,
|
private val userWalletsListManager: UserWalletsListManager,
|
||||||
private val errorsDataConverter: ErrorsDataConverter,
|
private val errorsDataConverter: ErrorsDataConverter,
|
||||||
private val dataSignatureVerifier: DataSignatureVerifier,
|
private val dataSignatureVerifier: DataSignatureVerifier,
|
||||||
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
moshi: Moshi,
|
moshi: Moshi,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
) : SwapRepository {
|
) : SwapRepository {
|
||||||
|
|
@ -60,6 +64,7 @@ internal class DefaultSwapRepository(
|
||||||
private val txDetailsMoshiAdapter = moshi.adapter(TxDetails::class.java)
|
private val txDetailsMoshiAdapter = moshi.adapter(TxDetails::class.java)
|
||||||
|
|
||||||
override suspend fun getPairs(
|
override suspend fun getPairs(
|
||||||
|
userWallet: UserWallet,
|
||||||
initialCurrency: LeastTokenInfo,
|
initialCurrency: LeastTokenInfo,
|
||||||
currencyList: List<CryptoCurrency>,
|
currencyList: List<CryptoCurrency>,
|
||||||
): PairsWithProviders {
|
): PairsWithProviders {
|
||||||
|
|
@ -73,6 +78,7 @@ internal class DefaultSwapRepository(
|
||||||
|
|
||||||
val pairsDeferred = async {
|
val pairsDeferred = async {
|
||||||
getPairsInternal(
|
getPairsInternal(
|
||||||
|
userWallet = userWallet,
|
||||||
from = arrayListOf(initial),
|
from = arrayListOf(initial),
|
||||||
to = currenciesList,
|
to = currenciesList,
|
||||||
)
|
)
|
||||||
|
|
@ -80,6 +86,7 @@ internal class DefaultSwapRepository(
|
||||||
|
|
||||||
val reversedPairsDeferred = async {
|
val reversedPairsDeferred = async {
|
||||||
getPairsInternal(
|
getPairsInternal(
|
||||||
|
userWallet = userWallet,
|
||||||
from = currenciesList,
|
from = currenciesList,
|
||||||
to = arrayListOf(initial),
|
to = arrayListOf(initial),
|
||||||
)
|
)
|
||||||
|
|
@ -90,7 +97,13 @@ internal class DefaultSwapRepository(
|
||||||
|
|
||||||
val allPairs = pairs + reversedPairs
|
val allPairs = pairs + reversedPairs
|
||||||
|
|
||||||
val providers = tangemExpressApi.getProviders().getOrThrow()
|
val providers = tangemExpressApi.getProviders(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
).getOrThrow()
|
||||||
|
|
||||||
return@withContext swapPairInfoConverter.convert(
|
return@withContext swapPairInfoConverter.convert(
|
||||||
SwapPairsWithProviders(
|
SwapPairsWithProviders(
|
||||||
|
|
@ -109,6 +122,7 @@ internal class DefaultSwapRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getPairsOnly(
|
override suspend fun getPairsOnly(
|
||||||
|
userWallet: UserWallet,
|
||||||
initialCurrency: LeastTokenInfo,
|
initialCurrency: LeastTokenInfo,
|
||||||
currencyList: List<CryptoCurrency>,
|
currencyList: List<CryptoCurrency>,
|
||||||
): PairsWithProviders {
|
): PairsWithProviders {
|
||||||
|
|
@ -122,6 +136,7 @@ internal class DefaultSwapRepository(
|
||||||
|
|
||||||
val pairsDeferred = async {
|
val pairsDeferred = async {
|
||||||
getPairsInternal(
|
getPairsInternal(
|
||||||
|
userWallet = userWallet,
|
||||||
from = arrayListOf(initial),
|
from = arrayListOf(initial),
|
||||||
to = currenciesList,
|
to = currenciesList,
|
||||||
)
|
)
|
||||||
|
|
@ -129,6 +144,7 @@ internal class DefaultSwapRepository(
|
||||||
|
|
||||||
val reversedPairsDeferred = async {
|
val reversedPairsDeferred = async {
|
||||||
getPairsInternal(
|
getPairsInternal(
|
||||||
|
userWallet = userWallet,
|
||||||
from = currenciesList,
|
from = currenciesList,
|
||||||
to = arrayListOf(initial),
|
to = arrayListOf(initial),
|
||||||
)
|
)
|
||||||
|
|
@ -156,25 +172,42 @@ internal class DefaultSwapRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPairsInternal(
|
private suspend fun getPairsInternal(
|
||||||
|
userWallet: UserWallet,
|
||||||
from: List<NetworkLeastTokenInfo>,
|
from: List<NetworkLeastTokenInfo>,
|
||||||
to: List<NetworkLeastTokenInfo>,
|
to: List<NetworkLeastTokenInfo>,
|
||||||
): ApiResponse<List<SwapPair>> {
|
): ApiResponse<List<SwapPair>> {
|
||||||
return tangemExpressApi.getPairs(
|
return tangemExpressApi.getPairs(
|
||||||
PairsRequestBody(
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
body = PairsRequestBody(
|
||||||
from = from,
|
from = from,
|
||||||
to = to,
|
to = to,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getExchangeStatus(txId: String): Either<UnknownError, ExchangeStatusModel> {
|
override suspend fun getExchangeStatus(
|
||||||
|
userWallet: UserWallet,
|
||||||
|
txId: String,
|
||||||
|
): Either<UnknownError,
|
||||||
|
ExchangeStatusModel,> {
|
||||||
return withContext(coroutineDispatcher.io) {
|
return withContext(coroutineDispatcher.io) {
|
||||||
either {
|
either {
|
||||||
catch(
|
catch(
|
||||||
block = {
|
block = {
|
||||||
exchangeStatusConverter.convert(
|
exchangeStatusConverter.convert(
|
||||||
tangemExpressApi
|
tangemExpressApi
|
||||||
.getExchangeStatus(txId)
|
.getExchangeStatus(
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
txId = txId,
|
||||||
|
)
|
||||||
.getOrThrow(),
|
.getOrThrow(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -188,7 +221,7 @@ internal class DefaultSwapRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findBestQuote(
|
override suspend fun findBestQuote(
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
fromContractAddress: String,
|
fromContractAddress: String,
|
||||||
fromNetwork: String,
|
fromNetwork: String,
|
||||||
toContractAddress: String,
|
toContractAddress: String,
|
||||||
|
|
@ -211,6 +244,11 @@ internal class DefaultSwapRepository(
|
||||||
toDecimals = toDecimals,
|
toDecimals = toDecimals,
|
||||||
providerId = providerId,
|
providerId = providerId,
|
||||||
rateType = rateType.name.lowercase(),
|
rateType = rateType.name.lowercase(),
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
QuoteModel(
|
QuoteModel(
|
||||||
toTokenAmount = createFromAmountWithOffset(response.toAmount, response.toDecimals),
|
toTokenAmount = createFromAmountWithOffset(response.toAmount, response.toDecimals),
|
||||||
|
|
@ -223,6 +261,7 @@ internal class DefaultSwapRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getExchangeData(
|
override suspend fun getExchangeData(
|
||||||
|
userWallet: UserWallet,
|
||||||
fromContractAddress: String,
|
fromContractAddress: String,
|
||||||
fromNetwork: String,
|
fromNetwork: String,
|
||||||
toContractAddress: String,
|
toContractAddress: String,
|
||||||
|
|
@ -255,6 +294,11 @@ internal class DefaultSwapRepository(
|
||||||
requestId = requestId,
|
requestId = requestId,
|
||||||
refundAddress = refundAddress,
|
refundAddress = refundAddress,
|
||||||
refundExtraId = refundExtraId,
|
refundExtraId = refundExtraId,
|
||||||
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
if (dataSignatureVerifier.verifySignature(response.signature, response.txDetailsJson)) {
|
if (dataSignatureVerifier.verifySignature(response.signature, response.txDetailsJson)) {
|
||||||
val txDetails = parseTxDetails(response.txDetailsJson)
|
val txDetails = parseTxDetails(response.txDetailsJson)
|
||||||
|
|
@ -281,6 +325,7 @@ internal class DefaultSwapRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun exchangeSent(
|
override suspend fun exchangeSent(
|
||||||
|
userWallet: UserWallet,
|
||||||
txId: String,
|
txId: String,
|
||||||
fromNetwork: String,
|
fromNetwork: String,
|
||||||
fromAddress: String,
|
fromAddress: String,
|
||||||
|
|
@ -290,7 +335,12 @@ internal class DefaultSwapRepository(
|
||||||
): Either<ExpressDataError, Unit> = withContext(coroutineDispatcher.io) {
|
): Either<ExpressDataError, Unit> = withContext(coroutineDispatcher.io) {
|
||||||
try {
|
try {
|
||||||
tangemExpressApi.exchangeSent(
|
tangemExpressApi.exchangeSent(
|
||||||
ExchangeSentRequestBody(
|
userWalletId = userWallet.walletId.stringValue,
|
||||||
|
refCode = ExpressUtils.getRefCode(
|
||||||
|
userWallet = userWallet,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
),
|
||||||
|
body = ExchangeSentRequestBody(
|
||||||
txId = txId,
|
txId = txId,
|
||||||
fromNetwork = fromNetwork,
|
fromNetwork = fromNetwork,
|
||||||
fromAddress = fromAddress,
|
fromAddress = fromAddress,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectList
|
import com.tangem.datasource.local.preferences.utils.getObjectList
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
|
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.feature.swap.converters.SavedSwapTransactionListConverter
|
import com.tangem.feature.swap.converters.SavedSwapTransactionListConverter
|
||||||
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
||||||
|
|
@ -80,9 +80,8 @@ internal class DefaultSwapTransactionRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getTransactions(
|
override suspend fun getTransactions(
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
cryptoCurrencyId: CryptoCurrency.ID,
|
cryptoCurrencyId: CryptoCurrency.ID,
|
||||||
scanResponse: ScanResponse,
|
|
||||||
): Flow<List<SavedSwapTransactionListModel>?> {
|
): Flow<List<SavedSwapTransactionListModel>?> {
|
||||||
return withContext(dispatchers.io) {
|
return withContext(dispatchers.io) {
|
||||||
val txStatuses = appPreferencesStore.getObjectMapSync<ExchangeStatusModel>(
|
val txStatuses = appPreferencesStore.getObjectMapSync<ExchangeStatusModel>(
|
||||||
|
|
@ -93,7 +92,7 @@ internal class DefaultSwapTransactionRepository(
|
||||||
).map { savedTransactions ->
|
).map { savedTransactions ->
|
||||||
val currencyTxs = savedTransactions
|
val currencyTxs = savedTransactions
|
||||||
?.filter {
|
?.filter {
|
||||||
it.userWalletId == userWalletId.stringValue &&
|
it.userWalletId == userWallet.walletId.stringValue &&
|
||||||
(
|
(
|
||||||
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
|
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
|
||||||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
|
it.fromCryptoCurrencyId == cryptoCurrencyId.value
|
||||||
|
|
@ -103,7 +102,7 @@ internal class DefaultSwapTransactionRepository(
|
||||||
currencyTxs?.mapNotNull {
|
currencyTxs?.mapNotNull {
|
||||||
converter.convertBack(
|
converter.convertBack(
|
||||||
value = it,
|
value = it,
|
||||||
scanResponse = scanResponse,
|
scanResponse = userWallet.scanResponse,
|
||||||
txStatuses = txStatuses,
|
txStatuses = txStatuses,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ internal class SwapDataModule {
|
||||||
errorsDataConverter: ErrorsDataConverter,
|
errorsDataConverter: ErrorsDataConverter,
|
||||||
@NetworkMoshi moshi: Moshi,
|
@NetworkMoshi moshi: Moshi,
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
|
appPreferencesStore: AppPreferencesStore,
|
||||||
): SwapRepository {
|
): SwapRepository {
|
||||||
return DefaultSwapRepository(
|
return DefaultSwapRepository(
|
||||||
tangemExpressApi = tangemExpressApi,
|
tangemExpressApi = tangemExpressApi,
|
||||||
|
|
@ -46,6 +47,7 @@ internal class SwapDataModule {
|
||||||
dataSignatureVerifier = dataSignature,
|
dataSignatureVerifier = dataSignature,
|
||||||
moshi = moshi,
|
moshi = moshi,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
|
appPreferencesStore = appPreferencesStore,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.swap.domain.api
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.feature.swap.domain.models.ExpressDataError
|
import com.tangem.feature.swap.domain.models.ExpressDataError
|
||||||
import com.tangem.feature.swap.domain.models.domain.*
|
import com.tangem.feature.swap.domain.models.domain.*
|
||||||
|
|
@ -9,16 +10,24 @@ import java.math.BigDecimal
|
||||||
|
|
||||||
interface SwapRepository {
|
interface SwapRepository {
|
||||||
|
|
||||||
suspend fun getPairs(initialCurrency: LeastTokenInfo, currencyList: List<CryptoCurrency>): PairsWithProviders
|
suspend fun getPairs(
|
||||||
|
userWallet: UserWallet,
|
||||||
|
initialCurrency: LeastTokenInfo,
|
||||||
|
currencyList: List<CryptoCurrency>,
|
||||||
|
): PairsWithProviders
|
||||||
|
|
||||||
/** Express getPairs request variant without providers request */
|
/** Express getPairs request variant without providers request */
|
||||||
suspend fun getPairsOnly(initialCurrency: LeastTokenInfo, currencyList: List<CryptoCurrency>): PairsWithProviders
|
suspend fun getPairsOnly(
|
||||||
|
userWallet: UserWallet,
|
||||||
|
initialCurrency: LeastTokenInfo,
|
||||||
|
currencyList: List<CryptoCurrency>,
|
||||||
|
): PairsWithProviders
|
||||||
|
|
||||||
suspend fun getExchangeStatus(txId: String): Either<UnknownError, ExchangeStatusModel>
|
suspend fun getExchangeStatus(userWallet: UserWallet, txId: String): Either<UnknownError, ExchangeStatusModel>
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
suspend fun findBestQuote(
|
suspend fun findBestQuote(
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
fromContractAddress: String,
|
fromContractAddress: String,
|
||||||
fromNetwork: String,
|
fromNetwork: String,
|
||||||
toContractAddress: String,
|
toContractAddress: String,
|
||||||
|
|
@ -43,6 +52,7 @@ interface SwapRepository {
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
suspend fun getExchangeData(
|
suspend fun getExchangeData(
|
||||||
|
userWallet: UserWallet,
|
||||||
fromContractAddress: String,
|
fromContractAddress: String,
|
||||||
fromNetwork: String,
|
fromNetwork: String,
|
||||||
toContractAddress: String,
|
toContractAddress: String,
|
||||||
|
|
@ -61,6 +71,7 @@ interface SwapRepository {
|
||||||
// TODO: Add target error handling, remove either ([REDACTED_JIRA])
|
// TODO: Add target error handling, remove either ([REDACTED_JIRA])
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
suspend fun exchangeSent(
|
suspend fun exchangeSent(
|
||||||
|
userWallet: UserWallet,
|
||||||
txId: String,
|
txId: String,
|
||||||
fromNetwork: String,
|
fromNetwork: String,
|
||||||
fromAddress: String,
|
fromAddress: String,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.feature.swap.domain
|
package com.tangem.feature.swap.domain
|
||||||
|
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||||
import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo
|
import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo
|
||||||
import com.tangem.feature.swap.domain.models.domain.SwapPairLeast
|
import com.tangem.feature.swap.domain.models.domain.SwapPairLeast
|
||||||
|
|
@ -10,9 +11,14 @@ class GetAvailablePairsUseCase(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
|
userWallet: UserWallet,
|
||||||
initialCurrency: LeastTokenInfo,
|
initialCurrency: LeastTokenInfo,
|
||||||
currencies: List<CryptoCurrency>,
|
currencies: List<CryptoCurrency>,
|
||||||
): List<SwapPairLeast> {
|
): List<SwapPairLeast> {
|
||||||
return swapRepository.getPairsOnly(initialCurrency, currencies).pairs
|
return swapRepository.getPairsOnly(
|
||||||
|
userWallet = userWallet,
|
||||||
|
initialCurrency = initialCurrency,
|
||||||
|
currencyList = currencies,
|
||||||
|
).pairs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -114,6 +114,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val pairsLeast = getPairs(
|
val pairsLeast = getPairs(
|
||||||
|
userWallet = userWallet,
|
||||||
initialCurrency = LeastTokenInfo(
|
initialCurrency = LeastTokenInfo(
|
||||||
contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress ?: "0",
|
contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress ?: "0",
|
||||||
network = currency.network.backendId,
|
network = currency.network.backendId,
|
||||||
|
|
@ -197,10 +198,15 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPairs(
|
private suspend fun getPairs(
|
||||||
|
userWallet: UserWallet,
|
||||||
initialCurrency: LeastTokenInfo,
|
initialCurrency: LeastTokenInfo,
|
||||||
currenciesList: List<CryptoCurrency>,
|
currenciesList: List<CryptoCurrency>,
|
||||||
): PairsWithProviders {
|
): PairsWithProviders {
|
||||||
return repository.getPairs(initialCurrency, currenciesList)
|
return repository.getPairs(
|
||||||
|
userWallet = userWallet,
|
||||||
|
initialCurrency = initialCurrency,
|
||||||
|
currencyList = currenciesList,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun givePermissionToSwap(
|
override suspend fun givePermissionToSwap(
|
||||||
|
|
@ -308,7 +314,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
isBalanceWithoutFeeEnough: Boolean,
|
isBalanceWithoutFeeEnough: Boolean,
|
||||||
): Pair<SwapProvider, SwapState> {
|
): Pair<SwapProvider, SwapState> {
|
||||||
val maybeQuotes = repository.findBestQuote(
|
val maybeQuotes = repository.findBestQuote(
|
||||||
userWalletId = userWalletId,
|
userWallet = userWallet,
|
||||||
fromContractAddress = fromToken.currency.getContractAddress(),
|
fromContractAddress = fromToken.currency.getContractAddress(),
|
||||||
fromNetwork = fromToken.currency.network.backendId,
|
fromNetwork = fromToken.currency.network.backendId,
|
||||||
toContractAddress = toToken.currency.getContractAddress(),
|
toContractAddress = toToken.currency.getContractAddress(),
|
||||||
|
|
@ -619,6 +625,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
return result.fold(
|
return result.fold(
|
||||||
ifRight = { txHash ->
|
ifRight = { txHash ->
|
||||||
repository.exchangeSent(
|
repository.exchangeSent(
|
||||||
|
userWallet = userWallet,
|
||||||
txId = swapData.transaction.txId,
|
txId = swapData.transaction.txId,
|
||||||
fromNetwork = currencyToSendStatus.currency.network.backendId,
|
fromNetwork = currencyToSendStatus.currency.network.backendId,
|
||||||
fromAddress = currencyToSendStatus.value.networkAddress?.defaultAddress?.value.orEmpty(),
|
fromAddress = currencyToSendStatus.value.networkAddress?.defaultAddress?.value.orEmpty(),
|
||||||
|
|
@ -674,6 +681,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
swapProvider: SwapProvider,
|
swapProvider: SwapProvider,
|
||||||
): SwapTransactionState {
|
): SwapTransactionState {
|
||||||
val exchangeData = repository.getExchangeData(
|
val exchangeData = repository.getExchangeData(
|
||||||
|
userWallet = userWallet,
|
||||||
fromContractAddress = currencyToSend.currency.getContractAddress(),
|
fromContractAddress = currencyToSend.currency.getContractAddress(),
|
||||||
fromNetwork = currencyToSend.currency.network.backendId,
|
fromNetwork = currencyToSend.currency.network.backendId,
|
||||||
toContractAddress = currencyToGet.currency.getContractAddress(),
|
toContractAddress = currencyToGet.currency.getContractAddress(),
|
||||||
|
|
@ -726,6 +734,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
ifLeft = { SwapTransactionState.Error.TransactionError(it) },
|
ifLeft = { SwapTransactionState.Error.TransactionError(it) },
|
||||||
ifRight = { txHash ->
|
ifRight = { txHash ->
|
||||||
repository.exchangeSent(
|
repository.exchangeSent(
|
||||||
|
userWallet = userWallet,
|
||||||
txId = exchangeDataCex.txId,
|
txId = exchangeDataCex.txId,
|
||||||
fromNetwork = currencyToSend.currency.network.backendId,
|
fromNetwork = currencyToSend.currency.network.backendId,
|
||||||
fromAddress = currencyToSend.value.networkAddress?.defaultAddress?.value.orEmpty(),
|
fromAddress = currencyToSend.value.networkAddress?.defaultAddress?.value.orEmpty(),
|
||||||
|
|
@ -1132,7 +1141,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val quotes = repository.findBestQuote(
|
val quotes = repository.findBestQuote(
|
||||||
userWalletId = userWalletId,
|
userWallet = userWallet,
|
||||||
fromContractAddress = fromToken.getContractAddress(),
|
fromContractAddress = fromToken.getContractAddress(),
|
||||||
fromNetwork = fromToken.network.backendId,
|
fromNetwork = fromToken.network.backendId,
|
||||||
toContractAddress = toToken.getContractAddress(),
|
toContractAddress = toToken.getContractAddress(),
|
||||||
|
|
@ -1388,6 +1397,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
selectedFee: FeeType,
|
selectedFee: FeeType,
|
||||||
): SwapState {
|
): SwapState {
|
||||||
return repository.getExchangeData(
|
return repository.getExchangeData(
|
||||||
|
userWallet = userWallet,
|
||||||
fromContractAddress = fromToken.currency.getContractAddress(),
|
fromContractAddress = fromToken.currency.getContractAddress(),
|
||||||
fromNetwork = fromToken.currency.network.backendId,
|
fromNetwork = fromToken.currency.network.backendId,
|
||||||
toContractAddress = toToken.currency.getContractAddress(),
|
toContractAddress = toToken.currency.getContractAddress(),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.feature.swap.domain
|
package com.tangem.feature.swap.domain
|
||||||
|
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
|
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
|
||||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
|
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
|
||||||
|
|
@ -18,9 +18,8 @@ interface SwapTransactionRepository {
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun getTransactions(
|
suspend fun getTransactions(
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
cryptoCurrencyId: CryptoCurrency.ID,
|
cryptoCurrencyId: CryptoCurrency.ID,
|
||||||
scanResponse: ScanResponse,
|
|
||||||
): Flow<List<SavedSwapTransactionListModel>?>
|
): Flow<List<SavedSwapTransactionListModel>?>
|
||||||
|
|
||||||
suspend fun removeTransaction(
|
suspend fun removeTransaction(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import arrow.core.getOrElse
|
||||||
import com.tangem.blockchain.common.address.AddressType
|
import com.tangem.blockchain.common.address.AddressType
|
||||||
import com.tangem.common.routing.AppRoute
|
import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.common.routing.AppRouter
|
import com.tangem.common.routing.AppRouter
|
||||||
|
import com.tangem.common.ui.bottomsheet.receive.AddressModel
|
||||||
|
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
|
||||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
|
|
@ -17,8 +19,6 @@ import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.decompose.ui.UiMessageSender
|
import com.tangem.core.decompose.ui.UiMessageSender
|
||||||
import com.tangem.core.navigation.share.ShareManager
|
import com.tangem.core.navigation.share.ShareManager
|
||||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||||
import com.tangem.common.ui.bottomsheet.receive.AddressModel
|
|
||||||
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
|
|
||||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
|
@ -166,7 +166,7 @@ internal class TokenDetailsModel @Inject constructor(
|
||||||
appCurrencyProvider = Provider { selectedAppCurrencyFlow.value },
|
appCurrencyProvider = Provider { selectedAppCurrencyFlow.value },
|
||||||
currentStateProvider = Provider { uiState.value },
|
currentStateProvider = Provider { uiState.value },
|
||||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||||
userWalletId = userWalletId,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.Quote
|
import com.tangem.domain.tokens.model.Quote
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
||||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
|
||||||
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
||||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||||
import com.tangem.feature.swap.domain.models.domain.*
|
import com.tangem.feature.swap.domain.models.domain.*
|
||||||
|
|
@ -29,7 +28,6 @@ import kotlinx.collections.immutable.PersistentList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
|
@ -39,14 +37,13 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
private val quotesRepository: QuotesRepository,
|
private val quotesRepository: QuotesRepository,
|
||||||
private val quotesRepositoryV2: QuotesRepositoryV2,
|
private val quotesRepositoryV2: QuotesRepositoryV2,
|
||||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
|
||||||
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
|
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
|
||||||
private val swapTransactionStatusStore: SwapTransactionStatusStore,
|
private val swapTransactionStatusStore: SwapTransactionStatusStore,
|
||||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||||
@Assisted private val clickIntents: TokenDetailsClickIntents,
|
@Assisted private val clickIntents: TokenDetailsClickIntents,
|
||||||
@Assisted private val appCurrencyProvider: Provider<AppCurrency>,
|
@Assisted private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
@Assisted private val currentStateProvider: Provider<TokenDetailsState>,
|
@Assisted private val currentStateProvider: Provider<TokenDetailsState>,
|
||||||
@Assisted private val userWalletId: UserWalletId,
|
@Assisted private val userWallet: UserWallet,
|
||||||
@Assisted private val cryptoCurrency: CryptoCurrency,
|
@Assisted private val cryptoCurrency: CryptoCurrency,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -60,14 +57,9 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
|
suspend operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
|
||||||
val selectedWallet = getSelectedWalletSyncUseCase().fold(
|
|
||||||
ifLeft = { return emptyFlow() },
|
|
||||||
ifRight = { it },
|
|
||||||
)
|
|
||||||
return swapTransactionRepository.getTransactions(
|
return swapTransactionRepository.getTransactions(
|
||||||
userWalletId = userWalletId,
|
userWallet = userWallet,
|
||||||
cryptoCurrencyId = cryptoCurrency.id,
|
cryptoCurrencyId = cryptoCurrency.id,
|
||||||
scanResponse = selectedWallet.scanResponse,
|
|
||||||
).conflate()
|
).conflate()
|
||||||
.map { savedTransactions ->
|
.map { savedTransactions ->
|
||||||
val quotes = savedTransactions
|
val quotes = savedTransactions
|
||||||
|
|
@ -91,7 +83,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
val shouldDispose = selectedTx.activeStatus?.isAutoDisposable == true || isForceDispose
|
val shouldDispose = selectedTx.activeStatus?.isAutoDisposable == true || isForceDispose
|
||||||
if (shouldDispose) {
|
if (shouldDispose) {
|
||||||
swapTransactionRepository.removeTransaction(
|
swapTransactionRepository.removeTransaction(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWallet.walletId,
|
||||||
fromCryptoCurrency = selectedTx.fromCryptoCurrency,
|
fromCryptoCurrency = selectedTx.fromCryptoCurrency,
|
||||||
toCryptoCurrency = selectedTx.toCryptoCurrency,
|
toCryptoCurrency = selectedTx.toCryptoCurrency,
|
||||||
txId = selectedTx.info.txId,
|
txId = selectedTx.info.txId,
|
||||||
|
|
@ -117,7 +109,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getExchangeStatus(txId: String, provider: SwapProvider): ExchangeStatusModel? {
|
private suspend fun getExchangeStatus(txId: String, provider: SwapProvider): ExchangeStatusModel? {
|
||||||
return swapRepository.getExchangeStatus(txId)
|
return swapRepository.getExchangeStatus(userWallet = userWallet, txId = txId)
|
||||||
.fold(
|
.fold(
|
||||||
ifLeft = { null },
|
ifLeft = { null },
|
||||||
ifRight = { statusModel ->
|
ifRight = { statusModel ->
|
||||||
|
|
@ -157,7 +149,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
val refundContractAddress = status.refundContractAddress
|
val refundContractAddress = status.refundContractAddress
|
||||||
if (refundNetwork != null && refundContractAddress != null) {
|
if (refundNetwork != null && refundContractAddress != null) {
|
||||||
return addCryptoCurrenciesUseCase(
|
return addCryptoCurrenciesUseCase(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWallet.walletId,
|
||||||
contractAddress = refundContractAddress,
|
contractAddress = refundContractAddress,
|
||||||
networkId = refundNetwork,
|
networkId = refundNetwork,
|
||||||
).getOrNull()
|
).getOrNull()
|
||||||
|
|
@ -219,7 +211,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
clickIntents: TokenDetailsClickIntents,
|
clickIntents: TokenDetailsClickIntents,
|
||||||
appCurrencyProvider: Provider<AppCurrency>,
|
appCurrencyProvider: Provider<AppCurrency>,
|
||||||
currentStateProvider: Provider<TokenDetailsState>,
|
currentStateProvider: Provider<TokenDetailsState>,
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
): ExchangeStatusFactory
|
): ExchangeStatusFactory
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||||
|
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotification
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotification
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
|
|
@ -36,7 +36,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
||||||
@Assisted private val clickIntents: TokenDetailsClickIntents,
|
@Assisted private val clickIntents: TokenDetailsClickIntents,
|
||||||
@Assisted private val cryptoCurrency: CryptoCurrency,
|
@Assisted private val cryptoCurrency: CryptoCurrency,
|
||||||
@Assisted appCurrencyProvider: Provider<AppCurrency>,
|
@Assisted appCurrencyProvider: Provider<AppCurrency>,
|
||||||
@Assisted userWalletId: UserWalletId,
|
@Assisted userWallet: UserWallet,
|
||||||
@Assisted cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
@Assisted cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||||
|
|
@ -49,7 +49,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
||||||
clickIntents = clickIntents,
|
clickIntents = clickIntents,
|
||||||
appCurrencyProvider = appCurrencyProvider,
|
appCurrencyProvider = appCurrencyProvider,
|
||||||
currentStateProvider = currentStateProvider,
|
currentStateProvider = currentStateProvider,
|
||||||
userWalletId = userWalletId,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
||||||
appCurrencyProvider = appCurrencyProvider,
|
appCurrencyProvider = appCurrencyProvider,
|
||||||
clickIntents = clickIntents,
|
clickIntents = clickIntents,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
userWalletId = userWalletId,
|
userWallet = userWallet,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,7 +210,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
||||||
clickIntents: TokenDetailsClickIntents,
|
clickIntents: TokenDetailsClickIntents,
|
||||||
appCurrencyProvider: Provider<AppCurrency>,
|
appCurrencyProvider: Provider<AppCurrency>,
|
||||||
currentStateProvider: Provider<TokenDetailsState>,
|
currentStateProvider: Provider<TokenDetailsState>,
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||||
): ExpressStatusFactory
|
): ExpressStatusFactory
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ import com.tangem.domain.onramp.model.OnrampStatus.Status.*
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter
|
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
|
|
@ -39,7 +39,7 @@ internal class OnrampStatusFactory @AssistedInject constructor(
|
||||||
@Assisted private val appCurrencyProvider: Provider<AppCurrency>,
|
@Assisted private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
@Assisted private val clickIntents: TokenDetailsClickIntents,
|
@Assisted private val clickIntents: TokenDetailsClickIntents,
|
||||||
@Assisted private val cryptoCurrency: CryptoCurrency,
|
@Assisted private val cryptoCurrency: CryptoCurrency,
|
||||||
@Assisted private val userWalletId: UserWalletId,
|
@Assisted private val userWallet: UserWallet,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val onrampTransactionStateConverter by lazy(LazyThreadSafetyMode.NONE) {
|
private val onrampTransactionStateConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
|
@ -54,7 +54,7 @@ internal class OnrampStatusFactory @AssistedInject constructor(
|
||||||
|
|
||||||
operator fun invoke(): Flow<List<ExpressTransactionStateUM.OnrampUM>> {
|
operator fun invoke(): Flow<List<ExpressTransactionStateUM.OnrampUM>> {
|
||||||
return getOnrampTransactionsUseCase(
|
return getOnrampTransactionsUseCase(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWallet.walletId,
|
||||||
cryptoCurrencyId = cryptoCurrency.id,
|
cryptoCurrencyId = cryptoCurrency.id,
|
||||||
).map { maybeTransaction ->
|
).map { maybeTransaction ->
|
||||||
maybeTransaction.fold(
|
maybeTransaction.fold(
|
||||||
|
|
@ -82,7 +82,7 @@ internal class OnrampStatusFactory @AssistedInject constructor(
|
||||||
return if (onrampTx.activeStatus.isTerminal) {
|
return if (onrampTx.activeStatus.isTerminal) {
|
||||||
onrampTx
|
onrampTx
|
||||||
} else {
|
} else {
|
||||||
getOnrampStatusUseCase(onrampTx.info.txId).fold(
|
getOnrampStatusUseCase(userWallet = userWallet, onrampTx.info.txId).fold(
|
||||||
ifLeft = {
|
ifLeft = {
|
||||||
Timber.e("Couldn't update onramp status. $it")
|
Timber.e("Couldn't update onramp status. $it")
|
||||||
onrampTx
|
onrampTx
|
||||||
|
|
@ -150,7 +150,7 @@ internal class OnrampStatusFactory @AssistedInject constructor(
|
||||||
appCurrencyProvider: Provider<AppCurrency>,
|
appCurrencyProvider: Provider<AppCurrency>,
|
||||||
clickIntents: TokenDetailsClickIntents,
|
clickIntents: TokenDetailsClickIntents,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
userWalletId: UserWalletId,
|
userWallet: UserWallet,
|
||||||
): OnrampStatusFactory
|
): OnrampStatusFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import com.tangem.domain.nft.FetchNFTCollectionsUseCase
|
||||||
import com.tangem.domain.settings.*
|
import com.tangem.domain.settings.*
|
||||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||||
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
|
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||||
|
|
@ -107,9 +108,7 @@ internal class WalletModel @Inject constructor(
|
||||||
subscribeToUserWalletsUpdates()
|
subscribeToUserWalletsUpdates()
|
||||||
subscribeOnBalanceHiding()
|
subscribeOnBalanceHiding()
|
||||||
subscribeOnSelectedWalletFlow()
|
subscribeOnSelectedWalletFlow()
|
||||||
subscribeToScreenBackgroundState()
|
|
||||||
subscribeOnPushNotificationsPermission()
|
subscribeOnPushNotificationsPermission()
|
||||||
subscribeOnExpressTransactionsUpdates()
|
|
||||||
subscribeOnNFTUpdates()
|
subscribeOnNFTUpdates()
|
||||||
|
|
||||||
clickIntents.initialize(innerWalletRouter, modelScope)
|
clickIntents.initialize(innerWalletRouter, modelScope)
|
||||||
|
|
@ -223,6 +222,8 @@ internal class WalletModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
walletDeepLinksHandler.registerForWallet(scope = modelScope, userWallet = selectedWallet)
|
walletDeepLinksHandler.registerForWallet(scope = modelScope, userWallet = selectedWallet)
|
||||||
|
subscribeOnExpressTransactionsUpdates(selectedWallet)
|
||||||
|
subscribeToScreenBackgroundState(selectedWallet)
|
||||||
}
|
}
|
||||||
.flowOn(dispatchers.main)
|
.flowOn(dispatchers.main)
|
||||||
.launchIn(modelScope)
|
.launchIn(modelScope)
|
||||||
|
|
@ -231,7 +232,7 @@ internal class WalletModel @Inject constructor(
|
||||||
|
|
||||||
// We need to update the current wallet quotes if the application was in the background for more than 10 seconds
|
// We need to update the current wallet quotes if the application was in the background for more than 10 seconds
|
||||||
// and then returned to the foreground
|
// and then returned to the foreground
|
||||||
private fun subscribeToScreenBackgroundState() {
|
private fun subscribeToScreenBackgroundState(userWallet: UserWallet) {
|
||||||
screenLifecycleProvider.isBackgroundState
|
screenLifecycleProvider.isBackgroundState
|
||||||
.onEach { isBackground ->
|
.onEach { isBackground ->
|
||||||
expressTxStatusTaskScheduler.cancelTask()
|
expressTxStatusTaskScheduler.cancelTask()
|
||||||
|
|
@ -241,30 +242,31 @@ internal class WalletModel @Inject constructor(
|
||||||
isBackground -> needToRefreshTimer()
|
isBackground -> needToRefreshTimer()
|
||||||
needToRefreshWallet && !isBackground -> {
|
needToRefreshWallet && !isBackground -> {
|
||||||
triggerRefreshWalletQuotes()
|
triggerRefreshWalletQuotes()
|
||||||
subscribeOnExpressTransactionsUpdates()
|
subscribeOnExpressTransactionsUpdates(userWallet)
|
||||||
}
|
}
|
||||||
!isBackground -> subscribeOnExpressTransactionsUpdates()
|
!isBackground -> subscribeOnExpressTransactionsUpdates(userWallet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.launchIn(modelScope)
|
.launchIn(modelScope)
|
||||||
|
.saveIn(expressStatusJobHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun subscribeOnExpressTransactionsUpdates() {
|
private fun subscribeOnExpressTransactionsUpdates(userWallet: UserWallet) {
|
||||||
modelScope.launch(dispatchers.main) {
|
expressTxStatusTaskScheduler.cancelTask()
|
||||||
expressTxStatusTaskScheduler.cancelTask()
|
expressTxStatusTaskScheduler.scheduleTask(
|
||||||
expressTxStatusTaskScheduler.scheduleTask(
|
modelScope,
|
||||||
modelScope,
|
PeriodicTask(
|
||||||
PeriodicTask(
|
isDelayFirst = false,
|
||||||
isDelayFirst = false,
|
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
||||||
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
task = {
|
||||||
task = {
|
runCatching {
|
||||||
runCatching { onrampStatusFactory.updateOnrmapTransactionStatuses() }
|
onrampStatusFactory.updateOnrmapTransactionStatuses(userWallet)
|
||||||
},
|
}
|
||||||
onSuccess = { /* no-op */ },
|
},
|
||||||
onError = { /* no-op */ },
|
onSuccess = { /* no-op */ },
|
||||||
),
|
onError = { /* no-op */ },
|
||||||
)
|
),
|
||||||
}.saveIn(expressStatusJobHolder)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import com.tangem.domain.onramp.OnrampUpdateTransactionStatusUseCase
|
||||||
import com.tangem.domain.onramp.model.OnrampStatus
|
import com.tangem.domain.onramp.model.OnrampStatus
|
||||||
import com.tangem.domain.onramp.model.OnrampStatus.Status.*
|
import com.tangem.domain.onramp.model.OnrampStatus.Status.*
|
||||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -40,22 +41,22 @@ internal class OnrampStatusFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateOnrmapTransactionStatuses() = withContext(dispatchers.io) {
|
suspend fun updateOnrmapTransactionStatuses(userWallet: UserWallet) = withContext(dispatchers.io) {
|
||||||
val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content
|
val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content
|
||||||
?: return@withContext
|
?: return@withContext
|
||||||
|
|
||||||
singleWalletState.expressTxs.map { tx ->
|
singleWalletState.expressTxs.map { tx ->
|
||||||
async {
|
async {
|
||||||
if (tx is ExpressTransactionStateUM.OnrampUM) {
|
if (tx is ExpressTransactionStateUM.OnrampUM) {
|
||||||
updateOnrampTxStatus(tx)
|
updateOnrampTxStatus(userWallet, tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.awaitAll()
|
}.awaitAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateOnrampTxStatus(onrampTx: ExpressTransactionStateUM.OnrampUM) {
|
private suspend fun updateOnrampTxStatus(userWallet: UserWallet, onrampTx: ExpressTransactionStateUM.OnrampUM) {
|
||||||
if (!onrampTx.activeStatus.isTerminal) {
|
if (!onrampTx.activeStatus.isTerminal) {
|
||||||
getOnrampStatusUseCase(onrampTx.info.txId).fold(
|
getOnrampStatusUseCase(userWallet = userWallet, onrampTx.info.txId).fold(
|
||||||
ifLeft = {
|
ifLeft = {
|
||||||
Timber.e("Couldn't update onramp status. $it")
|
Timber.e("Couldn't update onramp status. $it")
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
package com.tangem.lib.auth
|
package com.tangem.lib.auth
|
||||||
|
|
||||||
interface ExpressAuthProvider {
|
interface ExpressAuthProvider {
|
||||||
|
|
||||||
fun getUserId(): String
|
|
||||||
|
|
||||||
fun getSessionId(): String
|
fun getSessionId(): String
|
||||||
|
|
||||||
fun getRefCode(): String
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue