Updated on 2026-08-14
This commit is contained in:
commit
1f6cbc7ed0
537 changed files with 6377 additions and 12897 deletions
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.datasource.api.common.config
|
||||
|
||||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.utils.Provider
|
||||
|
|
@ -10,12 +10,12 @@ import com.tangem.utils.version.AppVersionProvider
|
|||
/**
|
||||
* Express [ApiConfig]
|
||||
*
|
||||
* @property configManager config manager
|
||||
* @property expressAuthProvider express auth provider
|
||||
* @property appVersionProvider app version provider
|
||||
* @property environmentConfigStorage environment config storage
|
||||
* @property expressAuthProvider express auth provider
|
||||
* @property appVersionProvider app version provider
|
||||
*/
|
||||
internal class Express(
|
||||
private val configManager: ConfigManager,
|
||||
private val environmentConfigStorage: EnvironmentConfigStorage,
|
||||
private val expressAuthProvider: ExpressAuthProvider,
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
) : ApiConfig() {
|
||||
|
|
@ -56,9 +56,9 @@ internal class Express(
|
|||
|
||||
private fun getApiKey(isProd: Boolean): String {
|
||||
return if (isProd) {
|
||||
configManager.config.express
|
||||
environmentConfigStorage.getConfigSync().express
|
||||
} else {
|
||||
configManager.config.devExpress
|
||||
environmentConfigStorage.getConfigSync().devExpress
|
||||
}
|
||||
?.apiKey
|
||||
?: error("No express config provided")
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ interface TangemTechMarketsApi {
|
|||
@Query("interval") interval: String,
|
||||
): ApiResponse<TokenMarketChartResponse>
|
||||
|
||||
@GET("coins/{coin_id}/exchanges")
|
||||
suspend fun getCoinExchanges(@Path("coin_id") coinId: String): ApiResponse<TokenMarketExchangesResponse>
|
||||
|
||||
@GET("coins/history_preview")
|
||||
suspend fun getCoinsListCharts(
|
||||
@Query("coin_ids") coinIds: String,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.datasource.api.markets.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Token market exchanges response
|
||||
*
|
||||
* @property exchanges list of exchanges
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TokenMarketExchangesResponse(
|
||||
@Json(name = "exchanges") val exchanges: List<Exchange>,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Exchange
|
||||
*
|
||||
* @property id id
|
||||
* @property name name
|
||||
* @property imageUrl image url
|
||||
* @property isCentralized CEX (true), DEX (false)
|
||||
* @property volumeInUsd aggregated volume in USD
|
||||
* @property trustScore trust score
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Exchange(
|
||||
@Json(name = "exchange_id") val id: String,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "image") val imageUrl: String?,
|
||||
@Json(name = "centralized") val isCentralized: Boolean,
|
||||
@Json(name = "volume_usd") val volumeInUsd: BigDecimal,
|
||||
@Json(name = "trust_score") val trustScore: Int?,
|
||||
)
|
||||
}
|
||||
|
|
@ -30,6 +30,8 @@ data class TokenMarketInfoResponse(
|
|||
val links: Links?,
|
||||
@Json(name = "price_performance")
|
||||
val pricePerformance: PricePerformance?,
|
||||
@Json(name = "exchanges_amount")
|
||||
val exchangesAmount: Int?,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
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.PaymentMethodDTO
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
|
||||
interface OnrampApi {
|
||||
|
||||
@GET("currencies")
|
||||
suspend fun getCurrencies(): ApiResponse<List<OnrampCurrencyDTO>>
|
||||
|
||||
@GET("countries")
|
||||
suspend fun getCountries(): ApiResponse<List<OnrampCountryDTO>>
|
||||
|
||||
@GET("country-by-ip")
|
||||
suspend fun getCountryByIp(): ApiResponse<OnrampCountryDTO>
|
||||
|
||||
@GET("payment-methods")
|
||||
suspend fun getPaymentMethods(): ApiResponse<List<PaymentMethodDTO>>
|
||||
|
||||
@POST("onramp-pairs")
|
||||
suspend fun getPairs(@Body body: OnrampPairsRequest): ApiResponse<List<OnrampPairDTO>>
|
||||
|
||||
@GET("onramp-quote")
|
||||
suspend fun getQuote(
|
||||
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
||||
@Query("toContractAddress") toContractAddress: String,
|
||||
@Query("toNetwork") toNetwork: String,
|
||||
@Query("paymentMethod") paymentMethod: String,
|
||||
@Query("countryCode") countryCode: String,
|
||||
@Query("fromAmount") fromAmount: String,
|
||||
@Query("toDecimals") toDecimals: Int,
|
||||
@Query("providerId") providerId: String,
|
||||
): ApiResponse<OnrampQuoteResponse>
|
||||
|
||||
@GET("onramp-data")
|
||||
suspend fun getData(
|
||||
@Query("fromCurrencyCode") fromCurrencyCode: String,
|
||||
@Query("toContractAddress") toContractAddress: String,
|
||||
@Query("toNetwork") toNetwork: String,
|
||||
@Query("paymentMethod") paymentMethod: String,
|
||||
@Query("countryCode") countryCode: String,
|
||||
@Query("fromAmount") fromAmount: String,
|
||||
@Query("toDecimals") toDecimals: Int,
|
||||
@Query("providerId") providerId: String,
|
||||
@Query("toAddress") toAddress: String,
|
||||
@Query("redirectUrl") redirectUrl: String,
|
||||
@Query("language") language: String?,
|
||||
@Query("theme") theme: String?,
|
||||
@Query("requestId") requestId: String,
|
||||
): ApiResponse<OnrampDataResponse>
|
||||
|
||||
@GET("onramp-status")
|
||||
suspend fun getStatus(@Query("txId") txId: String): ApiResponse<OnrampStatusResponse>
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.datasource.api.onramp.models.common
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampDestinationDTO(
|
||||
@Json(name = "contractAddress")
|
||||
val contractAddress: String,
|
||||
|
||||
@Json(name = "network")
|
||||
val network: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.datasource.api.onramp.models.request
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.datasource.api.onramp.models.common.OnrampDestinationDTO
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampPairsRequest(
|
||||
@Json(name = "fromCurrencyCode")
|
||||
val fromCurrencyCode: String?,
|
||||
|
||||
@Json(name = "countryCode")
|
||||
val countryCode: String,
|
||||
|
||||
@Json(name = "to")
|
||||
val to: List<OnrampDestinationDTO>,
|
||||
)
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.tangem.datasource.api.onramp.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
data class OnrampDataResponseWithTxDetails(
|
||||
val dataResponse: OnrampDataResponse,
|
||||
val txDetails: OnrampTxDetails,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampDataResponse(
|
||||
@Json(name = "txId")
|
||||
val txId: String,
|
||||
|
||||
@Json(name = "dataJson")
|
||||
val dataJson: String,
|
||||
|
||||
@Json(name = "signature")
|
||||
val signature: String,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampTxDetails(
|
||||
@Json(name = "fromCurrencyCode")
|
||||
val fromCurrencyCode: String,
|
||||
|
||||
@Json(name = "toContractAddress")
|
||||
val toContractAddress: String,
|
||||
|
||||
@Json(name = "toNetwork")
|
||||
val toNetwork: String,
|
||||
|
||||
@Json(name = "paymentMethod")
|
||||
val paymentMethod: String,
|
||||
|
||||
@Json(name = "countryCode")
|
||||
val countryCode: String,
|
||||
|
||||
@Json(name = "fromAmount")
|
||||
val fromAmount: String,
|
||||
|
||||
@Json(name = "toDecimals")
|
||||
val toDecimals: Int,
|
||||
|
||||
@Json(name = "providerId")
|
||||
val providerId: String,
|
||||
|
||||
@Json(name = "toAddress")
|
||||
val toAddress: String,
|
||||
|
||||
@Json(name = "redirectUrl")
|
||||
val redirectUrl: String,
|
||||
|
||||
@Json(name = "language")
|
||||
val language: String?,
|
||||
|
||||
@Json(name = "theme")
|
||||
val theme: String?,
|
||||
|
||||
@Json(name = "requestId")
|
||||
val requestId: String,
|
||||
|
||||
@Json(name = "externalTxId")
|
||||
val externalTxId: String,
|
||||
|
||||
@Json(name = "widgetUrl")
|
||||
val widgetUrl: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.datasource.api.onramp.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampQuoteResponse(
|
||||
@Json(name = "fromCurrencyCode")
|
||||
val fromCurrencyCode: String,
|
||||
|
||||
@Json(name = "toContractAddress")
|
||||
val toContractAddress: String,
|
||||
|
||||
@Json(name = "toNetwork")
|
||||
val toNetwork: String,
|
||||
|
||||
@Json(name = "paymentMethod")
|
||||
val paymentMethod: String,
|
||||
|
||||
@Json(name = "countryCode")
|
||||
val countryCode: String,
|
||||
|
||||
@Json(name = "fromAmount")
|
||||
val fromAmount: String,
|
||||
|
||||
@Json(name = "toAmount")
|
||||
val toAmount: String,
|
||||
|
||||
@Json(name = "toDecimals")
|
||||
val toDecimals: Int,
|
||||
|
||||
@Json(name = "providerId")
|
||||
val providerId: String,
|
||||
|
||||
@Json(name = "minFromAmount")
|
||||
val minFromAmount: String,
|
||||
|
||||
@Json(name = "maxFromAmount")
|
||||
val maxFromAmount: String,
|
||||
|
||||
@Json(name = "minToAmount")
|
||||
val minToAmount: String,
|
||||
|
||||
@Json(name = "maxToAmount")
|
||||
val maxToAmount: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.tangem.datasource.api.onramp.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampStatusResponse(
|
||||
@Json(name = "txId")
|
||||
val txId: String,
|
||||
|
||||
@Json(name = "providerId")
|
||||
val providerId: String,
|
||||
|
||||
@Json(name = "payoutAddress")
|
||||
val payoutAddress: String,
|
||||
|
||||
// @Json(name = "status")
|
||||
// val status: ???
|
||||
|
||||
@Json(name = "failReason")
|
||||
val failReason: String?,
|
||||
|
||||
@Json(name = "externalTxId")
|
||||
val externalTxId: String,
|
||||
|
||||
@Json(name = "externalTxUrl")
|
||||
val externalTxUrl: String?,
|
||||
|
||||
@Json(name = "payoutHash")
|
||||
val payoutHash: String?,
|
||||
|
||||
@Json(name = "createdAt")
|
||||
val createdAt: String,
|
||||
|
||||
@Json(name = "fromCurrencyCode")
|
||||
val fromCurrencyCode: String,
|
||||
|
||||
@Json(name = "fromAmount")
|
||||
val fromAmount: String,
|
||||
|
||||
@Json(name = "toContractAddress")
|
||||
val toContractAddress: String,
|
||||
|
||||
@Json(name = "toNetwork")
|
||||
val toNetwork: String,
|
||||
|
||||
@Json(name = "toDecimals")
|
||||
val toDecimals: String,
|
||||
|
||||
@Json(name = "toAmount")
|
||||
val toAmount: String,
|
||||
|
||||
@Json(name = "toActualAmount")
|
||||
val toActualAmount: String,
|
||||
|
||||
@Json(name = "paymentMethod")
|
||||
val paymentMethod: String,
|
||||
|
||||
@Json(name = "countryCode")
|
||||
val countryCode: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.datasource.api.onramp.models.response.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampCountryDTO(
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
|
||||
@Json(name = "code")
|
||||
val code: String,
|
||||
|
||||
@Json(name = "image")
|
||||
val image: String,
|
||||
|
||||
@Json(name = "alpha3")
|
||||
val alpha3: String,
|
||||
|
||||
@Json(name = "continent")
|
||||
val continent: String,
|
||||
|
||||
@Json(name = "defaultCurrency")
|
||||
val defaultCurrency: OnrampCurrencyDTO,
|
||||
|
||||
@Json(name = "onrampAvailable")
|
||||
val onrampAvailable: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.datasource.api.onramp.models.response.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampCurrencyDTO(
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
|
||||
@Json(name = "code")
|
||||
val code: String,
|
||||
|
||||
@Json(name = "image")
|
||||
val image: String,
|
||||
|
||||
@Json(name = "precision")
|
||||
val precision: Int,
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.datasource.api.onramp.models.response.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.datasource.api.onramp.models.common.OnrampDestinationDTO
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampPairDTO(
|
||||
@Json(name = "fromCurrencyCode")
|
||||
val fromCurrencyCode: String?,
|
||||
|
||||
@Json(name = "to")
|
||||
val to: OnrampDestinationDTO,
|
||||
|
||||
@Json(name = "providers")
|
||||
val providers: List<OnrampProviderDTO>,
|
||||
)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.datasource.api.onramp.models.response.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OnrampProviderDTO(
|
||||
@Json(name = "providerId")
|
||||
val providerId: String,
|
||||
|
||||
@Json(name = "paymentMethods")
|
||||
val paymentMethods: List<String>,
|
||||
)
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.datasource.api.onramp.models.response.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PaymentMethodDTO(
|
||||
@Json(name = "id")
|
||||
val id: String,
|
||||
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
|
||||
@Json(name = "image")
|
||||
val image: String,
|
||||
)
|
||||
|
|
@ -5,9 +5,7 @@ import com.tangem.datasource.api.stakekit.models.request.*
|
|||
import com.tangem.datasource.api.stakekit.models.response.EnabledYieldsResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.EnterActionResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.TokenWithYieldDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.transaction.StakingGasEstimateDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.transaction.StakingTransactionDTO
|
||||
import retrofit2.http.*
|
||||
|
|
@ -16,7 +14,7 @@ import retrofit2.http.*
|
|||
interface StakeKitApi {
|
||||
|
||||
@GET("yields/enabled")
|
||||
suspend fun getMultipleYields(
|
||||
suspend fun getEnabledYields(
|
||||
@Query("preferredValidatorsOnly") preferredValidatorsOnly: Boolean? = null,
|
||||
@Query("ledgerWalletAPICompatible") ledgerWalletAPICompatible: Boolean? = null,
|
||||
@Query("type") type: YieldType? = null,
|
||||
|
|
@ -26,12 +24,6 @@ interface StakeKitApi {
|
|||
@Query("limit") limit: Int? = null,
|
||||
): ApiResponse<EnabledYieldsResponse>
|
||||
|
||||
@GET("yields/{integrationId}")
|
||||
suspend fun getSingleYield(
|
||||
@Path("integrationId") integrationId: String,
|
||||
@Query("ledgerWalletAPICompatible") ledgerWalletAPICompatible: Boolean = false,
|
||||
): ApiResponse<YieldDTO>
|
||||
|
||||
@POST("yields/balances")
|
||||
suspend fun getMultipleYieldBalances(
|
||||
@Body body: List<YieldBalanceRequestBody>,
|
||||
|
|
@ -43,9 +35,6 @@ interface StakeKitApi {
|
|||
@Body body: YieldBalanceRequestBody,
|
||||
): ApiResponse<List<BalanceDTO>>
|
||||
|
||||
@GET("tokens")
|
||||
suspend fun getTokens(): ApiResponse<List<TokenWithYieldDTO>>
|
||||
|
||||
@POST("actions/enter")
|
||||
suspend fun createEnterAction(@Body body: ActionRequestBody): ApiResponse<EnterActionResponse>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package com.tangem.datasource.api.stakekit.models.response.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TokenWithYieldDTO(
|
||||
@Json(name = "token") val token: TokenDTO,
|
||||
@Json(name = "availableYields") val availableYieldIds: List<String>,
|
||||
)
|
||||
|
|
@ -4,7 +4,7 @@ import com.tangem.datasource.api.common.response.ApiResponse
|
|||
import com.tangem.datasource.api.promotion.models.PromotionInfoResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.*
|
||||
import com.tangem.datasource.api.utils.ReadTimeout
|
||||
import com.tangem.datasource.config.models.ProviderModel
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
import retrofit2.http.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GeoResponse(
|
||||
@Json(name = "code") val code: String,
|
||||
)
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.datasource.config
|
||||
|
||||
import com.tangem.datasource.config.models.Config
|
||||
import com.tangem.datasource.config.models.ConfigModel
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface ConfigManager {
|
||||
|
||||
val config: Config
|
||||
|
||||
suspend fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)? = null)
|
||||
|
||||
fun turnOff(name: String)
|
||||
|
||||
fun resetToDefault(name: String)
|
||||
|
||||
companion object {
|
||||
const val IS_CREATING_TWIN_CARDS_ALLOWED = "isCreatingTwinCardsAllowed"
|
||||
const val IS_TOP_UP_ENABLED = "isTopUpEnabled"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
package com.tangem.datasource.config
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.datasource.config.ConfigManager.Companion.IS_CREATING_TWIN_CARDS_ALLOWED
|
||||
import com.tangem.datasource.config.ConfigManager.Companion.IS_TOP_UP_ENABLED
|
||||
import com.tangem.datasource.config.models.Config
|
||||
import com.tangem.datasource.config.models.ConfigModel
|
||||
import com.tangem.datasource.config.models.ConfigValueModel
|
||||
import com.tangem.datasource.config.models.FeatureModel
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
|
||||
|
||||
override var config: Config = Config()
|
||||
private set
|
||||
|
||||
private var defaultConfig = Config()
|
||||
|
||||
override suspend fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)?) {
|
||||
configLoader.load { configModel ->
|
||||
setupFeature(configModel.features)
|
||||
setupConfigValues(configModel.configValues)
|
||||
onComplete?.invoke(config)
|
||||
}
|
||||
}
|
||||
|
||||
override fun turnOff(name: String) {
|
||||
when (name) {
|
||||
IS_TOP_UP_ENABLED -> config = config.copy(isTopUpEnabled = false)
|
||||
IS_CREATING_TWIN_CARDS_ALLOWED -> config = config.copy(isCreatingTwinCardsAllowed = false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetToDefault(name: String) {
|
||||
when (name) {
|
||||
IS_TOP_UP_ENABLED -> {
|
||||
config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
|
||||
}
|
||||
IS_CREATING_TWIN_CARDS_ALLOWED -> {
|
||||
config = config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFeature(featureModel: FeatureModel?) {
|
||||
val model = featureModel ?: return
|
||||
|
||||
config = config.copy(
|
||||
isTopUpEnabled = model.isTopUpEnabled,
|
||||
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
|
||||
)
|
||||
|
||||
defaultConfig = defaultConfig.copy(
|
||||
isTopUpEnabled = model.isTopUpEnabled,
|
||||
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupConfigValues(configValues: ConfigValueModel?) {
|
||||
val values = configValues ?: return
|
||||
|
||||
config = createConfig(config, values)
|
||||
defaultConfig = config.copy()
|
||||
}
|
||||
|
||||
private fun createConfig(config: Config, configValues: ConfigValueModel): Config {
|
||||
return config.copy(
|
||||
coinMarketCapKey = configValues.coinMarketCapKey,
|
||||
moonPayApiKey = configValues.moonPayApiKey,
|
||||
moonPayApiSecretKey = configValues.moonPayApiSecretKey,
|
||||
mercuryoWidgetId = configValues.mercuryoWidgetId,
|
||||
mercuryoSecret = configValues.mercuryoSecret,
|
||||
blockchainSdkConfig = BlockchainSdkConfig(
|
||||
blockchairCredentials = BlockchairCredentials(
|
||||
apiKey = configValues.blockchairApiKeys,
|
||||
authToken = configValues.blockchairAuthorizationToken,
|
||||
),
|
||||
blockcypherTokens = configValues.blockcypherTokens,
|
||||
quickNodeSolanaCredentials = QuickNodeCredentials(
|
||||
apiKey = configValues.quiknodeApiKey,
|
||||
subdomain = configValues.quiknodeSubdomain,
|
||||
),
|
||||
quickNodeBscCredentials = QuickNodeCredentials(
|
||||
apiKey = configValues.bscQuiknodeApiKey,
|
||||
subdomain = configValues.bscQuiknodeSubdomain,
|
||||
),
|
||||
infuraProjectId = configValues.infuraProjectId,
|
||||
tronGridApiKey = configValues.tronGridApiKey,
|
||||
nowNodeCredentials = NowNodeCredentials(configValues.nowNodesApiKey),
|
||||
getBlockCredentials = createGetBlockCredentials(configValues),
|
||||
kaspaSecondaryApiUrl = configValues.kaspaSecondaryApiUrl,
|
||||
tonCenterCredentials = TonCenterCredentials(
|
||||
mainnetApiKey = configValues.tonCenterKeys.mainnet,
|
||||
testnetApiKey = configValues.tonCenterKeys.testnet,
|
||||
),
|
||||
chiaFireAcademyApiKey = configValues.chiaFireAcademyApiKey,
|
||||
chiaTangemApiKey = configValues.chiaTangemApiKey,
|
||||
hederaArkhiaApiKey = configValues.hederaArkhiaKey,
|
||||
polygonScanApiKey = configValues.polygonScanApiKey,
|
||||
bittensorDwellirApiKey = configValues.bittensorDwellirApiKey,
|
||||
bittensorOnfinalityApiKey = configValues.bittensorOnfinalityKey,
|
||||
koinosProApiKey = configValues.koinosProApiKey,
|
||||
),
|
||||
amplitudeApiKey = configValues.amplitudeApiKey,
|
||||
sprinklr = configValues.sprinklr,
|
||||
walletConnectProjectId = configValues.walletConnectProjectId,
|
||||
tangemComAuthorization = configValues.tangemComAuthorization,
|
||||
express = configValues.express,
|
||||
devExpress = configValues.devExpress,
|
||||
stakeKitApiKey = configValues.stakeKitApiKey,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createGetBlockCredentials(configValues: ConfigValueModel): GetBlockCredentials? {
|
||||
return configValues.getBlockAccessTokens?.let { accessTokens ->
|
||||
GetBlockCredentials(
|
||||
xrp = GetBlockAccessToken(jsonRpc = accessTokens.xrp?.jsonRPC),
|
||||
cardano = GetBlockAccessToken(rosetta = accessTokens.cardano?.rosetta),
|
||||
avalanche = GetBlockAccessToken(jsonRpc = accessTokens.avalanche?.jsonRPC),
|
||||
eth = GetBlockAccessToken(jsonRpc = accessTokens.eth?.jsonRPC),
|
||||
etc = GetBlockAccessToken(jsonRpc = accessTokens.etc?.jsonRPC),
|
||||
fantom = GetBlockAccessToken(jsonRpc = accessTokens.fantom?.jsonRPC),
|
||||
rsk = GetBlockAccessToken(jsonRpc = accessTokens.rsk?.jsonRPC),
|
||||
bsc = GetBlockAccessToken(jsonRpc = accessTokens.bsc?.jsonRPC),
|
||||
polygon = GetBlockAccessToken(jsonRpc = accessTokens.polygon?.jsonRPC),
|
||||
gnosis = GetBlockAccessToken(jsonRpc = accessTokens.gnosis?.jsonRPC),
|
||||
cronos = GetBlockAccessToken(jsonRpc = accessTokens.cronos?.jsonRPC),
|
||||
solana = GetBlockAccessToken(jsonRpc = accessTokens.solana?.jsonRPC),
|
||||
ton = GetBlockAccessToken(jsonRpc = accessTokens.ton?.jsonRPC),
|
||||
tron = GetBlockAccessToken(rest = accessTokens.tron?.rest),
|
||||
cosmos = GetBlockAccessToken(rest = accessTokens.cosmos?.rest),
|
||||
near = GetBlockAccessToken(jsonRpc = accessTokens.near?.jsonRPC),
|
||||
aptos = GetBlockAccessToken(rest = accessTokens.aptos?.rest),
|
||||
dogecoin = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.dogecoin?.jsonRPC,
|
||||
blockBookRest = accessTokens.dogecoin?.blockBookRest,
|
||||
),
|
||||
litecoin = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.litecoin?.jsonRPC,
|
||||
blockBookRest = accessTokens.litecoin?.blockBookRest,
|
||||
),
|
||||
dash = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.dash?.jsonRPC,
|
||||
blockBookRest = accessTokens.dash?.blockBookRest,
|
||||
),
|
||||
bitcoin = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.bitcoin?.jsonRPC,
|
||||
blockBookRest = accessTokens.bitcoin?.blockBookRest,
|
||||
),
|
||||
algorand = GetBlockAccessToken(rest = accessTokens.algorand?.rest),
|
||||
zkSyncEra = GetBlockAccessToken(rest = accessTokens.zksync?.jsonRPC),
|
||||
polygonZkEvm = GetBlockAccessToken(rest = accessTokens.polygonZkevm?.jsonRPC),
|
||||
base = GetBlockAccessToken(rest = accessTokens.base?.jsonRPC),
|
||||
blast = GetBlockAccessToken(jsonRpc = accessTokens.blast?.jsonRPC),
|
||||
filecoin = GetBlockAccessToken(jsonRpc = accessTokens.filecoin?.jsonRPC),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.datasource.config
|
||||
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.models.ConfigModel
|
||||
import com.tangem.datasource.config.models.ConfigValueModel
|
||||
import com.tangem.datasource.config.models.FeatureModel
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class FeaturesLocalLoader(
|
||||
private val assetLoader: AssetLoader,
|
||||
buildEnvironment: String,
|
||||
) : Loader<ConfigModel> {
|
||||
|
||||
private val featuresName = "features_$buildEnvironment"
|
||||
private val configValuesName = "tangem-app-config/config_$buildEnvironment"
|
||||
|
||||
override suspend fun load(onComplete: (ConfigModel) -> Unit) {
|
||||
ConfigModel(
|
||||
features = assetLoader.load<FeatureModel>(fileName = featuresName),
|
||||
configValues = assetLoader.load<ConfigValueModel>(fileName = configValuesName),
|
||||
).also(onComplete)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.datasource.config
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Loader<T> {
|
||||
suspend fun load(onComplete: (T) -> Unit)
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.tangem.datasource.config.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
sealed interface ChatConfig
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SprinklrConfig(
|
||||
@Json(name = "appID") val appId: String,
|
||||
@Json(name = "apiKey") val apiKey: String,
|
||||
@Json(name = "environment") val environment: String,
|
||||
) : ChatConfig
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.datasource.config.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ExpressModel(
|
||||
@Json(name = "apiKey")
|
||||
val apiKey: String,
|
||||
@Json(name = "signVerifierPublicKey")
|
||||
val signVerifierPublicKey: String,
|
||||
)
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.datasource.config.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TonCenterKeys(
|
||||
@Json(name = "mainnet")
|
||||
val mainnet: String,
|
||||
@Json(name = "testnet")
|
||||
val testnet: String,
|
||||
)
|
||||
|
|
@ -5,10 +5,10 @@ import com.tangem.crypto.CryptoUtils
|
|||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
|
||||
internal class Sha256SignatureVerifier(
|
||||
private val configManager: ConfigManager,
|
||||
private val environmentConfigStorage: EnvironmentConfigStorage,
|
||||
private val apiConfigsManager: ApiConfigsManager,
|
||||
) : DataSignatureVerifier {
|
||||
|
||||
|
|
@ -24,8 +24,8 @@ internal class Sha256SignatureVerifier(
|
|||
private fun getPubKey(): String? {
|
||||
val expressConfig = apiConfigsManager.getEnvironmentConfig(ApiConfig.ID.Express)
|
||||
return when (expressConfig.environment) {
|
||||
ApiEnvironment.PROD -> configManager.config.express?.signVerifierPublicKey
|
||||
else -> configManager.config.devExpress?.signVerifierPublicKey
|
||||
ApiEnvironment.PROD -> environmentConfigStorage.getConfigSync().express?.signVerifierPublicKey
|
||||
else -> environmentConfigStorage.getConfigSync().devExpress?.signVerifierPublicKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import com.tangem.datasource.api.common.config.ApiConfig
|
|||
import com.tangem.datasource.api.common.config.Express
|
||||
import com.tangem.datasource.api.common.config.StakeKit
|
||||
import com.tangem.datasource.api.common.config.TangemTech
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
|
@ -21,11 +21,11 @@ internal object ApiConfigsModule {
|
|||
@Provides
|
||||
@IntoSet
|
||||
fun provideExpressConfig(
|
||||
configManager: ConfigManager,
|
||||
environmentConfigStorage: EnvironmentConfigStorage,
|
||||
expressAuthProvider: ExpressAuthProvider,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
): ApiConfig {
|
||||
return Express(configManager, expressAuthProvider, appVersionProvider)
|
||||
return Express(environmentConfigStorage, expressAuthProvider, appVersionProvider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.config.ConfigManagerImpl
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ConfigModule {
|
||||
|
||||
@Binds
|
||||
fun bindConfigManager(configManager: ConfigManagerImpl): ConfigManager
|
||||
}
|
||||
|
|
@ -4,11 +4,11 @@ import com.squareup.moshi.Moshi
|
|||
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.datasource.api.common.adapter.*
|
||||
import com.tangem.datasource.api.common.adapter.BigDecimalAdapter
|
||||
import com.tangem.datasource.api.common.adapter.DateTimeAdapter
|
||||
import com.tangem.datasource.api.common.adapter.LocalDateAdapter
|
||||
import com.tangem.datasource.config.models.ProviderModel
|
||||
import com.tangem.datasource.api.common.adapter.addStakeKitEnumFallbackAdapters
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.datasource.api.common.config.managers.ProdApiConfigsManager
|
|||
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
|
||||
import com.tangem.datasource.api.express.TangemExpressApi
|
||||
import com.tangem.datasource.api.markets.TangemTechMarketsApi
|
||||
import com.tangem.datasource.api.onramp.OnrampApi
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApiV2
|
||||
|
|
@ -94,6 +95,27 @@ internal object NetworkModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOnrampApi(
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
@ApplicationContext context: Context,
|
||||
apiConfigsManager: ApiConfigsManager,
|
||||
appLogsStore: AppLogsStore,
|
||||
): OnrampApi {
|
||||
return createApi(
|
||||
id = ApiConfig.ID.Express,
|
||||
moshi = moshi,
|
||||
context = context,
|
||||
apiConfigsManager = apiConfigsManager,
|
||||
clientBuilder = {
|
||||
addInterceptor(
|
||||
NetworkLogsSaveInterceptor(appLogsStore),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemTechApi(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.crypto.DataSignatureVerifier
|
||||
import com.tangem.datasource.crypto.Sha256SignatureVerifier
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -17,9 +17,9 @@ internal object SecurityModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideDataSignatureVerifier(
|
||||
configManager: ConfigManager,
|
||||
environmentConfigStorage: EnvironmentConfigStorage,
|
||||
apiConfigsManager: ApiConfigsManager,
|
||||
): DataSignatureVerifier {
|
||||
return Sha256SignatureVerifier(configManager, apiConfigsManager)
|
||||
return Sha256SignatureVerifier(environmentConfigStorage, apiConfigsManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.token.DefaultStakingYieldsStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import dagger.Module
|
||||
|
|
@ -16,6 +15,6 @@ internal object StakingTokensStoreModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingTokensStore(): StakingYieldsStore {
|
||||
return DefaultStakingYieldsStore(dataStore = RuntimeDataStore())
|
||||
return DefaultStakingYieldsStore()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.testnet.DefaultTestnetTokensStorage
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object TestnetTokensStorageModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesTestnetTokensStorage(assetLoader: AssetLoader): TestnetTokensStorage {
|
||||
return DefaultTestnetTokensStorage(assetLoader)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.datasource.di.local.config
|
||||
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.config.environment.DefaultEnvironmentConfigStorage
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.datasource.local.config.providers.BlockchainProvidersStorage
|
||||
import com.tangem.datasource.local.config.providers.DefaultBlockchainProvidersStorage
|
||||
import com.tangem.datasource.local.config.testnet.DefaultTestnetTokensStorage
|
||||
import com.tangem.datasource.local.config.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object ConfigModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideEnvironmentConfigStorage(assetLoader: AssetLoader): EnvironmentConfigStorage {
|
||||
return DefaultEnvironmentConfigStorage(
|
||||
assetLoader = assetLoader,
|
||||
environmentConfigStore = RuntimeStateStore(defaultValue = EnvironmentConfig()),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesTestnetTokensStorage(assetLoader: AssetLoader): TestnetTokensStorage {
|
||||
return DefaultTestnetTokensStorage(assetLoader)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideProvidersOrderConfigStorage(assetLoader: AssetLoader): BlockchainProvidersStorage {
|
||||
return DefaultBlockchainProvidersStorage(
|
||||
assetLoader = assetLoader,
|
||||
runtimeStateStore = RuntimeStateStore(defaultValue = emptyMap()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.datasource.local.config.environment
|
||||
|
||||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.config.environment.converter.EnvironmentConfigConverter
|
||||
import com.tangem.datasource.local.config.environment.models.EnvironmentConfigModel
|
||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Default implementation for storing [EnvironmentConfig]
|
||||
*
|
||||
* @property assetLoader asset loader
|
||||
* @property environmentConfigStore config store
|
||||
*/
|
||||
internal class DefaultEnvironmentConfigStorage(
|
||||
private val assetLoader: AssetLoader,
|
||||
private val environmentConfigStore: RuntimeStateStore<EnvironmentConfig>,
|
||||
) : EnvironmentConfigStorage {
|
||||
|
||||
override suspend fun initialize(): EnvironmentConfig {
|
||||
val environmentConfigModel = assetLoader.load<EnvironmentConfigModel>(fileName = CONFIG_FILE_NAME)
|
||||
?: return environmentConfigStore.get().value
|
||||
|
||||
val config = EnvironmentConfigConverter.convert(value = environmentConfigModel)
|
||||
environmentConfigStore.store(value = config)
|
||||
|
||||
Timber.i("Config [$CONFIG_FILE_NAME] loaded successfully")
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
override fun getConfig(): Flow<EnvironmentConfig> = environmentConfigStore.get()
|
||||
|
||||
override fun getConfigSync(): EnvironmentConfig = environmentConfigStore.get().value
|
||||
|
||||
private companion object {
|
||||
const val CONFIG_FILE_NAME = "tangem-app-config/config_${BuildConfig.ENVIRONMENT}"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,17 @@
|
|||
package com.tangem.datasource.config.models
|
||||
package com.tangem.datasource.local.config.environment
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.datasource.local.config.environment.models.ExpressModel
|
||||
|
||||
data class Config(
|
||||
val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
|
||||
data class EnvironmentConfig(
|
||||
val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
|
||||
val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
|
||||
val mercuryoWidgetId: String = "",
|
||||
val mercuryoSecret: String = "",
|
||||
val amplitudeApiKey: String = "",
|
||||
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
|
||||
val isTopUpEnabled: Boolean = false,
|
||||
@Deprecated("Not relevant since version 3.23")
|
||||
val isCreatingTwinCardsAllowed: Boolean = false,
|
||||
val sprinklr: SprinklrConfig? = null,
|
||||
val walletConnectProjectId: String = "",
|
||||
val tangemComAuthorization: String? = null,
|
||||
val express: ExpressModel? = null,
|
||||
val devExpress: ExpressModel? = null,
|
||||
val stakeKitApiKey: String? = null,
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.datasource.local.config.environment
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Storage for [EnvironmentConfig]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface EnvironmentConfigStorage {
|
||||
|
||||
/** Initialize and return [EnvironmentConfig] */
|
||||
suspend fun initialize(): EnvironmentConfig
|
||||
|
||||
/** Get [EnvironmentConfig] as [Flow] */
|
||||
fun getConfig(): Flow<EnvironmentConfig>
|
||||
|
||||
/** Get [EnvironmentConfig] synchronously */
|
||||
fun getConfigSync(): EnvironmentConfig
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package com.tangem.datasource.local.config.environment.converter
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.datasource.local.config.environment.models.EnvironmentConfigModel
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converts [EnvironmentConfigModel] to [BlockchainSdkConfig]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object BlockchainSDKConfigConverter : Converter<EnvironmentConfigModel, BlockchainSdkConfig> {
|
||||
|
||||
override fun convert(value: EnvironmentConfigModel): BlockchainSdkConfig {
|
||||
return BlockchainSdkConfig(
|
||||
blockchairCredentials = BlockchairCredentials(
|
||||
apiKey = value.blockchairApiKeys,
|
||||
authToken = value.blockchairAuthorizationToken,
|
||||
),
|
||||
blockcypherTokens = value.blockcypherTokens,
|
||||
quickNodeSolanaCredentials = QuickNodeCredentials(
|
||||
apiKey = value.quiknodeApiKey,
|
||||
subdomain = value.quiknodeSubdomain,
|
||||
),
|
||||
quickNodeBscCredentials = QuickNodeCredentials(
|
||||
apiKey = value.bscQuiknodeApiKey,
|
||||
subdomain = value.bscQuiknodeSubdomain,
|
||||
),
|
||||
infuraProjectId = value.infuraProjectId,
|
||||
tronGridApiKey = value.tronGridApiKey,
|
||||
nowNodeCredentials = NowNodeCredentials(value.nowNodesApiKey),
|
||||
getBlockCredentials = createGetBlockCredentials(value),
|
||||
kaspaSecondaryApiUrl = value.kaspaSecondaryApiUrl,
|
||||
tonCenterCredentials = TonCenterCredentials(
|
||||
mainnetApiKey = value.tonCenterKeys.mainnet,
|
||||
testnetApiKey = value.tonCenterKeys.testnet,
|
||||
),
|
||||
chiaFireAcademyApiKey = value.chiaFireAcademyApiKey,
|
||||
chiaTangemApiKey = value.chiaTangemApiKey,
|
||||
hederaArkhiaApiKey = value.hederaArkhiaKey,
|
||||
polygonScanApiKey = value.polygonScanApiKey,
|
||||
bittensorDwellirApiKey = value.bittensorDwellirApiKey,
|
||||
bittensorOnfinalityApiKey = value.bittensorOnfinalityKey,
|
||||
koinosProApiKey = value.koinosProApiKey,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createGetBlockCredentials(configValues: EnvironmentConfigModel): GetBlockCredentials? {
|
||||
return configValues.getBlockAccessTokens?.let { accessTokens ->
|
||||
GetBlockCredentials(
|
||||
xrp = GetBlockAccessToken(jsonRpc = accessTokens.xrp?.jsonRPC),
|
||||
cardano = GetBlockAccessToken(rosetta = accessTokens.cardano?.rosetta),
|
||||
avalanche = GetBlockAccessToken(jsonRpc = accessTokens.avalanche?.jsonRPC),
|
||||
eth = GetBlockAccessToken(jsonRpc = accessTokens.eth?.jsonRPC),
|
||||
etc = GetBlockAccessToken(jsonRpc = accessTokens.etc?.jsonRPC),
|
||||
fantom = GetBlockAccessToken(jsonRpc = accessTokens.fantom?.jsonRPC),
|
||||
rsk = GetBlockAccessToken(jsonRpc = accessTokens.rsk?.jsonRPC),
|
||||
bsc = GetBlockAccessToken(jsonRpc = accessTokens.bsc?.jsonRPC),
|
||||
polygon = GetBlockAccessToken(jsonRpc = accessTokens.polygon?.jsonRPC),
|
||||
gnosis = GetBlockAccessToken(jsonRpc = accessTokens.gnosis?.jsonRPC),
|
||||
cronos = GetBlockAccessToken(jsonRpc = accessTokens.cronos?.jsonRPC),
|
||||
solana = GetBlockAccessToken(jsonRpc = accessTokens.solana?.jsonRPC),
|
||||
ton = GetBlockAccessToken(jsonRpc = accessTokens.ton?.jsonRPC),
|
||||
tron = GetBlockAccessToken(rest = accessTokens.tron?.rest),
|
||||
cosmos = GetBlockAccessToken(rest = accessTokens.cosmos?.rest),
|
||||
near = GetBlockAccessToken(jsonRpc = accessTokens.near?.jsonRPC),
|
||||
aptos = GetBlockAccessToken(rest = accessTokens.aptos?.rest),
|
||||
dogecoin = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.dogecoin?.jsonRPC,
|
||||
blockBookRest = accessTokens.dogecoin?.blockBookRest,
|
||||
),
|
||||
litecoin = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.litecoin?.jsonRPC,
|
||||
blockBookRest = accessTokens.litecoin?.blockBookRest,
|
||||
),
|
||||
dash = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.dash?.jsonRPC,
|
||||
blockBookRest = accessTokens.dash?.blockBookRest,
|
||||
),
|
||||
bitcoin = GetBlockAccessToken(
|
||||
jsonRpc = accessTokens.bitcoin?.jsonRPC,
|
||||
blockBookRest = accessTokens.bitcoin?.blockBookRest,
|
||||
),
|
||||
algorand = GetBlockAccessToken(rest = accessTokens.algorand?.rest),
|
||||
zkSyncEra = GetBlockAccessToken(jsonRpc = accessTokens.zksync?.jsonRPC),
|
||||
polygonZkEvm = GetBlockAccessToken(jsonRpc = accessTokens.polygonZkevm?.jsonRPC),
|
||||
base = GetBlockAccessToken(jsonRpc = accessTokens.base?.jsonRPC),
|
||||
blast = GetBlockAccessToken(jsonRpc = accessTokens.blast?.jsonRPC),
|
||||
filecoin = GetBlockAccessToken(jsonRpc = accessTokens.filecoin?.jsonRPC),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.datasource.local.config.environment.converter
|
||||
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.datasource.local.config.environment.models.EnvironmentConfigModel
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converter from [EnvironmentConfigModel] to [EnvironmentConfig]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object EnvironmentConfigConverter : Converter<EnvironmentConfigModel, EnvironmentConfig> {
|
||||
|
||||
override fun convert(value: EnvironmentConfigModel): EnvironmentConfig {
|
||||
return EnvironmentConfig(
|
||||
moonPayApiKey = value.moonPayApiKey,
|
||||
moonPayApiSecretKey = value.moonPayApiSecretKey,
|
||||
mercuryoWidgetId = value.mercuryoWidgetId,
|
||||
mercuryoSecret = value.mercuryoSecret,
|
||||
blockchainSdkConfig = BlockchainSDKConfigConverter.convert(value = value),
|
||||
amplitudeApiKey = value.amplitudeApiKey,
|
||||
walletConnectProjectId = value.walletConnectProjectId,
|
||||
express = value.express,
|
||||
devExpress = value.devExpress,
|
||||
stakeKitApiKey = value.stakeKitApiKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +1,37 @@
|
|||
package com.tangem.datasource.config.models
|
||||
package com.tangem.datasource.local.config.environment.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
// TODO remove
|
||||
class FeatureModel(
|
||||
val isTopUpEnabled: Boolean,
|
||||
val isCreatingTwinCardsAllowed: Boolean,
|
||||
)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
class ConfigValueModel(
|
||||
val coinMarketCapKey: String,
|
||||
val mercuryoWidgetId: String,
|
||||
val mercuryoSecret: String,
|
||||
val moonPayApiKey: String,
|
||||
val moonPayApiSecretKey: String,
|
||||
val blockchairApiKeys: List<String>,
|
||||
val blockchairAuthorizationToken: String?,
|
||||
val quiknodeSubdomain: String,
|
||||
val quiknodeApiKey: String,
|
||||
val bscQuiknodeSubdomain: String,
|
||||
val bscQuiknodeApiKey: String,
|
||||
val nowNodesApiKey: String,
|
||||
@JsonClass(generateAdapter = true)
|
||||
class EnvironmentConfigModel(
|
||||
@Json(name = "mercuryoWidgetId") val mercuryoWidgetId: String,
|
||||
@Json(name = "mercuryoSecret") val mercuryoSecret: String,
|
||||
@Json(name = "moonPayApiKey") val moonPayApiKey: String,
|
||||
@Json(name = "moonPayApiSecretKey") val moonPayApiSecretKey: String,
|
||||
@Json(name = "blockchairApiKeys") val blockchairApiKeys: List<String>,
|
||||
@Json(name = "blockchairAuthorizationToken") val blockchairAuthorizationToken: String?,
|
||||
@Json(name = "quiknodeSubdomain") val quiknodeSubdomain: String,
|
||||
@Json(name = "quiknodeApiKey") val quiknodeApiKey: String,
|
||||
@Json(name = "bscQuiknodeSubdomain") val bscQuiknodeSubdomain: String,
|
||||
@Json(name = "bscQuiknodeApiKey") val bscQuiknodeApiKey: String,
|
||||
@Json(name = "nowNodesApiKey") val nowNodesApiKey: String,
|
||||
@Json(name = "getBlockAccessTokens") val getBlockAccessTokens: GetBlockAccessTokens?,
|
||||
@Json(name = "tonCenterApiKey") val tonCenterKeys: TonCenterKeys,
|
||||
val blockcypherTokens: Set<String>?,
|
||||
val infuraProjectId: String?,
|
||||
val sprinklr: SprinklrConfig?,
|
||||
val tronGridApiKey: String,
|
||||
val amplitudeApiKey: String,
|
||||
val kaspaSecondaryApiUrl: String,
|
||||
val walletConnectProjectId: String,
|
||||
val tangemComAuthorization: String?,
|
||||
val chiaFireAcademyApiKey: String?,
|
||||
val chiaTangemApiKey: String?,
|
||||
val devExpress: ExpressModel?,
|
||||
val express: ExpressModel?,
|
||||
@Json(name = "blockcypherTokens") val blockcypherTokens: Set<String>?,
|
||||
@Json(name = "infuraProjectId") val infuraProjectId: String?,
|
||||
@Json(name = "tronGridApiKey") val tronGridApiKey: String,
|
||||
@Json(name = "amplitudeApiKey") val amplitudeApiKey: String,
|
||||
@Json(name = "kaspaSecondaryApiUrl") val kaspaSecondaryApiUrl: String,
|
||||
@Json(name = "walletConnectProjectId") val walletConnectProjectId: String,
|
||||
@Json(name = "chiaFireAcademyApiKey") val chiaFireAcademyApiKey: String?,
|
||||
@Json(name = "chiaTangemApiKey") val chiaTangemApiKey: String?,
|
||||
@Json(name = "devExpress") val devExpress: ExpressModel?,
|
||||
@Json(name = "express") val express: ExpressModel?,
|
||||
@Json(name = "hederaArkhiaKey") val hederaArkhiaKey: String?,
|
||||
val polygonScanApiKey: String?,
|
||||
val stakeKitApiKey: String?,
|
||||
@Json(name = "polygonScanApiKey") val polygonScanApiKey: String?,
|
||||
@Json(name = "stakeKitApiKey") val stakeKitApiKey: String?,
|
||||
@Json(name = "bittensorDwellirKey") val bittensorDwellirApiKey: String?,
|
||||
@Json(name = "bittensorOnfinalityKey") val bittensorOnfinalityKey: String?,
|
||||
@Json(name = "koinosProApiKey") val koinosProApiKey: String?,
|
||||
|
|
@ -80,6 +68,12 @@ data class GetBlockAccessTokens(
|
|||
@Json(name = "filecoin") val filecoin: GetBlockToken?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TonCenterKeys(
|
||||
@Json(name = "mainnet") val mainnet: String,
|
||||
@Json(name = "testnet") val testnet: String,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GetBlockToken(
|
||||
@Json(name = "jsonRpc") val jsonRPC: String?,
|
||||
|
|
@ -88,11 +82,10 @@ data class GetBlockToken(
|
|||
@Json(name = "rosetta") val rosetta: String?,
|
||||
)
|
||||
|
||||
class ConfigModel(
|
||||
val features: FeatureModel?,
|
||||
val configValues: ConfigValueModel?,
|
||||
) {
|
||||
companion object {
|
||||
fun empty(): ConfigModel = ConfigModel(null, null)
|
||||
}
|
||||
}
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ExpressModel(
|
||||
@Json(name = "apiKey")
|
||||
val apiKey: String,
|
||||
@Json(name = "signVerifierPublicKey")
|
||||
val signVerifierPublicKey: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.datasource.local.config.providers
|
||||
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
|
||||
/**
|
||||
* Blockchain providers storage
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface BlockchainProvidersStorage {
|
||||
|
||||
/** Get config */
|
||||
suspend fun getConfigSync(): Map<String, List<ProviderModel>>
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.datasource.local.config.providers
|
||||
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
||||
|
||||
/**
|
||||
* Default blockchain providers storage
|
||||
*
|
||||
* @property assetLoader asset loader
|
||||
* @property runtimeStateStore runtime state store
|
||||
*/
|
||||
internal class DefaultBlockchainProvidersStorage(
|
||||
private val assetLoader: AssetLoader,
|
||||
private val runtimeStateStore: RuntimeStateStore<Map<String, List<ProviderModel>>>,
|
||||
) : BlockchainProvidersStorage {
|
||||
|
||||
override suspend fun getConfigSync(): Map<String, List<ProviderModel>> {
|
||||
val cachedData = runtimeStateStore.get().value
|
||||
|
||||
if (cachedData.isNotEmpty()) return cachedData
|
||||
|
||||
val config = assetLoader.load<Map<String, List<ProviderModel>>>(fileName = PROVIDER_TYPES_FILE_NAME).orEmpty()
|
||||
|
||||
runtimeStateStore.store(value = config)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PROVIDER_TYPES_FILE_NAME = "tangem-app-config/providers_order"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.config.models
|
||||
package com.tangem.datasource.local.config.providers.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.datasource.local.testnet
|
||||
package com.tangem.datasource.local.config.testnet
|
||||
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig
|
||||
|
||||
/**
|
||||
* Default implementation for storing testnet tokens data
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.datasource.local.testnet
|
||||
package com.tangem.datasource.local.config.testnet
|
||||
|
||||
import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig
|
||||
|
||||
/**
|
||||
* Storage of testnet tokens data
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.local.testnet.models
|
||||
package com.tangem.datasource.local.config.testnet.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ import com.squareup.moshi.Json
|
|||
*
|
||||
* @property tokens testnet tokens list
|
||||
*/
|
||||
// TODO: use CoinsResponse
|
||||
data class TestnetTokensConfig(
|
||||
@Json(name = "coins") val tokens: List<Token>,
|
||||
) {
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
package com.tangem.datasource.local.datastore
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.utils.Trigger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import timber.log.Timber
|
||||
|
||||
@Deprecated("Use shared preferences data store instead")
|
||||
internal class FileDataStore<Value : Any>(
|
||||
private val fileReader: FileReader,
|
||||
private val adapter: JsonAdapter<Value>,
|
||||
) : StringKeyDataStore<Value> {
|
||||
|
||||
private val mutex = Mutex()
|
||||
private val writeTrigger = Trigger()
|
||||
override suspend fun isEmpty(): Boolean {
|
||||
val e = NotImplementedError("`isEmpty()` function not implemented for `FileDataStore`")
|
||||
Timber.e(e)
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
override suspend fun contains(key: String): Boolean = getSyncOrNull(key) != null
|
||||
|
||||
override fun get(key: String): Flow<Value> {
|
||||
return writeTrigger
|
||||
.map {
|
||||
mutex.withLock {
|
||||
getInternal(key)
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
override fun getAll(): Flow<List<Value>> {
|
||||
val e = NotImplementedError("`getAll()` function not implemented for `FileDataStore`")
|
||||
Timber.e(e)
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(key: String): Value? {
|
||||
return getInternal(key)
|
||||
}
|
||||
|
||||
override suspend fun getAllSyncOrNull(): List<Value>? {
|
||||
val e = NotImplementedError("`getAllSyncOrNull()` function not implemented for `FileDataStore`")
|
||||
Timber.e(e)
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
override suspend fun store(key: String, value: Value) {
|
||||
try {
|
||||
mutex.withLock {
|
||||
val json = adapter.toJson(value)
|
||||
|
||||
fileReader.rewriteFile(json, key)
|
||||
writeTrigger.trigger()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to write file: $key")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun store(values: Map<String, Value>) {
|
||||
values.forEach { (key, item) ->
|
||||
store(key, item)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remove(key: String) {
|
||||
fileReader.removeFile(key)
|
||||
writeTrigger.trigger()
|
||||
}
|
||||
|
||||
override suspend fun remove(keys: Collection<String>) {
|
||||
val e = NotImplementedError("`remove(keys)` function not implemented for `FileDataStore`")
|
||||
Timber.e(e)
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
override suspend fun clear() {
|
||||
val e = NotImplementedError("`clear()` function not implemented for `FileDataStore`")
|
||||
Timber.e(e)
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
private fun getInternal(fileName: String): Value? {
|
||||
return try {
|
||||
val json = fileReader.readFile(fileName)
|
||||
|
||||
adapter.fromJson(json)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to read file: $fileName")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.datasource.local.datastore
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Runtime store
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface RuntimeStateStore<T> {
|
||||
|
||||
/** Get flow of elements [T] */
|
||||
fun get(): StateFlow<T>
|
||||
|
||||
/** Store [value] */
|
||||
suspend fun store(value: T)
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Create [RuntimeStateStore]
|
||||
*
|
||||
* @param T type of stored value
|
||||
* @param defaultValue default value
|
||||
*/
|
||||
operator fun <T> invoke(defaultValue: T): RuntimeStateStore<T> = object : RuntimeStateStore<T> {
|
||||
private val flow = MutableStateFlow(value = defaultValue)
|
||||
|
||||
override fun get(): StateFlow<T> = flow
|
||||
|
||||
override suspend fun store(value: T) {
|
||||
flow.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.datasource.local.datastore.utils
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
/**
|
||||
* Represents a trigger mechanism to emit values on-demand.
|
||||
*
|
||||
* This class provides a mechanism to trigger emissions via the [trigger] method.
|
||||
*
|
||||
* @property triggerFlow The internal flow that gets toggled to trigger emissions.
|
||||
*/
|
||||
internal class Trigger(
|
||||
private val triggerFlow: MutableStateFlow<Boolean> = MutableStateFlow(value = false),
|
||||
) : Flow<Unit> {
|
||||
|
||||
/**
|
||||
* Collects values emitted by this flow.
|
||||
*
|
||||
* Overrides the default collection mechanism to emit a [Unit] value whenever [triggerFlow] changes.
|
||||
*
|
||||
* @param collector The collector responsible for handling emitted values.
|
||||
*/
|
||||
override suspend fun collect(collector: FlowCollector<Unit>): Nothing {
|
||||
triggerFlow.collect { collector.emit(Unit) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an emission.
|
||||
*/
|
||||
fun trigger() {
|
||||
triggerFlow.value = !triggerFlow.value
|
||||
}
|
||||
}
|
||||
|
|
@ -103,7 +103,9 @@ class AppLogsStore @Inject constructor(
|
|||
private fun launchWithLock(callback: () -> Unit) {
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
callback()
|
||||
runCatching {
|
||||
callback()
|
||||
}.onFailure(Timber::e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
|
|
@ -29,9 +29,13 @@ internal class DefaultStakingBalanceStore(
|
|||
}
|
||||
}
|
||||
|
||||
override fun get(userWalletId: UserWalletId, address: String, integrationId: String): Flow<YieldBalanceWrapperDTO> {
|
||||
override fun get(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
): Flow<YieldBalanceWrapperDTO?> {
|
||||
return dataStore.get(userWalletId.stringValue)
|
||||
.mapNotNull { balances ->
|
||||
.map { balances ->
|
||||
balances.firstOrNull { it.integrationId == integrationId && it.addresses.address == address }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.domain.staking.model.StakingTokenWithYield
|
||||
|
||||
internal class DefaultStakingTokensStore(
|
||||
private val dataStore: StringKeyDataStore<List<StakingTokenWithYield>>,
|
||||
) : StakingTokensStore {
|
||||
|
||||
override suspend fun getSyncOrNull(): List<StakingTokenWithYield>? {
|
||||
return dataStore.getSyncOrNull(STAKING_TOKENS_KEY)
|
||||
}
|
||||
|
||||
override suspend fun store(items: List<StakingTokenWithYield>) {
|
||||
dataStore.store(STAKING_TOKENS_KEY, items)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val STAKING_TOKENS_KEY = "STAKING_TOKENS_KEY"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,16 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
|
||||
internal class DefaultStakingYieldsStore(
|
||||
private val dataStore: StringKeyDataStore<List<YieldDTO>>,
|
||||
) : StakingYieldsStore {
|
||||
internal class DefaultStakingYieldsStore : StakingYieldsStore {
|
||||
|
||||
override suspend fun getSyncOrNull(): List<YieldDTO>? {
|
||||
return dataStore.getSyncOrNull(STAKING_YIELDS_KEY)
|
||||
private var yields = mutableListOf<YieldDTO>()
|
||||
|
||||
override fun get(): List<YieldDTO> {
|
||||
return yields
|
||||
}
|
||||
|
||||
override suspend fun store(items: List<YieldDTO>) {
|
||||
dataStore.store(STAKING_YIELDS_KEY, items)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val STAKING_YIELDS_KEY = "STAKING_YIELDS_KEY"
|
||||
override fun store(items: List<YieldDTO>) {
|
||||
yields = items.toMutableList()
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ interface StakingBalanceStore {
|
|||
|
||||
suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>)
|
||||
|
||||
fun get(userWalletId: UserWalletId, address: String, integrationId: String): Flow<YieldBalanceWrapperDTO>
|
||||
fun get(userWalletId: UserWalletId, address: String, integrationId: String): Flow<YieldBalanceWrapperDTO?>
|
||||
|
||||
suspend fun getSyncOrNull(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.domain.staking.model.StakingTokenWithYield
|
||||
|
||||
interface StakingTokensStore {
|
||||
|
||||
suspend fun getSyncOrNull(): List<StakingTokenWithYield>?
|
||||
|
||||
suspend fun store(items: List<StakingTokenWithYield>)
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
|||
|
||||
interface StakingYieldsStore {
|
||||
|
||||
suspend fun getSyncOrNull(): List<YieldDTO>?
|
||||
fun get(): List<YieldDTO>
|
||||
|
||||
suspend fun store(items: List<YieldDTO>)
|
||||
fun store(items: List<YieldDTO>)
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.datasource.api.common.config.managers
|
||||
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.config.Loader
|
||||
import com.tangem.datasource.config.models.Config
|
||||
import com.tangem.datasource.config.models.ConfigModel
|
||||
import com.tangem.datasource.config.models.ExpressModel
|
||||
|
||||
/**
|
||||
* Mock [ConfigManager] implementation for [ProdApiConfigsManagerTest]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class MockConfigManager : ConfigManager {
|
||||
|
||||
override val config = Config(
|
||||
express = ExpressModel(apiKey = ProdApiConfigsManagerTest.EXPRESS_API_KEY, signVerifierPublicKey = ""),
|
||||
devExpress = ExpressModel(apiKey = ProdApiConfigsManagerTest.EXPRESS_DEV_API_KEY, signVerifierPublicKey = ""),
|
||||
)
|
||||
|
||||
override suspend fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)?) = Unit
|
||||
override fun turnOff(name: String) = Unit
|
||||
override fun resetToDefault(name: String) = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.datasource.api.common.config.managers
|
||||
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.datasource.local.config.environment.models.ExpressModel
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
/**
|
||||
* Mock [EnvironmentConfigStorage] implementation for [ProdApiConfigsManagerTest]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class MockEnvironmentConfigStorage : EnvironmentConfigStorage {
|
||||
|
||||
private val environmentConfig = EnvironmentConfig(
|
||||
express = ExpressModel(apiKey = EXPRESS_API_KEY, signVerifierPublicKey = "vocibus"),
|
||||
devExpress = ExpressModel(apiKey = EXPRESS_DEV_API_KEY, signVerifierPublicKey = "pellentesque"),
|
||||
)
|
||||
|
||||
override suspend fun initialize() = environmentConfig
|
||||
override fun getConfig() = flowOf(environmentConfig)
|
||||
override fun getConfigSync() = environmentConfig
|
||||
|
||||
companion object {
|
||||
const val EXPRESS_API_KEY = "express_api_key"
|
||||
const val EXPRESS_DEV_API_KEY = "express_dev_api_key"
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ import org.junit.Test
|
|||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
private val configManager = MockConfigManager()
|
||||
private val configManager = MockEnvironmentConfigStorage()
|
||||
private val appVersionProvider = mockk<AppVersionProvider>()
|
||||
private val expressAuthProvider = mockk<ExpressAuthProvider>()
|
||||
private val stakeKitAuthProvider = mockk<StakeKitAuthProvider>()
|
||||
|
|
@ -67,8 +67,6 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
|||
const val EXPRESS_USER_ID = "express_user_id"
|
||||
const val EXPRESS_SESSION_ID = "express_session_id"
|
||||
const val EXPRESS_REF_CODE = "express_ref_code"
|
||||
const val EXPRESS_API_KEY = "express_api_key"
|
||||
const val EXPRESS_DEV_API_KEY = "express_dev_api_key"
|
||||
const val STAKE_KIT_API_KEY = "stake_kit_api_key"
|
||||
|
||||
@JvmStatic
|
||||
|
|
@ -109,7 +107,11 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
|||
},
|
||||
headers = mapOf(
|
||||
"api-key" to Provider {
|
||||
if (environment == ApiEnvironment.PROD) EXPRESS_API_KEY else EXPRESS_DEV_API_KEY
|
||||
if (environment == ApiEnvironment.PROD) {
|
||||
MockEnvironmentConfigStorage.EXPRESS_API_KEY
|
||||
} else {
|
||||
MockEnvironmentConfigStorage.EXPRESS_DEV_API_KEY
|
||||
}
|
||||
},
|
||||
"user-id" to Provider { EXPRESS_USER_ID },
|
||||
"session-id" to Provider { EXPRESS_SESSION_ID },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue