Updated on 2026-08-14
This commit is contained in:
parent
e87d946e40
commit
a3a18854c8
48 changed files with 544 additions and 328 deletions
|
|
@ -48,10 +48,8 @@ internal class Express(
|
|||
|
||||
private fun createHeaders(isProd: Boolean) = buildMap {
|
||||
put(key = "api-key", value = ProviderSuspend { getApiKey(isProd) })
|
||||
put(key = "user-id", value = ProviderSuspend(expressAuthProvider::getUserId))
|
||||
put(key = "session-id", value = ProviderSuspend(expressAuthProvider::getSessionId))
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider).values)
|
||||
put(key = "refcode", value = ProviderSuspend(expressAuthProvider::getRefCode))
|
||||
}
|
||||
|
||||
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.PairsRequestBody
|
||||
import com.tangem.datasource.api.express.models.response.*
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.*
|
||||
|
||||
/**
|
||||
* Interface of Tangem Express API (new swap mechanism)
|
||||
|
|
@ -17,16 +14,29 @@ import retrofit2.http.Query
|
|||
interface TangemExpressApi {
|
||||
|
||||
@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")
|
||||
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")
|
||||
suspend fun getProviders(): ApiResponse<List<ExchangeProvider>>
|
||||
suspend fun getProviders(
|
||||
@Header("user-id") userWalletId: String,
|
||||
@Header("refcode") refCode: String,
|
||||
): ApiResponse<List<ExchangeProvider>>
|
||||
|
||||
@GET("exchange-quote")
|
||||
suspend fun getExchangeQuote(
|
||||
@Header("user-id") userWalletId: String,
|
||||
@Header("refcode") refCode: String,
|
||||
@Query("fromContractAddress") fromContractAddress: String,
|
||||
@Query("fromNetwork") fromNetwork: String,
|
||||
@Query("toContractAddress") toContractAddress: String,
|
||||
|
|
@ -40,6 +50,8 @@ interface TangemExpressApi {
|
|||
|
||||
@GET("exchange-data")
|
||||
suspend fun getExchangeData(
|
||||
@Header("user-id") userWalletId: String,
|
||||
@Header("refcode") refCode: String,
|
||||
@Query("fromContractAddress") fromContractAddress: String,
|
||||
@Query("fromNetwork") fromNetwork: String,
|
||||
@Query("toContractAddress") toContractAddress: String,
|
||||
|
|
@ -57,8 +69,16 @@ interface TangemExpressApi {
|
|||
): ApiResponse<ExchangeDataResponse>
|
||||
|
||||
@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")
|
||||
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.onramp.models.request.OnrampPairsRequest
|
||||
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.OnrampStatusResponse
|
||||
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.OnrampPairDTO
|
||||
import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.*
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
|
||||
interface OnrampApi {
|
||||
|
||||
@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")
|
||||
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")
|
||||
suspend fun getCountryByIp(): ApiResponse<OnrampCountryDTO>
|
||||
suspend fun getCountryByIp(
|
||||
@Header("user-id") userWalletId: String,
|
||||
@Header("refcode") refCode: String,
|
||||
): ApiResponse<OnrampCountryDTO>
|
||||
|
||||
@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")
|
||||
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")
|
||||
suspend fun getQuote(
|
||||
@Header("user-id") userWalletId: String,
|
||||
@Header("refcode") refCode: String,
|
||||
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
||||
@Query("fromPrecision") fromPrecision: Int,
|
||||
@Query("toContractAddress") toContractAddress: String,
|
||||
|
|
@ -47,6 +62,8 @@ interface OnrampApi {
|
|||
|
||||
@GET("onramp-data")
|
||||
suspend fun getData(
|
||||
@Header("user-id") userWalletId: String,
|
||||
@Header("refcode") refCode: String,
|
||||
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
||||
@Query("fromPrecision") fromPrecision: Int,
|
||||
@Query("toContractAddress") toContractAddress: String,
|
||||
|
|
@ -64,5 +81,9 @@ interface OnrampApi {
|
|||
): ApiResponse<OnrampDataResponse>
|
||||
|
||||
@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.LeastTokenInfo
|
||||
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.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.lceContent
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -33,31 +36,34 @@ typealias InitializationStatusFlow = MutableStateFlow<Lce<Throwable, List<Asset>
|
|||
internal class DefaultExpressServiceLoader @Inject constructor(
|
||||
private val tangemExpressApi: TangemExpressApi,
|
||||
private val expressAssetsStore: ExpressAssetsStore,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : ExpressServiceLoader {
|
||||
|
||||
private val initializationStatuses =
|
||||
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) {
|
||||
val initializationStatus = getInitializationStatusInternal(userWalletId)
|
||||
val initializationStatus = getInitializationStatusInternal(userWallet.walletId)
|
||||
|
||||
try {
|
||||
if (userTokens.isNotEmpty()) {
|
||||
val response = tangemExpressApi.getAssets(
|
||||
userWalletId = userWallet.walletId.stringValue,
|
||||
refCode = getRefCode(userWallet, appPreferencesStore),
|
||||
body = AssetsRequestBody(tokensList = userTokens),
|
||||
).getOrThrow()
|
||||
|
||||
expressAssetsStore.store(userWalletId, response)
|
||||
expressAssetsStore.store(userWallet.walletId, response)
|
||||
|
||||
initializationStatus.update { response.lceContent() }
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
if (expressAssetsStore.getSyncOrNull(userWalletId) == null) {
|
||||
if (expressAssetsStore.getSyncOrNull(userWallet.walletId) == null) {
|
||||
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.response.Asset
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
|
@ -13,8 +14,8 @@ import kotlinx.coroutines.flow.Flow
|
|||
*/
|
||||
interface ExpressServiceLoader {
|
||||
|
||||
/** Update service using [userWalletId] and [userTokens] */
|
||||
suspend fun update(userWalletId: UserWalletId, userTokens: List<LeastTokenInfo>)
|
||||
/** Update service using [userWallet] and [userTokens] */
|
||||
suspend fun update(userWallet: UserWallet, userTokens: List<LeastTokenInfo>)
|
||||
|
||||
/** Get initialization status by [userWalletId] */
|
||||
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
|
||||
fun setup() {
|
||||
every { appVersionProvider.versionName } returns VERSION_NAME
|
||||
every { expressAuthProvider.getUserId() } returns EXPRESS_USER_ID
|
||||
every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID
|
||||
every { expressAuthProvider.getRefCode() } returns EXPRESS_REF_CODE
|
||||
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
|
||||
every { appAuthProvider.getCardId() } returns APP_CARD_ID
|
||||
every { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
|
||||
|
|
@ -131,9 +129,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
|||
MockEnvironmentConfigStorage.EXPRESS_DEV_API_KEY
|
||||
}
|
||||
},
|
||||
"user-id" to ProviderSuspend { EXPRESS_USER_ID },
|
||||
"session-id" to ProviderSuspend { EXPRESS_SESSION_ID },
|
||||
"refcode" to ProviderSuspend { EXPRESS_REF_CODE },
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue