diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt index 0de58a0b32..6836861c8c 100644 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt +++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt @@ -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()) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt index 01fec5d3d9..30a65ed706 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt index 15b7a73a5b..94bdae395e 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt @@ -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) } diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt index 7317484b19..dac29b2d3a 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenManager.kt @@ -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 { - val result = tangemTechService.coinsCheckAddress(contractAddress, networkId) + ): List { + val result = tangemTechService.coins.checkAddress(contractAddress, networkId) return when (result) { is Result.Success -> { - result.data.tokens + val resultTokens = result.data.tokens + val newTokensList = mutableListOf() + 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 { + 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), + )) } -} \ No newline at end of file +} + +private class CardPublicKeyHttpInterceptor(cardPublicKeyHex: String) : AddHeaderInterceptor(mapOf( + "card_public_key" to cardPublicKeyHex, +)) \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt index f1a5a921a9..6d73a3955f 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt @@ -5,46 +5,57 @@ import java.math.BigDecimal /** [REDACTED_AUTHOR] */ -data class CoinsPricesResponse( - val prices: List -) +interface HttpResponse +interface TangemTechResponse : HttpResponse -data class CoinPrice( - val name: String, - val price: BigDecimal, -) - -data class CoinsCheckAddressResponse( - val imageHost: String, - val tokens: List, - val total: Int, -) { - data class Token( - val id: String, - val name: String, - val symbol: String, - val active: Boolean, - val contracts: List - ) { - 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) : Coins() { + data class Price( + val name: String, + val price: BigDecimal, ) } -} -data class CoinsCurrenciesResponse( - val currencies: List, -) { - 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, val total: Int) : Coins() { + data class Token( + val id: String, + val name: String, + val symbol: String, + val active: Boolean, + val contracts: List + ) { + data class Contract( + val networkId: String, + val address: String, + val decimalCount: BigDecimal?, + val active: Boolean + ) + } + } + data class TokensResponse(val imageHost: String, val tokens: List, val total: Int) : Coins() { + data class Token( + val id: String, + val name: String, + val symbol: String, + val contracts: List? + ) { + data class Contract( + val networkId: String, + val address: String, + val decimalCount: BigDecimal?, + ) + } + } + + data class CurrenciesResponse(val currencies: List) { + data class Currency( + val id: String, + val code: String, + val name: String, + val rateBTC: String, + val unit: String, + val type: String, + ) + } +} \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt index fbb92c73f1..2917063e30 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt @@ -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, - ): Result + ): Coins.PricesResponse @GET("coins/check-address") suspend fun coinsCheckAddress( @Query("contractAddress") contractAddress: String, @Query("networkId") networkId: String? = null, - ): Result + ): Coins.CheckAddressResponse @GET("coins/currencies") - suspend fun coinsCurrencies(): Result + suspend fun coinsCurrencies(): Coins.CurrenciesResponse @GET("coins/tokens") - suspend fun coinsTokens(): Result + suspend fun coinsTokens(): Coins.TokensResponse } \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt index 4cb45db16b..10c0ceb3cc 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt @@ -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 = listOf( + coins + ) + private val headerInterceptors = mutableListOf( - CacheHttpInterceptor(cacheMaxAge) + CacheControlHttpInterceptor(cacheMaxAge) ) private var api: TangemTechApi = createApi() - suspend fun coinsPrices( - currency: String, - ids: List - ): Result { - return try { - api.coinsPrices(currency, ids) - } catch (ex: Exception) { - Result.Failure(ex) - } - } - - suspend fun coinsCheckAddress( - contractAddress: String, - networkId: String? = null - ): Result { - return try { - api.coinsCheckAddress(contractAddress, networkId) - } catch (ex: Exception) { - Result.Failure(ex) - } - } - - suspend fun coinsCurrencies(): Result { - return try { - api.coinsCurrencies() - } catch (ex: Exception) { - Result.Failure(ex) - } - } - - suspend fun coinsTokens(): Result { - return try { - api.coinsTokens() - } catch (ex: Exception) { - Result.Failure(ex) - } - } - fun addHeaderInterceptors(interceptors: List) { 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) -) \ No newline at end of file +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 + ): Result { + return performRequest { api.coinsPrices(currency, ids) } + } + + suspend fun checkAddress( + contractAddress: String, + networkId: String? = null + ): Result { + return performRequest { api.coinsCheckAddress(contractAddress, networkId) } + } + + suspend fun currencies(): Result { + return performRequest { api.coinsCurrencies() } + } + + suspend fun tokens(): Result { + return performRequest { api.coinsTokens() } + } +} \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/common/Interceptors.kt b/network/src/main/java/com/tangem/network/common/Interceptors.kt index dab4451281..dff259332e 100644 --- a/network/src/main/java/com/tangem/network/common/Interceptors.kt +++ b/network/src/main/java/com/tangem/network/common/Interceptors.kt @@ -21,6 +21,6 @@ open class AddHeaderInterceptor( } } -class CacheHttpInterceptor( - maxAgeSeconds: Int -) : AddHeaderInterceptor(mapOf("Cache-Control" to "max-age=$maxAgeSeconds")) \ No newline at end of file +class CacheControlHttpInterceptor(maxAgeSeconds: Int) : AddHeaderInterceptor(mapOf( + "Cache-Control" to "max-age=$maxAgeSeconds", +)) \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/common/Retrofit.kt b/network/src/main/java/com/tangem/network/common/Retrofit.kt index d6e9614290..86ab156c51 100644 --- a/network/src/main/java/com/tangem/network/common/Retrofit.kt +++ b/network/src/main/java/com/tangem/network/common/Retrofit.kt @@ -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 } \ No newline at end of file