Updated on 2026-08-14

This commit is contained in:
Tangem 2022-04-06 18:52:29 +03:00
parent 9b5bc7bdff
commit 608121ab50
9 changed files with 277 additions and 1 deletions

1
network/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

23
network/build.gradle Normal file
View file

@ -0,0 +1,23 @@
plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
// Tangem sdk's
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-140'
// Network
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-moshi:2.6.0'
implementation 'com.squareup.moshi:moshi:1.13.0'
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
}

View file

@ -0,0 +1,50 @@
package com.tangem.network.api.tangemTech
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
data class CoinsPricesResponse(
val prices: List<CoinPrice>
)
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
)
}
}
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,
)
}

View file

@ -0,0 +1,30 @@
package com.tangem.network.api.tangemTech
import com.tangem.common.services.Result
import retrofit2.http.GET
import retrofit2.http.Query
/**
[REDACTED_AUTHOR]
*/
interface TangemTechApi {
@GET("coins/prices")
suspend fun coinsPrices(
@Query("currency") currency: String,
@Query("ids") ids: List<String>,
): Result<CoinsPricesResponse>
@GET("coins/check-address")
suspend fun coinsCheckAddress(
@Query("contractAddress") contractAddress: String,
@Query("networkId") networkId: String? = null,
): Result<CoinsCheckAddressResponse>
@GET("coins/currencies")
suspend fun coinsCurrencies(): Result<CoinsCurrenciesResponse>
@GET("coins/tokens")
suspend fun coinsTokens(): Result<CoinsCurrenciesResponse>
}

View file

@ -0,0 +1,66 @@
package com.tangem.network.api.tangemTech
import com.tangem.common.services.Result
import com.tangem.network.common.AddHeaderInterceptor
import com.tangem.network.common.CacheHttpInterceptor
import com.tangem.network.common.createRetrofitInstance
/**
[REDACTED_AUTHOR]
*/
class TangemTechService {
private val headerInterceptors = mutableListOf<AddHeaderInterceptor>(
CacheHttpInterceptor(cacheMaxAge)
)
private var api: TangemTechApi = createApi()
suspend fun coinsPrices(
currency: String,
ids: List<String>
): Result<CoinsPricesResponse> {
return api.coinsPrices(currency, ids)
}
suspend fun coinsCheckAddress(
contractAddress: String,
networkId: String? = null
): Result<CoinsCheckAddressResponse> {
return api.coinsCheckAddress(contractAddress, networkId)
}
suspend fun coinsCurrencies(): Result<CoinsCurrenciesResponse> {
return api.coinsCurrencies()
}
suspend fun coinsTokens(): Result<CoinsCurrenciesResponse> {
return api.coinsTokens()
}
fun addHeaderInterceptors(interceptors: List<AddHeaderInterceptor>) {
headerInterceptors.removeAll(interceptors)
headerInterceptors.addAll(interceptors)
api = createApi()
}
private fun createApi(): TangemTechApi {
val retrofit = createRetrofitInstance(
baseUrl = baseUrl,
interceptors = headerInterceptors.toList()
)
return retrofit.create(TangemTechApi::class.java)
}
companion object {
const val baseUrl = "https://api.tangem-tech.com/"
const val cacheMaxAge = 600
}
}
class TangemAuthInterceptor(
private val cardPublicKeyHex: String
) : AddHeaderInterceptor(
mapOf("card_public_key" to cardPublicKeyHex)
)

View file

@ -0,0 +1,26 @@
package com.tangem.network.common
import okhttp3.Interceptor
import okhttp3.Response
/**
[REDACTED_AUTHOR]
*/
open class AddHeaderInterceptor(
private val headers: Map<String, String>
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder().apply {
headers.forEach {
addHeader(it.key, it.value)
}
}.build()
return chain.proceed(request)
}
}
class CacheHttpInterceptor(
maxAgeSeconds: Int
) : AddHeaderInterceptor(mapOf("Cache-Control" to "max-age=$maxAgeSeconds"))

View file

@ -0,0 +1,38 @@
package com.tangem.network.common
import com.squareup.moshi.FromJson
import com.squareup.moshi.Moshi
import com.squareup.moshi.ToJson
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.common.json.TangemSdkAdapter
import retrofit2.Converter
import retrofit2.converter.moshi.MoshiConverterFactory
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
class MoshiConverter {
companion object {
fun createFactory(moshi: Moshi = defaultMoshi()): Converter.Factory = MoshiConverterFactory.create(moshi)
fun defaultMoshi(): Moshi = Moshi.Builder()
.add(BigDecimalAdapter)
.add(KotlinJsonAdapterFactory())
.add(TangemSdkAdapter.DerivationPathAdapter())
.add(TangemSdkAdapter.DerivationNodeAdapter())
.build()
fun sdkMoshi(): Moshi = MoshiJsonConverter.INSTANCE.moshi
}
}
object BigDecimalAdapter {
@FromJson
fun fromJson(string: String) = BigDecimal(string)
@ToJson
fun toJson(value: BigDecimal) = value.toString()
}

View file

@ -0,0 +1,41 @@
package com.tangem.network.common
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Converter
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
// TODO: refactoring: make it better through factory
fun createRetrofitInstance(
baseUrl: String,
okHttpBuilder: OkHttpClient.Builder = OkHttpClient.Builder(),
interceptors: List<Interceptor> = emptyList(),
converterFactory: Converter.Factory = MoshiConverter.createFactory(),
logEnabled: Boolean = false
): Retrofit {
interceptors.forEach { okHttpBuilder.addInterceptor(it) }
addTimeOuts(okHttpBuilder)
if (logEnabled) okHttpBuilder.addInterceptor(createHttpLoggingInterceptor())
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(converterFactory)
.client(okHttpBuilder.build())
.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
}
}

View file

@ -1 +1,2 @@
include ':app'
include ':app'
include ':network'