Updated on 2026-08-14
This commit is contained in:
parent
c779af8173
commit
ee8c426ca9
9 changed files with 161 additions and 137 deletions
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.network.coinmarketcap
|
||||
|
||||
import com.tangem.network.common.AddHeaderInterceptor
|
||||
import com.tangem.network.common.createRetrofitInstance
|
||||
import okhttp3.Interceptor
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
|
@ -9,9 +9,9 @@ interface CoinMarketCapApi {
|
|||
|
||||
@GET("v1/tools/price-conversion")
|
||||
suspend fun getRateInfo(
|
||||
@Query("amount") amount: Int,
|
||||
@Query("symbol") cryptoCurrencyName: String,
|
||||
@Query("convert") fiatCurrencyName: String? = null
|
||||
@Query("amount") amount: Int,
|
||||
@Query("symbol") cryptoCurrencyName: String,
|
||||
@Query("convert") fiatCurrencyName: String? = null
|
||||
): RateInfoResponse
|
||||
|
||||
@GET("v1/fiat/map")
|
||||
|
|
@ -23,15 +23,11 @@ interface CoinMarketCapApi {
|
|||
|
||||
fun create(apiKey: String): CoinMarketCapApi {
|
||||
return createRetrofitInstance(
|
||||
baseUrl,
|
||||
interceptors = listOf(createCoinMarketRequestInterceptor(apiKey)),
|
||||
baseUrl = baseUrl,
|
||||
interceptors = listOf(
|
||||
AddHeaderInterceptor(mapOf("X-CMC_PRO_API_KEY" to apiKey)),
|
||||
),
|
||||
).create(CoinMarketCapApi::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCoinMarketRequestInterceptor(apiKey: String): Interceptor = Interceptor { chain ->
|
||||
val requestBuilder = chain.request().newBuilder()
|
||||
requestBuilder.addHeader("X-CMC_PRO_API_KEY", apiKey)
|
||||
chain.proceed(requestBuilder.build())
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.tap.network.exchangeServices.onramper
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
|
||||
|
|
@ -23,15 +21,6 @@ interface OnramperApi {
|
|||
}
|
||||
}
|
||||
|
||||
class AddKeyToHeaderInterceptor(
|
||||
private val key: String
|
||||
) : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request().newBuilder().addHeader("Authorization", "Basic $key").build()
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GatewaysResponse(
|
||||
val gateways: List<OnramperGateway>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.net.Uri
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.network.common.AddHeaderInterceptor
|
||||
import com.tangem.network.common.createRetrofitInstance
|
||||
import com.tangem.tap.common.extensions.urlEncode
|
||||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
|
|
@ -25,7 +26,9 @@ class OnramperService(
|
|||
private val api: OnramperApi by lazy {
|
||||
createRetrofitInstance(
|
||||
baseUrl = OnramperApi.BASE_URL,
|
||||
interceptors = listOf(AddKeyToHeaderInterceptor(apiKey))
|
||||
interceptors = listOf(
|
||||
AddHeaderInterceptor(mapOf("Authorization" to "Basic $apiKey")),
|
||||
)
|
||||
).create(OnramperApi::class.java)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.domain.features.addCustomToken
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.network.api.tangemTech.CoinsCheckAddressResponse
|
||||
import com.tangem.network.api.tangemTech.TangemAuthInterceptor
|
||||
import com.tangem.network.api.tangemTech.Coins
|
||||
import com.tangem.network.api.tangemTech.TangemTechService
|
||||
import com.tangem.network.common.AddHeaderInterceptor
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -12,20 +12,52 @@ class AddCustomTokenManager(
|
|||
private val tangemTechService: TangemTechService
|
||||
) {
|
||||
|
||||
suspend fun findContractAddress(
|
||||
suspend fun checkAddress(
|
||||
contractAddress: String,
|
||||
networkId: String? = null
|
||||
): List<CoinsCheckAddressResponse.Token> {
|
||||
val result = tangemTechService.coinsCheckAddress(contractAddress, networkId)
|
||||
): List<Coins.CheckAddressResponse.Token> {
|
||||
val result = tangemTechService.coins.checkAddress(contractAddress, networkId)
|
||||
return when (result) {
|
||||
is Result.Success -> {
|
||||
result.data.tokens
|
||||
val resultTokens = result.data.tokens
|
||||
val newTokensList = mutableListOf<Coins.CheckAddressResponse.Token>()
|
||||
resultTokens.forEach {
|
||||
val contractsWithTheSameAddress = it.contracts.filter { it.address == contractAddress }
|
||||
if (contractsWithTheSameAddress.isNotEmpty()) {
|
||||
val newToken = it.copy(contracts = contractsWithTheSameAddress)
|
||||
newTokensList.add(newToken)
|
||||
}
|
||||
}
|
||||
when {
|
||||
// https://tangem.slack.com/archives/GMXC6PP71/p1649672562078679
|
||||
newTokensList.size > 1 -> listOf(newTokensList[0])
|
||||
else -> newTokensList
|
||||
}
|
||||
}
|
||||
is Result.Failure -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun tokens(): List<Coins.TokensResponse.Token> {
|
||||
val result = tangemTechService.coins.tokens()
|
||||
return when (result) {
|
||||
is Result.Success -> {
|
||||
val currencies = result.data.tokens
|
||||
currencies.filter {
|
||||
it.contracts.isNullOrEmpty()
|
||||
}
|
||||
}
|
||||
is Result.Failure -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun attachAuthKey(authKey: String) {
|
||||
tangemTechService.addHeaderInterceptors(listOf(TangemAuthInterceptor(authKey)))
|
||||
tangemTechService.addHeaderInterceptors(listOf(
|
||||
CardPublicKeyHttpInterceptor(authKey),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CardPublicKeyHttpInterceptor(cardPublicKeyHex: String) : AddHeaderInterceptor(mapOf(
|
||||
"card_public_key" to cardPublicKeyHex,
|
||||
))
|
||||
|
|
@ -5,46 +5,57 @@ import java.math.BigDecimal
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class CoinsPricesResponse(
|
||||
val prices: List<CoinPrice>
|
||||
)
|
||||
interface HttpResponse
|
||||
interface TangemTechResponse : HttpResponse
|
||||
|
||||
data class CoinPrice(
|
||||
val name: String,
|
||||
val price: BigDecimal,
|
||||
)
|
||||
|
||||
data class CoinsCheckAddressResponse(
|
||||
val imageHost: String,
|
||||
val tokens: List<Token>,
|
||||
val total: Int,
|
||||
) {
|
||||
data class Token(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val active: Boolean,
|
||||
val contracts: List<Contract>
|
||||
) {
|
||||
data class Contract(
|
||||
val networkId: String,
|
||||
val address: String,
|
||||
val decimalCount: BigDecimal?,
|
||||
val active: Boolean
|
||||
sealed class Coins : TangemTechResponse {
|
||||
data class PricesResponse(val prices: List<Price>) : Coins() {
|
||||
data class Price(
|
||||
val name: String,
|
||||
val price: BigDecimal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class CoinsCurrenciesResponse(
|
||||
val currencies: List<Currency>,
|
||||
) {
|
||||
data class Currency(
|
||||
val id: String,
|
||||
val code: String,
|
||||
val name: String,
|
||||
val rateBTC: String,
|
||||
val unit: String,
|
||||
val type: String,
|
||||
)
|
||||
}
|
||||
data class CheckAddressResponse(val imageHost: String, val tokens: List<Token>, val total: Int) : Coins() {
|
||||
data class Token(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val active: Boolean,
|
||||
val contracts: List<Contract>
|
||||
) {
|
||||
data class Contract(
|
||||
val networkId: String,
|
||||
val address: String,
|
||||
val decimalCount: BigDecimal?,
|
||||
val active: Boolean
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class TokensResponse(val imageHost: String, val tokens: List<Token>, val total: Int) : Coins() {
|
||||
data class Token(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val contracts: List<Contract>?
|
||||
) {
|
||||
data class Contract(
|
||||
val networkId: String,
|
||||
val address: String,
|
||||
val decimalCount: BigDecimal?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class CurrenciesResponse(val currencies: List<Currency>) {
|
||||
data class Currency(
|
||||
val id: String,
|
||||
val code: String,
|
||||
val name: String,
|
||||
val rateBTC: String,
|
||||
val unit: String,
|
||||
val type: String,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.network.api.tangemTech
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
|
@ -13,18 +12,18 @@ interface TangemTechApi {
|
|||
suspend fun coinsPrices(
|
||||
@Query("currency") currency: String,
|
||||
@Query("ids") ids: List<String>,
|
||||
): Result<CoinsPricesResponse>
|
||||
): Coins.PricesResponse
|
||||
|
||||
@GET("coins/check-address")
|
||||
suspend fun coinsCheckAddress(
|
||||
@Query("contractAddress") contractAddress: String,
|
||||
@Query("networkId") networkId: String? = null,
|
||||
): Result<CoinsCheckAddressResponse>
|
||||
): Coins.CheckAddressResponse
|
||||
|
||||
@GET("coins/currencies")
|
||||
suspend fun coinsCurrencies(): Result<CoinsCurrenciesResponse>
|
||||
suspend fun coinsCurrencies(): Coins.CurrenciesResponse
|
||||
|
||||
@GET("coins/tokens")
|
||||
suspend fun coinsTokens(): Result<CoinsCurrenciesResponse>
|
||||
suspend fun coinsTokens(): Coins.TokensResponse
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.network.api.tangemTech
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.network.common.AddHeaderInterceptor
|
||||
import com.tangem.network.common.CacheHttpInterceptor
|
||||
import com.tangem.network.common.CacheControlHttpInterceptor
|
||||
import com.tangem.network.common.createRetrofitInstance
|
||||
|
||||
/**
|
||||
|
|
@ -10,50 +11,18 @@ import com.tangem.network.common.createRetrofitInstance
|
|||
*/
|
||||
class TangemTechService {
|
||||
|
||||
val coins: CoinsRoute = CoinsRoute()
|
||||
|
||||
private val techRoutes: List<TangemTechRoute> = listOf(
|
||||
coins
|
||||
)
|
||||
|
||||
private val headerInterceptors = mutableListOf<AddHeaderInterceptor>(
|
||||
CacheHttpInterceptor(cacheMaxAge)
|
||||
CacheControlHttpInterceptor(cacheMaxAge)
|
||||
)
|
||||
|
||||
private var api: TangemTechApi = createApi()
|
||||
|
||||
suspend fun coinsPrices(
|
||||
currency: String,
|
||||
ids: List<String>
|
||||
): Result<CoinsPricesResponse> {
|
||||
return try {
|
||||
api.coinsPrices(currency, ids)
|
||||
} catch (ex: Exception) {
|
||||
Result.Failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun coinsCheckAddress(
|
||||
contractAddress: String,
|
||||
networkId: String? = null
|
||||
): Result<CoinsCheckAddressResponse> {
|
||||
return try {
|
||||
api.coinsCheckAddress(contractAddress, networkId)
|
||||
} catch (ex: Exception) {
|
||||
Result.Failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun coinsCurrencies(): Result<CoinsCurrenciesResponse> {
|
||||
return try {
|
||||
api.coinsCurrencies()
|
||||
} catch (ex: Exception) {
|
||||
Result.Failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun coinsTokens(): Result<CoinsCurrenciesResponse> {
|
||||
return try {
|
||||
api.coinsTokens()
|
||||
} catch (ex: Exception) {
|
||||
Result.Failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
fun addHeaderInterceptors(interceptors: List<AddHeaderInterceptor>) {
|
||||
headerInterceptors.removeAll(interceptors)
|
||||
headerInterceptors.addAll(interceptors)
|
||||
|
|
@ -65,8 +34,9 @@ class TangemTechService {
|
|||
baseUrl = baseUrl,
|
||||
interceptors = headerInterceptors.toList()
|
||||
)
|
||||
|
||||
return retrofit.create(TangemTechApi::class.java)
|
||||
return retrofit.create(TangemTechApi::class.java).apply {
|
||||
techRoutes.forEach { it.setApi(this) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
@ -75,8 +45,36 @@ class TangemTechService {
|
|||
}
|
||||
}
|
||||
|
||||
class TangemAuthInterceptor(
|
||||
private val cardPublicKeyHex: String
|
||||
) : AddHeaderInterceptor(
|
||||
mapOf("card_public_key" to cardPublicKeyHex)
|
||||
)
|
||||
private interface TangemTechRoute {
|
||||
fun setApi(api: TangemTechApi)
|
||||
}
|
||||
|
||||
class CoinsRoute : TangemTechRoute {
|
||||
private lateinit var api: TangemTechApi
|
||||
|
||||
override fun setApi(api: TangemTechApi) {
|
||||
this.api = api
|
||||
}
|
||||
|
||||
suspend fun prices(
|
||||
currency: String,
|
||||
ids: List<String>
|
||||
): Result<Coins.PricesResponse> {
|
||||
return performRequest { api.coinsPrices(currency, ids) }
|
||||
}
|
||||
|
||||
suspend fun checkAddress(
|
||||
contractAddress: String,
|
||||
networkId: String? = null
|
||||
): Result<Coins.CheckAddressResponse> {
|
||||
return performRequest { api.coinsCheckAddress(contractAddress, networkId) }
|
||||
}
|
||||
|
||||
suspend fun currencies(): Result<Coins.CurrenciesResponse> {
|
||||
return performRequest { api.coinsCurrencies() }
|
||||
}
|
||||
|
||||
suspend fun tokens(): Result<Coins.TokensResponse> {
|
||||
return performRequest { api.coinsTokens() }
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,6 @@ open class AddHeaderInterceptor(
|
|||
}
|
||||
}
|
||||
|
||||
class CacheHttpInterceptor(
|
||||
maxAgeSeconds: Int
|
||||
) : AddHeaderInterceptor(mapOf("Cache-Control" to "max-age=$maxAgeSeconds"))
|
||||
class CacheControlHttpInterceptor(maxAgeSeconds: Int) : AddHeaderInterceptor(mapOf(
|
||||
"Cache-Control" to "max-age=$maxAgeSeconds",
|
||||
))
|
||||
|
|
@ -15,8 +15,13 @@ fun createRetrofitInstance(
|
|||
converterFactory: Converter.Factory = MoshiConverter.createFactory(),
|
||||
logEnabled: Boolean = false
|
||||
): Retrofit {
|
||||
okHttpBuilder.apply {
|
||||
callTimeout(10, TimeUnit.SECONDS)
|
||||
connectTimeout(20, TimeUnit.SECONDS)
|
||||
readTimeout(20, TimeUnit.SECONDS)
|
||||
writeTimeout(20, TimeUnit.SECONDS)
|
||||
}
|
||||
interceptors.forEach { okHttpBuilder.addInterceptor(it) }
|
||||
addTimeOuts(okHttpBuilder)
|
||||
|
||||
if (logEnabled) okHttpBuilder.addInterceptor(createHttpLoggingInterceptor())
|
||||
|
||||
|
|
@ -27,15 +32,6 @@ fun createRetrofitInstance(
|
|||
.build()
|
||||
}
|
||||
|
||||
private fun addTimeOuts(okHttpBuilder: OkHttpClient.Builder) {
|
||||
okHttpBuilder.callTimeout(1, TimeUnit.SECONDS)
|
||||
okHttpBuilder.connectTimeout(20, TimeUnit.SECONDS)
|
||||
okHttpBuilder.readTimeout(20, TimeUnit.SECONDS)
|
||||
okHttpBuilder.writeTimeout(20, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
||||
return HttpLoggingInterceptor().apply {
|
||||
level = HttpLoggingInterceptor.Level.BODY
|
||||
}
|
||||
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
|
||||
level = HttpLoggingInterceptor.Level.BODY
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue