Updated on 2026-08-14
This commit is contained in:
commit
0470e1f010
1144 changed files with 48463 additions and 11166 deletions
|
|
@ -6,20 +6,17 @@ import com.tangem.datasource.utils.RequestHeader
|
|||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
/**
|
||||
* Express [ApiConfig]
|
||||
*
|
||||
* @property environmentConfig environment config
|
||||
* @property expressAuthProvider express auth provider
|
||||
* @property appVersionProvider app version provider
|
||||
* @property appInfoProvider app info provider
|
||||
*/
|
||||
internal class Express(
|
||||
private val environmentConfig: EnvironmentConfig,
|
||||
private val expressAuthProvider: ExpressAuthProvider,
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
private val appInfoProvider: AppInfoProvider,
|
||||
) : ApiConfig() {
|
||||
|
||||
|
|
@ -102,7 +99,7 @@ internal class Express(
|
|||
private fun createHeaders(isProd: Boolean) = buildMap {
|
||||
put(key = "api-key", value = ProviderSuspend { getApiKey(isProd) })
|
||||
put(key = "session-id", value = ProviderSuspend(expressAuthProvider::getSessionId))
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
|
||||
}
|
||||
|
||||
private fun getApiKey(isProd: Boolean): String {
|
||||
|
|
|
|||
|
|
@ -6,14 +6,12 @@ import com.tangem.datasource.utils.RequestHeader
|
|||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
/**
|
||||
* Gasless transactions [ApiConfig]
|
||||
*/
|
||||
internal class GaslessTxService(
|
||||
private val authProvider: AuthProvider,
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
private val appInfoProvider: AppInfoProvider,
|
||||
) : ApiConfig() {
|
||||
|
||||
|
|
@ -50,7 +48,7 @@ internal class GaslessTxService(
|
|||
)
|
||||
|
||||
private fun createHeaders(environment: ApiEnvironment) = buildMap {
|
||||
putAll(RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
|
||||
putAll(RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
|
||||
put(
|
||||
key = "Authorization",
|
||||
value = ProviderSuspend {
|
||||
|
|
|
|||
|
|
@ -5,14 +5,12 @@ import com.tangem.datasource.api.common.AuthProvider
|
|||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
/**
|
||||
* News [ApiConfig]
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class News(
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
private val appInfoProvider: AppInfoProvider,
|
||||
private val authProvider: AuthProvider,
|
||||
) : ApiConfig() {
|
||||
|
|
@ -64,7 +62,7 @@ internal class News(
|
|||
apiEnvironment = Provider { environment },
|
||||
).values,
|
||||
)
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ package com.tangem.datasource.api.common.config
|
|||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
|
||||
internal sealed class TangemPay(
|
||||
private val environmentConfig: EnvironmentConfig,
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
private val appInfoProvider: AppInfoProvider,
|
||||
) : ApiConfig() {
|
||||
|
||||
override val defaultEnvironment: ApiEnvironment = getInitialEnvironment()
|
||||
|
|
@ -52,7 +52,7 @@ internal sealed class TangemPay(
|
|||
)
|
||||
|
||||
private fun createHeaders(apiEnvironment: ApiEnvironment) = mapOf(
|
||||
"version" to ProviderSuspend { appVersionProvider.versionName },
|
||||
"version" to ProviderSuspend { appInfoProvider.appVersion },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
"X-API-KEY" to ProviderSuspend { getBffStaticToken(apiEnvironment) },
|
||||
)
|
||||
|
|
@ -74,8 +74,8 @@ internal sealed class TangemPay(
|
|||
|
||||
class Bff(
|
||||
environmentConfig: EnvironmentConfig,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
) : TangemPay(environmentConfig, appVersionProvider) {
|
||||
appInfoProvider: AppInfoProvider,
|
||||
) : TangemPay(environmentConfig, appInfoProvider) {
|
||||
override fun getBaseUrl(apiEnvironment: ApiEnvironment): String {
|
||||
return when (apiEnvironment) {
|
||||
ApiEnvironment.DEV -> "https://api.dev.us.paera.com/bff-v2/"
|
||||
|
|
@ -93,8 +93,8 @@ internal sealed class TangemPay(
|
|||
|
||||
class Auth(
|
||||
environmentConfig: EnvironmentConfig,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
) : TangemPay(environmentConfig, appVersionProvider) {
|
||||
appInfoProvider: AppInfoProvider,
|
||||
) : TangemPay(environmentConfig, appInfoProvider) {
|
||||
override fun getBaseUrl(apiEnvironment: ApiEnvironment): String {
|
||||
return when (apiEnvironment) {
|
||||
ApiEnvironment.DEV -> "https://api.dev.us.paera.com/"
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ import com.tangem.datasource.api.common.AuthProvider
|
|||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
/** TangemTech [ApiConfig] */
|
||||
internal class TangemTech(
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
private val authProvider: AuthProvider,
|
||||
private val appInfoProvider: AppInfoProvider,
|
||||
) : ApiConfig() {
|
||||
|
|
@ -62,7 +60,7 @@ internal class TangemTech(
|
|||
|
||||
private fun createHeaders(apiEnvironment: ApiEnvironment) = buildMap {
|
||||
putAll(from = RequestHeader.TangemApiKeyHeader(authProvider, Provider { apiEnvironment }).values)
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
|
||||
putAll(from = RequestHeader.AuthenticationHeader(authProvider).values)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,12 +6,10 @@ import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
|||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
/** YieldSupply [ApiConfig] */
|
||||
internal class YieldSupply(
|
||||
private val environmentConfig: EnvironmentConfig,
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
private val authProvider: AuthProvider,
|
||||
private val appInfoProvider: AppInfoProvider,
|
||||
) : ApiConfig() {
|
||||
|
|
@ -66,7 +64,7 @@ internal class YieldSupply(
|
|||
put(key = "api-key", value = ProviderSuspend {
|
||||
getApiKey(apiEnvironment)
|
||||
})
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
|
||||
putAll(from = RequestHeader.AuthenticationHeader(authProvider).values)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,18 @@ interface TangemPayApi {
|
|||
@Body body: FreezeUnfreezeCardRequest,
|
||||
): ApiResponse<FreezeUnfreezeCardResponse>
|
||||
|
||||
@GET("v1/fees/{type}")
|
||||
suspend fun getFee(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Path("type") type: String,
|
||||
): ApiResponse<FeeResponse>
|
||||
|
||||
@POST("v1/customer/card/reissue")
|
||||
suspend fun reissueCard(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Body body: ReissueCardRequest,
|
||||
): ApiResponse<ReissueCardResponse>
|
||||
|
||||
@POST("v1/customer/card/withdraw/data")
|
||||
suspend fun getWithdrawData(
|
||||
@Header("Authorization") authHeader: String,
|
||||
|
|
@ -96,4 +108,11 @@ interface TangemPayApi {
|
|||
@Header("Authorization") authHeader: String,
|
||||
@Body body: WithdrawRequest,
|
||||
): ApiResponse<WithdrawResponse>
|
||||
|
||||
@PATCH("v1/customer/card/{card_id}")
|
||||
suspend fun updateCard(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Body body: UpdateCardRequest,
|
||||
@Path("card_id") cardId: String,
|
||||
): ApiResponse<UpdateCardDisplayNameResponse>
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.datasource.api.pay.models.request
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ReissueCardRequest(
|
||||
@Json(name = "card_id") val cardId: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.datasource.api.pay.models.request
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UpdateCardRequest(
|
||||
@Json(name = "display_name") val displayName: String? = null,
|
||||
@Json(name = "card_limit") val cardLimit: CardLimit? = null,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardLimit(
|
||||
@Json(name = "amount") val amount: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.datasource.api.pay.models.response
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CustomerMeResponse(
|
||||
|
|
@ -29,6 +30,9 @@ data class CustomerMeResponse(
|
|||
@Json(name = "status") val status: Status,
|
||||
@Json(name = "updated_at") val updatedAt: String,
|
||||
@Json(name = "payment_account_id") val paymentAccountId: String,
|
||||
@Json(name = "display_name") val displayName: String?,
|
||||
@Json(name = "actual_card_limit") val actualCardLimit: CardLimit?,
|
||||
@Json(name = "admin_card_limit") val adminCardLimit: CardLimit?,
|
||||
) {
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class Status {
|
||||
|
|
@ -70,6 +74,12 @@ data class CustomerMeResponse(
|
|||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardLimit(
|
||||
@Json(name = "amount") val amount: BigDecimal,
|
||||
@Json(name = "period_type") val periodType: String,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PaymentAccount(
|
||||
@Json(name = "id") val id: String,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.datasource.api.pay.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class FeeResponse(
|
||||
@Json(name = "result") val result: Result,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Result(
|
||||
@Json(name = "type") val type: String,
|
||||
@Json(name = "amount") val amount: String,
|
||||
@Json(name = "currency") val currency: String,
|
||||
@Json(name = "description") val description: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.datasource.api.pay.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ReissueCardResponse(
|
||||
@Json(name = "result") val result: Result,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Result(
|
||||
@Json(name = "order_id") val orderId: String,
|
||||
@Json(name = "status") val status: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.datasource.api.pay.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UpdateCardDisplayNameResponse(
|
||||
@Json(name = "result") val result: Result?,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Result(
|
||||
@Json(name = "display_name") val displayName: String?,
|
||||
)
|
||||
}
|
||||
|
|
@ -42,7 +42,8 @@ data class UserTokensResponse(
|
|||
return otherToken.contractAddress == this.contractAddress &&
|
||||
otherToken.networkId == this.networkId &&
|
||||
otherToken.derivationPath == this.derivationPath &&
|
||||
otherToken.decimals == this.decimals
|
||||
otherToken.decimals == this.decimals &&
|
||||
otherToken.dynamicAddressesEnabled == this.dynamicAddressesEnabled
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = calculateHashCode(
|
||||
|
|
@ -50,6 +51,7 @@ data class UserTokensResponse(
|
|||
networkId.hashCode(),
|
||||
derivationPath.hashCode(),
|
||||
decimals.hashCode(),
|
||||
dynamicAddressesEnabled.hashCode(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.tangem.lib.auth.ExpressAuthProvider
|
|||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -23,13 +22,11 @@ internal object ApiConfigsModule {
|
|||
fun provideExpressConfig(
|
||||
environmentConfig: EnvironmentConfig,
|
||||
expressAuthProvider: ExpressAuthProvider,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
appInfoProvider: AppInfoProvider,
|
||||
): ApiConfig {
|
||||
return Express(
|
||||
environmentConfig = environmentConfig,
|
||||
expressAuthProvider = expressAuthProvider,
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
|
|
@ -48,55 +45,47 @@ internal object ApiConfigsModule {
|
|||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideTangemTechConfig(
|
||||
appVersionProvider: AppVersionProvider,
|
||||
authProvider: AuthProvider,
|
||||
appInfoProvider: AppInfoProvider,
|
||||
): ApiConfig = TangemTech(
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = authProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
fun provideTangemTechConfig(authProvider: AuthProvider, appInfoProvider: AppInfoProvider): ApiConfig {
|
||||
return TangemTech(
|
||||
authProvider = authProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideNewsConfig(
|
||||
appVersionProvider: AppVersionProvider,
|
||||
authProvider: AuthProvider,
|
||||
appInfoProvider: AppInfoProvider,
|
||||
): ApiConfig = News(
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
authProvider = authProvider,
|
||||
)
|
||||
fun provideNewsConfig(authProvider: AuthProvider, appInfoProvider: AppInfoProvider): ApiConfig {
|
||||
return News(
|
||||
appInfoProvider = appInfoProvider,
|
||||
authProvider = authProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideYieldSupplyConfig(
|
||||
environmentConfig: EnvironmentConfig,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
authProvider: AuthProvider,
|
||||
appInfoProvider: AppInfoProvider,
|
||||
): ApiConfig = YieldSupply(
|
||||
environmentConfig = environmentConfig,
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = authProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
): ApiConfig {
|
||||
return YieldSupply(
|
||||
environmentConfig = environmentConfig,
|
||||
authProvider = authProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideTangemPayBffConfig(
|
||||
environmentConfig: EnvironmentConfig,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
): ApiConfig = TangemPay.Bff(environmentConfig, appVersionProvider)
|
||||
fun provideTangemPayBffConfig(environmentConfig: EnvironmentConfig, appInfoProvider: AppInfoProvider): ApiConfig {
|
||||
return TangemPay.Bff(environmentConfig, appInfoProvider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideTangemPayAuthConfig(
|
||||
environmentConfig: EnvironmentConfig,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
): ApiConfig = TangemPay.Auth(environmentConfig, appVersionProvider)
|
||||
fun provideTangemPayAuthConfig(environmentConfig: EnvironmentConfig, appInfoProvider: AppInfoProvider): ApiConfig {
|
||||
return TangemPay.Auth(environmentConfig, appInfoProvider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
|
|
@ -112,14 +101,9 @@ internal object ApiConfigsModule {
|
|||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideGaslessServiceConfig(
|
||||
appVersionProvider: AppVersionProvider,
|
||||
authProvider: AuthProvider,
|
||||
appInfoProvider: AppInfoProvider,
|
||||
): ApiConfig {
|
||||
fun provideGaslessServiceConfig(authProvider: AuthProvider, appInfoProvider: AppInfoProvider): ApiConfig {
|
||||
return GaslessTxService(
|
||||
authProvider = authProvider,
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.info.AndroidAppInfoProvider
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
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 AppInfoModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAppInfoProvider(appVersionProvider: AppVersionProvider): AppInfoProvider {
|
||||
return AndroidAppInfoProvider(appVersionProvider)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ class MoshiModule {
|
|||
.withSubtype(PaymentAccountStatusValueDM.NotCreated::class.java, "not_created")
|
||||
.withSubtype(PaymentAccountStatusValueDM.UnderReview::class.java, "kyc_status")
|
||||
.withSubtype(PaymentAccountStatusValueDM.IssuingCard::class.java, "issuing_card")
|
||||
.withSubtype(PaymentAccountStatusValueDM.ActiveCard::class.java, "active_card")
|
||||
.withSubtype(PaymentAccountStatusValueDM.ActiveAccount::class.java, "active_account")
|
||||
.withSubtype(PaymentAccountStatusValueDM.CardIssueFailed::class.java, "card_issue_failed"),
|
||||
)
|
||||
.add(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package com.tangem.datasource.di
|
|||
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.visa.DefaultTangemPayCardFrozenStateStore
|
||||
import com.tangem.datasource.local.visa.DefaultTangemPayReissueCardStore
|
||||
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
||||
import com.tangem.datasource.local.visa.TangemPayReissueCardStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -20,4 +22,12 @@ internal object TangemPayStoresModule {
|
|||
dataStore = RuntimeDataStore(),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemPayReissueCardStore(): TangemPayReissueCardStore {
|
||||
return DefaultTangemPayReissueCardStore(
|
||||
feeStore = RuntimeDataStore(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ object ExpressUtils {
|
|||
private const val BATCH_ID_CHANGENOW = "BB000013"
|
||||
private const val BATCH_ID_PARTNER = "AF990015"
|
||||
|
||||
fun getRefCode(userWallet: UserWallet, appPreferencesStore: AppPreferencesStore): String? {
|
||||
fun getRefCode(userWallet: UserWallet?, appPreferencesStore: AppPreferencesStore): String? {
|
||||
return when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.datasource.info
|
||||
|
||||
import android.os.Build
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class AndroidAppInfoProvider @Inject constructor(
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
) : AppInfoProvider {
|
||||
override val platform: String
|
||||
get() = "Android"
|
||||
override val device: String
|
||||
get() = "${Build.MANUFACTURER} ${Build.MODEL}"
|
||||
override val osVersion: String
|
||||
get() = Build.VERSION.RELEASE
|
||||
override val language: String
|
||||
get() = Locale.getDefault().language
|
||||
override val timezone: String
|
||||
get() = TimeZone.getDefault().id
|
||||
override val appVersion: String
|
||||
get() = appVersionProvider.versionName
|
||||
override val isHuaweiDevice: Boolean
|
||||
get() = Build.MANUFACTURER.equals("HUAWEI", ignoreCase = true) ||
|
||||
Build.BRAND.equals("HUAWEI", ignoreCase = true)
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ data class EnvironmentConfig(
|
|||
val mercuryoWidgetId: String = "",
|
||||
val mercuryoSecret: String = "",
|
||||
val amplitudeApiKey: String = "",
|
||||
val amplitudeApiKeyDev: String? = null,
|
||||
val appsFlyerApiKey: String = "",
|
||||
val appsAppId: String = "",
|
||||
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ internal object GeneratedEnvironmentConfigConverter {
|
|||
mercuryoSecret = GeneratedEnvironmentConfig.mercuryoSecret,
|
||||
blockchainSdkConfig = createBlockchainSdkConfig(),
|
||||
amplitudeApiKey = GeneratedEnvironmentConfig.amplitudeApiKey,
|
||||
amplitudeApiKeyDev = GeneratedEnvironmentConfig.amplitudeApiKeyDev,
|
||||
appsFlyerApiKey = AppsFlyer.appsFlyerDevKey,
|
||||
appsAppId = AppsFlyer.appsFlyerAppID,
|
||||
walletConnectProjectId = GeneratedEnvironmentConfig.walletConnectProjectId,
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ sealed interface NetworkStatusDM {
|
|||
/**
|
||||
* Verified
|
||||
*
|
||||
* @property networkId network id
|
||||
* @property derivationPath derivation path
|
||||
* @property selectedAddress selected address
|
||||
* @property availableAddresses available addresses
|
||||
* @property amounts amounts
|
||||
* @property networkId network id
|
||||
* @property derivationPath derivation path
|
||||
* @property selectedAddress selected address
|
||||
* @property availableAddresses available addresses
|
||||
* @property amounts amounts
|
||||
* @property yieldSupplyStatuses yield supply statuses
|
||||
*/
|
||||
@NameLabel("amounts")
|
||||
data class Verified(
|
||||
|
|
@ -65,6 +66,11 @@ sealed interface NetworkStatusDM {
|
|||
@Json(name = "error_message") val errorMessage: String,
|
||||
) : NetworkStatusDM
|
||||
|
||||
/**
|
||||
* Id
|
||||
*
|
||||
* @property value blockchain id [com.tangem.blockchain.common.Blockchain.id]
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ID(
|
||||
@Json(name = "value") val value: String,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PR
|
|||
import com.tangem.datasource.local.preferences.PreferencesKeys.SHOULD_SHOW_RING_PROMO_KEY
|
||||
import com.tangem.datasource.local.preferences.utils.CleanupKeyMigration
|
||||
import com.tangem.datasource.local.preferences.utils.SharedPreferencesKeyMigration
|
||||
import com.tangem.datasource.local.preferences.utils.SwapCurrencyIdMigration
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
|
||||
|
|
@ -80,6 +81,7 @@ internal object PreferencesDataStore {
|
|||
legacyKeyName = LEGACY_DEFAULT_KEY_NAME,
|
||||
keyName = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY.name,
|
||||
),
|
||||
SwapCurrencyIdMigration(),
|
||||
CleanupKeyMigration(key = APP_LOGS_KEY),
|
||||
CleanupKeyMigration(key = IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY),
|
||||
CleanupKeyMigration(key = IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY),
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ object PreferencesKeys {
|
|||
|
||||
val HAS_HAD_FIRST_TOP_UP_KEY by lazy { stringPreferencesKey(name = "hasHadFirstTopUp") }
|
||||
|
||||
val PENDING_DISCOVERY_SYNC_KEY by lazy { stringPreferencesKey(name = "pendingDiscoverySync") }
|
||||
val PENDING_ASSETS_DISCOVERY_KEY by lazy { stringPreferencesKey(name = "pendingAssetsDiscovery") }
|
||||
|
||||
// region Notifications
|
||||
val NOTIFICATIONS_APPLICATION_ID_KEY by lazy { stringPreferencesKey(name = "notificationsApplicationId") }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,151 @@
|
|||
package com.tangem.datasource.local.preferences.utils
|
||||
|
||||
import androidx.datastore.core.DataMigration
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
|
||||
/**
|
||||
* Migrates cached CryptoCurrency.ID strings from old blockchain.id format to new networkId format.
|
||||
*
|
||||
* After refactoring, Network.rawId stores backendId values (e.g. "ethereum") instead of
|
||||
* blockchain.id values (e.g. "ETH"). CryptoCurrency.ID body contains this value, so cached IDs
|
||||
* like "coin⟨ETH⟩ethereum" must become "coin⟨ethereum⟩ethereum".
|
||||
*
|
||||
* Affected DataStore keys: [PreferencesKeys.SWAP_TRANSACTIONS_KEY],
|
||||
* [PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY].
|
||||
*/
|
||||
internal class SwapCurrencyIdMigration : DataMigration<Preferences> {
|
||||
|
||||
override suspend fun shouldMigrate(currentData: Preferences): Boolean {
|
||||
return currentData.contains(PreferencesKeys.SWAP_TRANSACTIONS_KEY) ||
|
||||
currentData.contains(PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY)
|
||||
}
|
||||
|
||||
override suspend fun migrate(currentData: Preferences): Preferences {
|
||||
val mutablePrefs = currentData.toMutablePreferences()
|
||||
|
||||
currentData[PreferencesKeys.SWAP_TRANSACTIONS_KEY]?.let { json ->
|
||||
mutablePrefs[PreferencesKeys.SWAP_TRANSACTIONS_KEY] = migrateJson(json)
|
||||
}
|
||||
|
||||
currentData[PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY]?.let { json ->
|
||||
mutablePrefs[PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY] = migrateJson(json)
|
||||
}
|
||||
|
||||
return mutablePrefs.toPreferences()
|
||||
}
|
||||
|
||||
override suspend fun cleanUp() {
|
||||
// nothing to clean up
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces old blockchain.id values with networkId values inside CryptoCurrency.ID strings
|
||||
* found anywhere in the JSON. Works by finding all `⟨oldId⟩` and `⟨oldId→` patterns and
|
||||
* replacing the old ID with the new one.
|
||||
*/
|
||||
private fun migrateJson(json: String): String {
|
||||
var result = json
|
||||
|
||||
for ((oldId, newId) in BLOCKCHAIN_ID_TO_NETWORK_ID) {
|
||||
// Body without derivation path: ⟨oldId⟩ → ⟨newId⟩
|
||||
result = result.replace("$BODY_START$oldId$BODY_END", "$BODY_START$newId$BODY_END")
|
||||
// Body with derivation path: ⟨oldId→ → ⟨newId→
|
||||
result = result.replace(
|
||||
"$BODY_START$oldId$DERIVATION_DELIMITER",
|
||||
"$BODY_START$newId$DERIVATION_DELIMITER",
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val BODY_START = '\u27E8' // ⟨
|
||||
const val BODY_END = '\u27E9' // ⟩
|
||||
const val DERIVATION_DELIMITER = '\u2192' // →
|
||||
|
||||
/** Mapping of old blockchain.id → new networkId (only entries where values differ). */
|
||||
val BLOCKCHAIN_ID_TO_NETWORK_ID = mapOf(
|
||||
"ARBITRUM-ONE" to "arbitrum-one",
|
||||
"ARBITRUM/test" to "arbitrum-one/test",
|
||||
"AVALANCHE" to "avalanche",
|
||||
"AVALANCHE/test" to "avalanche/test",
|
||||
"BINANCE" to "binancecoin",
|
||||
"BINANCE/test" to "binancecoin/test",
|
||||
"BSC" to "binance-smart-chain",
|
||||
"BSC/test" to "binance-smart-chain/test",
|
||||
"BTC" to "bitcoin",
|
||||
"BTC/test" to "bitcoin/test",
|
||||
"BCH" to "bitcoin-cash",
|
||||
"BCH/test" to "bitcoin-cash/test",
|
||||
"CARDANO-S" to "cardano",
|
||||
"DOGE" to "dogecoin",
|
||||
"DUC" to "ducatus",
|
||||
"ETH" to "ethereum",
|
||||
"ETH/test" to "ethereum/test",
|
||||
"ETC" to "ethereum-classic",
|
||||
"ETC/test" to "ethereum-classic/test",
|
||||
"ETH-Pow" to "ethereum-pow-iou",
|
||||
"ETH-Pow/test" to "ethereum-pow-iou/test",
|
||||
"FTM" to "fantom",
|
||||
"FTM/test" to "fantom/test",
|
||||
"GNO" to "xdai",
|
||||
"KAS" to "kaspa",
|
||||
"KAS/test" to "kaspa/test",
|
||||
"KAVA" to "kava",
|
||||
"KAVA/test" to "kava/test",
|
||||
"Kusama" to "kusama",
|
||||
"LTC" to "litecoin",
|
||||
"NEAR" to "near-protocol",
|
||||
"NEAR/test" to "near-protocol/test",
|
||||
"NEXA" to "nexa",
|
||||
"NEXA/test" to "nexa/test",
|
||||
"OPTIMISM" to "optimistic-ethereum",
|
||||
"Polkadot" to "polkadot",
|
||||
"POLYGON" to "polygon-pos",
|
||||
"POLYGON/test" to "polygon-pos/test",
|
||||
"RSK" to "rootstock",
|
||||
"SOLANA" to "solana",
|
||||
"SOLANA/test" to "solana/test",
|
||||
"TELOS" to "telos",
|
||||
"TELOS/test" to "telos/test",
|
||||
"The-Open-Network" to "the-open-network",
|
||||
"The-Open-Network/test" to "the-open-network/test",
|
||||
"TRON" to "tron",
|
||||
"TRON/test" to "tron/test",
|
||||
"XLM" to "stellar",
|
||||
"XLM/test" to "stellar/test",
|
||||
"XRP" to "xrp",
|
||||
"XTZ" to "tezos",
|
||||
"DASH" to "dash",
|
||||
"xdc" to "xdc-network",
|
||||
"xdc/test" to "xdc-network/test",
|
||||
"hedera" to "hedera-hashgraph",
|
||||
"hedera/test" to "hedera-hashgraph/test",
|
||||
"areon" to "areon-network",
|
||||
"areon/test" to "areon-network/test",
|
||||
"pls" to "pulsechain",
|
||||
"pls/test" to "pulsechain/test",
|
||||
"zkSyncEra" to "zksync",
|
||||
"zkSyncEra/test" to "zksync/test",
|
||||
"polygonZkEVM" to "polygon-zkevm",
|
||||
"polygonZkEVM/test" to "polygon-zkevm/test",
|
||||
"flare" to "flare-network",
|
||||
"flare/test" to "flare-network/test",
|
||||
"playa3ull" to "playa3ull-games",
|
||||
"sei" to "sei-network",
|
||||
"sei/test" to "sei-network/test",
|
||||
"casper" to "casper-network",
|
||||
"casper/test" to "casper-network/test",
|
||||
"odyssey" to "dione",
|
||||
"odyssey/test" to "dione/test",
|
||||
"hyperliquid" to "hyperevm",
|
||||
"hyperliquid/test" to "hyperevm/test",
|
||||
"quai" to "quai-network",
|
||||
"quai/test" to "quai-network/test",
|
||||
"manta/test" to "manta-pacific/test",
|
||||
"dischain" to "ethereumfair",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.datasource.local.visa
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.domain.models.pay.TangemPayReissueCardFee
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
internal class DefaultTangemPayReissueCardStore(
|
||||
private val feeStore: RuntimeDataStore<TangemPayReissueCardFee>,
|
||||
) : TangemPayReissueCardStore {
|
||||
|
||||
override suspend fun storeReissueFee(
|
||||
userWalletId: UserWalletId,
|
||||
tangemPayReissueCardFee: TangemPayReissueCardFee,
|
||||
) {
|
||||
feeStore.store(userWalletId.stringValue, tangemPayReissueCardFee)
|
||||
}
|
||||
|
||||
override suspend fun getReissueFee(userWalletId: UserWalletId): TangemPayReissueCardFee? {
|
||||
return feeStore.getSyncOrNull(userWalletId.stringValue)
|
||||
}
|
||||
|
||||
override suspend fun storeReissueOrderId(cardId: String, orderId: String) {
|
||||
// TODO v_rodionov: #[REDACTED_TASK_KEY] store orderId in app prefs
|
||||
}
|
||||
|
||||
override suspend fun getOrderId(cardId: String): String? {
|
||||
// TODO v_rodionov: #[REDACTED_TASK_KEY] store orderId in app prefs
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.datasource.local.visa
|
||||
|
||||
import com.tangem.domain.models.pay.TangemPayReissueCardFee
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
interface TangemPayReissueCardStore {
|
||||
|
||||
suspend fun storeReissueFee(userWalletId: UserWalletId, tangemPayReissueCardFee: TangemPayReissueCardFee)
|
||||
|
||||
suspend fun getReissueFee(userWalletId: UserWalletId): TangemPayReissueCardFee?
|
||||
|
||||
suspend fun storeReissueOrderId(cardId: String, orderId: String)
|
||||
|
||||
suspend fun getOrderId(cardId: String): String?
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ package com.tangem.datasource.local.visa.entity
|
|||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.domain.models.kyc.KycStatus
|
||||
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||
import dev.onenowy.moshipolymorphicadapter.PolymorphicAdapterType
|
||||
import dev.onenowy.moshipolymorphicadapter.annotations.NameLabel
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -37,17 +38,14 @@ sealed interface PaymentAccountStatusValueDM {
|
|||
@Json(name = "issuing_card") val marker: Boolean = true,
|
||||
) : PaymentAccountStatusValueDM
|
||||
|
||||
@NameLabel("active_card")
|
||||
data class ActiveCard(
|
||||
@Json(name = "active_card") val isLocked: Boolean,
|
||||
@NameLabel("active_account")
|
||||
data class ActiveAccount(
|
||||
@Json(name = "customer_id") val customerId: String,
|
||||
@Json(name = "card_id") val cardId: String,
|
||||
@Json(name = "last_four_digits") val lastFourDigits: String,
|
||||
@Json(name = "currency_code") val currencyCode: String,
|
||||
@Json(name = "deposit_address") val depositAddress: String?,
|
||||
@Json(name = "is_pin_set") val isPinSet: Boolean,
|
||||
@Json(name = "fiat_balance") val fiatBalance: FiatBalanceDM,
|
||||
@Json(name = "crypto_balance") val cryptoBalance: CryptoBalanceDM,
|
||||
@Json(name = "cards") val cards: List<TangemPayCard>,
|
||||
) : PaymentAccountStatusValueDM
|
||||
|
||||
@NameLabel("card_issue_failed")
|
||||
|
|
@ -70,4 +68,15 @@ sealed interface PaymentAccountStatusValueDM {
|
|||
@Json(name = "token_contract_address") val tokenContractAddress: String,
|
||||
@Json(name = "balance") val balance: BigDecimal,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TangemPayCard(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "has_pin_code") val hasPinCode: Boolean,
|
||||
@Json(name = "display_name") val displayName: String?,
|
||||
@Json(name = "actual_daily_limit") val actualDailyLimit: SerializedBigDecimal?,
|
||||
@Json(name = "admin_daily_limit") val adminDailyLimit: SerializedBigDecimal?,
|
||||
@Json(name = "is_frozen") val isFrozen: Boolean,
|
||||
@Json(name = "last_digits") val lastDigits: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.datasource.utils
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
|
|
@ -34,7 +35,11 @@ class MoshiDataStoreSerializer<T>(
|
|||
|
||||
override suspend fun readFrom(input: InputStream): T {
|
||||
return input.bufferedReader().use { reader ->
|
||||
adapter.fromJson(reader.readText()) ?: defaultValue
|
||||
try {
|
||||
adapter.fromJson(reader.readText()) ?: defaultValue
|
||||
} catch (e: Exception) {
|
||||
throw CorruptionException("Failed to deserialize data", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
package com.tangem.datasource.utils
|
||||
|
||||
import android.os.Build
|
||||
import com.tangem.datasource.api.common.AuthProvider
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
import com.tangem.datasource.utils.RequestHeader.CacheControlHeader.checkHeaderValueOrEmpty
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
/**
|
||||
|
|
@ -29,17 +26,16 @@ sealed class RequestHeader(vararg pairs: Pair<String, ProviderSuspend<String>>)
|
|||
)
|
||||
|
||||
class AppVersionPlatformHeaders(
|
||||
appVersionProvider: AppVersionProvider,
|
||||
appInfoProvider: AppInfoProvider,
|
||||
) : RequestHeader(
|
||||
"system_version" to ProviderSuspend { appInfoProvider.osVersion },
|
||||
"version" to ProviderSuspend { appVersionProvider.versionName },
|
||||
"version" to ProviderSuspend { appInfoProvider.appVersion },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"language" to ProviderSuspend { appInfoProvider.language.checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
"device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() },
|
||||
"device" to ProviderSuspend { appInfoProvider.device.checkHeaderValueOrEmpty() },
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -51,21 +51,18 @@ class ApiConfigTest {
|
|||
Express(
|
||||
environmentConfig = environmentConfig,
|
||||
expressAuthProvider = mockk(),
|
||||
appVersionProvider = mockk(),
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.YieldSupply -> {
|
||||
YieldSupply(
|
||||
environmentConfig = environmentConfig,
|
||||
appVersionProvider = mockk(),
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.TangemTech -> {
|
||||
TangemTech(
|
||||
appVersionProvider = mockk(),
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
|
|
@ -73,23 +70,21 @@ class ApiConfigTest {
|
|||
ApiConfig.ID.StakeKit -> StakeKit(stakeKitAuthProvider = mockk())
|
||||
ApiConfig.ID.TangemPay -> TangemPay.Bff(
|
||||
environmentConfig = environmentConfig,
|
||||
appVersionProvider = mockk(),
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
ApiConfig.ID.TangemPayAuth -> TangemPay.Auth(
|
||||
environmentConfig = environmentConfig,
|
||||
appVersionProvider = mockk(),
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
ApiConfig.ID.BlockAid -> BlockAid(environmentConfig = environmentConfig)
|
||||
ApiConfig.ID.MoonPay -> MoonPay()
|
||||
ApiConfig.ID.P2PEthPool -> P2PEthPool(p2pAuthProvider = mockk())
|
||||
ApiConfig.ID.News -> News(
|
||||
appVersionProvider = mockk(),
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
ApiConfig.ID.GaslessTxService -> GaslessTxService(
|
||||
authProvider = appAuthProvider,
|
||||
appVersionProvider = mockk(),
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import com.tangem.lib.auth.StakeKitAuthProvider
|
|||
import com.tangem.test.core.ProvideTestModels
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
|
|
@ -38,7 +37,6 @@ import java.util.TimeZone
|
|||
internal class ProdApiConfigsManagerTest {
|
||||
|
||||
private val environmentConfig = createMockEnvironmentConfig()
|
||||
private val appVersionProvider = mockk<AppVersionProvider>()
|
||||
private val expressAuthProvider = mockk<ExpressAuthProvider>()
|
||||
private val stakeKitAuthProvider = mockk<StakeKitAuthProvider>()
|
||||
private val p2pEthPoolAuthProvider = mockk<P2PEthPoolAuthProvider>()
|
||||
|
|
@ -52,14 +50,13 @@ internal class ProdApiConfigsManagerTest {
|
|||
@BeforeEach
|
||||
fun setup() {
|
||||
clearMocks(
|
||||
appVersionProvider,
|
||||
expressAuthProvider,
|
||||
stakeKitAuthProvider,
|
||||
appAuthProvider,
|
||||
appInfoProvider,
|
||||
)
|
||||
|
||||
every { appVersionProvider.versionName } returns VERSION_NAME
|
||||
every { appInfoProvider.appVersion } returns VERSION_NAME
|
||||
every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID
|
||||
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
|
||||
every { p2pEthPoolAuthProvider.getApiKey() } returns P2P_API_KEY
|
||||
|
|
@ -71,6 +68,8 @@ internal class ProdApiConfigsManagerTest {
|
|||
coEvery { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
|
||||
|
||||
every { appInfoProvider.osVersion } returns "Android 16"
|
||||
every { appInfoProvider.language } returns Locale.getDefault().toLanguageTag()
|
||||
every { appInfoProvider.device } returns "${Build.MANUFACTURER} ${Build.MODEL}"
|
||||
|
||||
manager = ProdApiConfigsManager(apiConfigs = createApiConfigs())
|
||||
}
|
||||
|
|
@ -94,21 +93,18 @@ internal class ProdApiConfigsManagerTest {
|
|||
Express(
|
||||
environmentConfig = environmentConfig,
|
||||
expressAuthProvider = expressAuthProvider,
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.YieldSupply -> {
|
||||
YieldSupply(
|
||||
environmentConfig = environmentConfig,
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.TangemTech -> {
|
||||
TangemTech(
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
|
|
@ -116,23 +112,21 @@ internal class ProdApiConfigsManagerTest {
|
|||
ApiConfig.ID.StakeKit -> StakeKit(stakeKitAuthProvider = stakeKitAuthProvider)
|
||||
ApiConfig.ID.TangemPay -> TangemPay.Bff(
|
||||
environmentConfig = environmentConfig,
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
ApiConfig.ID.TangemPayAuth -> TangemPay.Auth(
|
||||
environmentConfig = environmentConfig,
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
ApiConfig.ID.BlockAid -> BlockAid(environmentConfig = environmentConfig)
|
||||
ApiConfig.ID.MoonPay -> MoonPay()
|
||||
ApiConfig.ID.P2PEthPool -> P2PEthPool(p2pAuthProvider = p2pEthPoolAuthProvider)
|
||||
ApiConfig.ID.News -> News(
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
ApiConfig.ID.GaslessTxService -> GaslessTxService(
|
||||
authProvider = appAuthProvider,
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
|
|
@ -195,7 +189,7 @@ internal class ProdApiConfigsManagerTest {
|
|||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
|
|
@ -218,7 +212,7 @@ internal class ProdApiConfigsManagerTest {
|
|||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
|
|
@ -241,7 +235,7 @@ internal class ProdApiConfigsManagerTest {
|
|||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
|
|
@ -316,7 +310,7 @@ internal class ProdApiConfigsManagerTest {
|
|||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
|
|
@ -394,7 +388,7 @@ internal class ProdApiConfigsManagerTest {
|
|||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,224 @@
|
|||
package com.tangem.datasource.local.preferences.utils
|
||||
|
||||
import androidx.datastore.preferences.core.mutablePreferencesOf
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class SwapCurrencyIdMigrationTest {
|
||||
|
||||
private val migration = SwapCurrencyIdMigration()
|
||||
|
||||
// region shouldMigrate
|
||||
|
||||
@Test
|
||||
fun `shouldMigrate returns true when swap transactions key exists`() = runTest {
|
||||
val prefs = mutablePreferencesOf(PreferencesKeys.SWAP_TRANSACTIONS_KEY to "[]")
|
||||
|
||||
assertThat(migration.shouldMigrate(prefs)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigrate returns true when last swapped currency key exists`() = runTest {
|
||||
val prefs = mutablePreferencesOf(PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY to "[]")
|
||||
|
||||
assertThat(migration.shouldMigrate(prefs)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigrate returns true when both keys exist`() = runTest {
|
||||
val prefs = mutablePreferencesOf(
|
||||
PreferencesKeys.SWAP_TRANSACTIONS_KEY to "[]",
|
||||
PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY to "[]",
|
||||
)
|
||||
|
||||
assertThat(migration.shouldMigrate(prefs)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigrate returns false when no keys exist`() = runTest {
|
||||
val prefs = mutablePreferencesOf()
|
||||
|
||||
assertThat(migration.shouldMigrate(prefs)).isFalse()
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region migrate — coin IDs without derivation path
|
||||
|
||||
@Test
|
||||
fun `migrates simple coin ID - ETH to ethereum`() = runTest {
|
||||
val result = migrateLastSwapped(currencyIdJson("coin${BS}ETH${BE}ethereum"))
|
||||
|
||||
assertThat(result).isEqualTo(currencyIdJson("coin${BS}ethereum${BE}ethereum"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migrates simple coin ID - BTC to bitcoin`() = runTest {
|
||||
val result = migrateLastSwapped(currencyIdJson("coin${BS}BTC${BE}bitcoin"))
|
||||
|
||||
assertThat(result).isEqualTo(currencyIdJson("coin${BS}bitcoin${BE}bitcoin"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migrates BSC to binance-smart-chain`() = runTest {
|
||||
val result = migrateLastSwapped(currencyIdJson("coin${BS}BSC${BE}binancecoin"))
|
||||
|
||||
assertThat(result).isEqualTo(currencyIdJson("coin${BS}binance-smart-chain${BE}binancecoin"))
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region migrate — coin IDs with derivation path
|
||||
|
||||
@Test
|
||||
fun `migrates coin ID with derivation path`() = runTest {
|
||||
val result = migrateLastSwapped(currencyIdJson("coin${BS}ETH${DP}12367123${BE}ethereum"))
|
||||
|
||||
assertThat(result).isEqualTo(currencyIdJson("coin${BS}ethereum${DP}12367123${BE}ethereum"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migrates POLYGON with derivation path`() = runTest {
|
||||
val result = migrateLastSwapped(currencyIdJson("coin${BS}POLYGON${DP}99999${BE}polygon-pos"))
|
||||
|
||||
assertThat(result).isEqualTo(currencyIdJson("coin${BS}polygon-pos${DP}99999${BE}polygon-pos"))
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region migrate — token IDs
|
||||
|
||||
@Test
|
||||
fun `migrates token ID with contract address`() = runTest {
|
||||
val result = migrateLastSwapped(currencyIdJson("token${BS}ETH${BE}usdt${CA}0xdAC17"))
|
||||
|
||||
assertThat(result).isEqualTo(currencyIdJson("token${BS}ethereum${BE}usdt${CA}0xdAC17"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migrates token ID with derivation path and contract address`() = runTest {
|
||||
val result = migrateLastSwapped(
|
||||
currencyIdJson("token${BS}ETH${DP}12345${BE}usdt${CA}0xdAC17"),
|
||||
)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
currencyIdJson("token${BS}ethereum${DP}12345${BE}usdt${CA}0xdAC17"),
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region migrate — swap transactions (both from and to IDs)
|
||||
|
||||
@Test
|
||||
fun `migrates both fromCryptoCurrencyId and toCryptoCurrencyId`() = runTest {
|
||||
val from = "coin${BS}ETH${BE}ethereum"
|
||||
val to = "coin${BS}BTC${BE}bitcoin"
|
||||
val oldJson = "[{\"fromCryptoCurrencyId\":\"$from\",\"toCryptoCurrencyId\":\"$to\"}]"
|
||||
|
||||
val expectedFrom = "coin${BS}ethereum${BE}ethereum"
|
||||
val expectedTo = "coin${BS}bitcoin${BE}bitcoin"
|
||||
val expected = "[{\"fromCryptoCurrencyId\":\"$expectedFrom\",\"toCryptoCurrencyId\":\"$expectedTo\"}]"
|
||||
|
||||
val result = migrateSwapTransactions(oldJson)
|
||||
|
||||
assertThat(result).isEqualTo(expected)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region migrate — no-op cases
|
||||
|
||||
@Test
|
||||
fun `does not modify already migrated IDs`() = runTest {
|
||||
val json = currencyIdJson("coin${BS}ethereum${BE}ethereum")
|
||||
|
||||
val result = migrateLastSwapped(json)
|
||||
|
||||
assertThat(result).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not modify IDs where blockchain id equals networkId`() = runTest {
|
||||
val json = currencyIdJson("coin${BS}cosmos${BE}cosmos")
|
||||
|
||||
val result = migrateLastSwapped(json)
|
||||
|
||||
assertThat(result).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not modify empty list`() = runTest {
|
||||
val result = migrateLastSwapped("[]")
|
||||
|
||||
assertThat(result).isEqualTo("[]")
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region migrate — multiple entries
|
||||
|
||||
@Test
|
||||
fun `migrates multiple entries in list`() = runTest {
|
||||
val old1 = currencyIdValue("coin${BS}ETH${BE}ethereum")
|
||||
val old2 = currencyIdValue("coin${BS}TRON${BE}tron")
|
||||
val oldJson = "[$old1,$old2]"
|
||||
|
||||
val new1 = currencyIdValue("coin${BS}ethereum${BE}ethereum")
|
||||
val new2 = currencyIdValue("coin${BS}tron${BE}tron")
|
||||
val expected = "[$new1,$new2]"
|
||||
|
||||
val result = migrateLastSwapped(oldJson)
|
||||
|
||||
assertThat(result).isEqualTo(expected)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region migrate — preserves unrelated keys
|
||||
|
||||
@Test
|
||||
fun `preserves unrelated preference keys`() = runTest {
|
||||
val unrelatedKey = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY
|
||||
val unrelatedValue = "some_value"
|
||||
val prefs = mutablePreferencesOf(
|
||||
PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY to currencyIdJson("coin${BS}ETH${BE}ethereum"),
|
||||
unrelatedKey to unrelatedValue,
|
||||
)
|
||||
|
||||
val result = migration.migrate(prefs)
|
||||
|
||||
assertThat(result[unrelatedKey]).isEqualTo(unrelatedValue)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region helpers
|
||||
|
||||
private suspend fun migrateLastSwapped(json: String): String? {
|
||||
val prefs = mutablePreferencesOf(PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY to json)
|
||||
val result = migration.migrate(prefs)
|
||||
return result[PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY]
|
||||
}
|
||||
|
||||
private suspend fun migrateSwapTransactions(json: String): String? {
|
||||
val prefs = mutablePreferencesOf(PreferencesKeys.SWAP_TRANSACTIONS_KEY to json)
|
||||
val result = migration.migrate(prefs)
|
||||
return result[PreferencesKeys.SWAP_TRANSACTIONS_KEY]
|
||||
}
|
||||
|
||||
private fun currencyIdJson(id: String): String = "[${currencyIdValue(id)}]"
|
||||
|
||||
private fun currencyIdValue(id: String): String = "{\"cryptoCurrencyId\":\"$id\"}"
|
||||
|
||||
private companion object {
|
||||
const val BS = '\u27E8' // ⟨ body start
|
||||
const val BE = '\u27E9' // ⟩ body end
|
||||
const val DP = '\u2192' // → derivation path delimiter
|
||||
const val CA = '\u2693' // ⚓ contract address delimiter
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue