Updated on 2026-08-14
This commit is contained in:
parent
9c5490bced
commit
c211d221ea
14 changed files with 251 additions and 113 deletions
|
|
@ -10,10 +10,8 @@ import com.tangem.common.card.EllipticCurve
|
||||||
import com.tangem.common.extensions.hexToBytes
|
import com.tangem.common.extensions.hexToBytes
|
||||||
import com.tangem.common.extensions.toHexString
|
import com.tangem.common.extensions.toHexString
|
||||||
import com.tangem.data.common.cache.CacheRegistry
|
import com.tangem.data.common.cache.CacheRegistry
|
||||||
import com.tangem.data.visa.utils.VisaConfig
|
import com.tangem.data.visa.config.VisaLibLoader
|
||||||
import com.tangem.data.visa.utils.VisaCurrencyFactory
|
import com.tangem.data.visa.utils.*
|
||||||
import com.tangem.data.visa.utils.VisaTxDetailsFactory
|
|
||||||
import com.tangem.data.visa.utils.VisaTxHistoryPagingSource
|
|
||||||
import com.tangem.datasource.api.common.response.getOrThrow
|
import com.tangem.datasource.api.common.response.getOrThrow
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
|
|
@ -24,8 +22,6 @@ import com.tangem.domain.visa.model.VisaTxHistoryItem
|
||||||
import com.tangem.domain.visa.repository.VisaRepository
|
import com.tangem.domain.visa.repository.VisaRepository
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.lib.visa.VisaContractInfoProvider
|
|
||||||
import com.tangem.lib.visa.api.VisaApi
|
|
||||||
import com.tangem.lib.visa.model.VisaTxHistoryResponse
|
import com.tangem.lib.visa.model.VisaTxHistoryResponse
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -35,9 +31,8 @@ import kotlinx.coroutines.withContext
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
internal class DefaultVisaRepository(
|
internal class DefaultVisaRepository(
|
||||||
private val visaContractInfoProvider: VisaContractInfoProvider,
|
private val visaLibLoader: VisaLibLoader,
|
||||||
private val tangemTechApi: TangemTechApi,
|
private val tangemTechApi: TangemTechApi,
|
||||||
private val visaApi: VisaApi,
|
|
||||||
private val cacheRegistry: CacheRegistry,
|
private val cacheRegistry: CacheRegistry,
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val userWalletsStore: UserWalletsStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
|
@ -76,9 +71,11 @@ internal class DefaultVisaRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchVisaCurrency(address: String) {
|
private suspend fun fetchVisaCurrency(address: String) {
|
||||||
|
val contractInfoProvider = visaLibLoader.getOrCreateProvider()
|
||||||
|
|
||||||
parZip(
|
parZip(
|
||||||
dispatchers.io,
|
dispatchers.io,
|
||||||
{ visaContractInfoProvider.getContractInfo(address) },
|
{ contractInfoProvider.getContractInfo(address) },
|
||||||
{ getFiatRate() },
|
{ getFiatRate() },
|
||||||
{ contractInfo, fiatRate ->
|
{ contractInfo, fiatRate ->
|
||||||
fetchedCurrencies.update { value ->
|
fetchedCurrencies.update { value ->
|
||||||
|
|
@ -97,6 +94,7 @@ internal class DefaultVisaRepository(
|
||||||
): Flow<PagingData<VisaTxHistoryItem>> {
|
): Flow<PagingData<VisaTxHistoryItem>> {
|
||||||
val userWallet = findVisaUserWallet(userWalletId)
|
val userWallet = findVisaUserWallet(userWalletId)
|
||||||
val cardPubKey = getCardPubKey(userWallet)
|
val cardPubKey = getCardPubKey(userWallet)
|
||||||
|
val api = visaLibLoader.getOrCreateApi()
|
||||||
val pager = Pager(
|
val pager = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = pageSize,
|
pageSize = pageSize,
|
||||||
|
|
@ -110,7 +108,7 @@ internal class DefaultVisaRepository(
|
||||||
isRefresh = isRefresh,
|
isRefresh = isRefresh,
|
||||||
),
|
),
|
||||||
cacheRegistry = cacheRegistry,
|
cacheRegistry = cacheRegistry,
|
||||||
visaApi = visaApi,
|
visaApi = api,
|
||||||
fetchedItems = fetchedHistoryItems,
|
fetchedItems = fetchedHistoryItems,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
|
|
@ -137,7 +135,7 @@ internal class DefaultVisaRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun makeAddress(userWalletId: UserWalletId): String {
|
private suspend fun makeAddress(userWalletId: UserWalletId): String {
|
||||||
if (IS_DEMO_MODE_ENABLED) return DEMO_ADDRESS
|
if (VisaConstants.IS_DEMO_MODE_ENABLED) return getDemoAddress()
|
||||||
|
|
||||||
val userWallet = findVisaUserWallet(userWalletId)
|
val userWallet = findVisaUserWallet(userWalletId)
|
||||||
val walletAddresses = makeWalletAddresses(userWallet)
|
val walletAddresses = makeWalletAddresses(userWallet)
|
||||||
|
|
@ -149,13 +147,13 @@ internal class DefaultVisaRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getFiatRate(): BigDecimal? {
|
private suspend fun getFiatRate(): BigDecimal? {
|
||||||
val fiatCurrencyId = VisaConfig.fiatCurrency.code.lowercase()
|
val fiatCurrencyId = VisaConstants.fiatCurrency.code.lowercase()
|
||||||
val quotes = tangemTechApi.getQuotes(
|
val quotes = tangemTechApi.getQuotes(
|
||||||
currencyId = fiatCurrencyId,
|
currencyId = fiatCurrencyId,
|
||||||
coinIds = VisaConfig.TOKEN_ID,
|
coinIds = VisaConstants.TOKEN_ID,
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
|
|
||||||
return quotes.quotes[VisaConfig.TOKEN_ID]?.price
|
return quotes.quotes[VisaConstants.TOKEN_ID]?.price
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeWalletAddresses(userWallet: UserWallet): Set<Address> {
|
private fun makeWalletAddresses(userWallet: UserWallet): Set<Address> {
|
||||||
|
|
@ -165,7 +163,7 @@ internal class DefaultVisaRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCardPubKey(userWallet: UserWallet): String {
|
private fun getCardPubKey(userWallet: UserWallet): String {
|
||||||
if (IS_DEMO_MODE_ENABLED) return DEMO_PUBLIC_KEY
|
if (VisaConstants.IS_DEMO_MODE_ENABLED) return getDemoPublicKey()
|
||||||
|
|
||||||
val cardWallet = userWallet.scanResponse.card.wallets.firstOrNull {
|
val cardWallet = userWallet.scanResponse.card.wallets.firstOrNull {
|
||||||
it.curve == EllipticCurve.Secp256k1
|
it.curve == EllipticCurve.Secp256k1
|
||||||
|
|
@ -189,12 +187,4 @@ internal class DefaultVisaRepository(
|
||||||
private fun getVisaCurrencyKey(address: String): String {
|
private fun getVisaCurrencyKey(address: String): String {
|
||||||
return "visa_currency_$address"
|
return "visa_currency_$address"
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
|
||||||
// Must be `false` in production
|
|
||||||
const val IS_DEMO_MODE_ENABLED = false
|
|
||||||
|
|
||||||
const val DEMO_ADDRESS = "0x40d8194b7168723ece51fa34d16825c60ba03dfa"
|
|
||||||
const val DEMO_PUBLIC_KEY = "02C2BBA0DA1E066EA968C1EB129499F6DEBC5FD82D70D61DCAF691CDB69AF5D8B9"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.tangem.data.visa.config
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
|
||||||
|
internal data class VisaConfig(
|
||||||
|
@Json(name = "testnet")
|
||||||
|
val testnet: Addresses,
|
||||||
|
@Json(name = "mainnet")
|
||||||
|
val mainnet: Addresses,
|
||||||
|
@Json(name = "txHistoryAPIAdditionalHeaders")
|
||||||
|
val header: Header,
|
||||||
|
) {
|
||||||
|
|
||||||
|
data class Addresses(
|
||||||
|
@Json(name = "paymentAccountRegistry")
|
||||||
|
val paymentAccountRegistry: String,
|
||||||
|
@Json(name = "bridgeProcessor")
|
||||||
|
val bridgeProcessor: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Header(
|
||||||
|
@Json(name = "x-asn")
|
||||||
|
val xAsn: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.tangem.data.visa.config
|
||||||
|
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.tangem.data.visa.BuildConfig
|
||||||
|
import com.tangem.data.visa.utils.VisaConstants
|
||||||
|
import com.tangem.datasource.asset.loader.AssetLoader
|
||||||
|
import com.tangem.datasource.di.NetworkMoshi
|
||||||
|
import com.tangem.lib.visa.VisaContractInfoProvider
|
||||||
|
import com.tangem.lib.visa.api.VisaApi
|
||||||
|
import com.tangem.lib.visa.api.VisaApiBuilder
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class VisaLibLoader @Inject constructor(
|
||||||
|
private val assetLoader: AssetLoader,
|
||||||
|
@NetworkMoshi private val moshi: Moshi,
|
||||||
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val createMutex = Mutex()
|
||||||
|
|
||||||
|
private var config: VisaConfig? = null
|
||||||
|
|
||||||
|
private var provider: VisaContractInfoProvider? = null
|
||||||
|
private var api: VisaApi? = null
|
||||||
|
|
||||||
|
suspend fun getOrCreateProvider(): VisaContractInfoProvider = provider ?: createProvider()
|
||||||
|
|
||||||
|
suspend fun getOrCreateApi(): VisaApi = api ?: createApi()
|
||||||
|
|
||||||
|
private suspend fun createProvider(): VisaContractInfoProvider = createMutex.withLock {
|
||||||
|
val config = getOrLoadConfig()
|
||||||
|
|
||||||
|
provider = VisaContractInfoProvider.Builder(
|
||||||
|
useTestnetRpc = VisaConstants.USE_TEST_ENV,
|
||||||
|
bridgeProcessorAddress = if (VisaConstants.USE_TEST_ENV) {
|
||||||
|
config.testnet.bridgeProcessor
|
||||||
|
} else {
|
||||||
|
config.mainnet.bridgeProcessor
|
||||||
|
},
|
||||||
|
paymentAccountRegistryAddress = if (VisaConstants.USE_TEST_ENV) {
|
||||||
|
config.testnet.paymentAccountRegistry
|
||||||
|
} else {
|
||||||
|
config.mainnet.paymentAccountRegistry
|
||||||
|
},
|
||||||
|
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
|
||||||
|
dispatchers = dispatchers,
|
||||||
|
).build()
|
||||||
|
|
||||||
|
return requireNotNull(provider) {
|
||||||
|
"Visa provider is not created"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun createApi(): VisaApi = createMutex.withLock {
|
||||||
|
val config = getOrLoadConfig()
|
||||||
|
|
||||||
|
api = VisaApiBuilder(
|
||||||
|
useDevApi = VisaConstants.USE_TEST_ENV,
|
||||||
|
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
|
||||||
|
moshi = moshi,
|
||||||
|
headers = mapOf(
|
||||||
|
X_ASN_HEADER_NAME to config.header.xAsn,
|
||||||
|
),
|
||||||
|
).build()
|
||||||
|
|
||||||
|
return requireNotNull(api) {
|
||||||
|
"Visa API is not created"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getOrLoadConfig(): VisaConfig {
|
||||||
|
config = assetLoader.load<VisaConfig>(VISA_CONFIG_FILE_NAME)
|
||||||
|
|
||||||
|
return requireNotNull(config) {
|
||||||
|
"Visa config is not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val VISA_CONFIG_FILE_NAME = "tangem-app-config/visa_config"
|
||||||
|
private const val X_ASN_HEADER_NAME = "x-asn"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
package com.tangem.data.visa.di
|
package com.tangem.data.visa.di
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import com.tangem.data.common.cache.CacheRegistry
|
import com.tangem.data.common.cache.CacheRegistry
|
||||||
import com.tangem.data.visa.BuildConfig
|
|
||||||
import com.tangem.data.visa.DefaultVisaRepository
|
import com.tangem.data.visa.DefaultVisaRepository
|
||||||
|
import com.tangem.data.visa.config.VisaLibLoader
|
||||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.di.NetworkMoshi
|
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.visa.repository.VisaRepository
|
import com.tangem.domain.visa.repository.VisaRepository
|
||||||
import com.tangem.lib.visa.VisaContractInfoProvider
|
|
||||||
import com.tangem.lib.visa.api.VisaApiBuilder
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
|
@ -25,29 +21,16 @@ internal object ImplementedVisaDataModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
@ImplementedVisaRepository
|
@ImplementedVisaRepository
|
||||||
fun provideVisaRepository(
|
fun provideVisaRepository(
|
||||||
@NetworkMoshi moshi: Moshi,
|
visaLibLoader: VisaLibLoader,
|
||||||
tangemTechApi: TangemTechApi,
|
tangemTechApi: TangemTechApi,
|
||||||
cacheRegistry: CacheRegistry,
|
cacheRegistry: CacheRegistry,
|
||||||
userWalletsStore: UserWalletsStore,
|
userWalletsStore: UserWalletsStore,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): VisaRepository {
|
): VisaRepository = DefaultVisaRepository(
|
||||||
val contractInfoProvider = VisaContractInfoProvider.Builder(
|
visaLibLoader,
|
||||||
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
|
tangemTechApi,
|
||||||
dispatchers = dispatchers,
|
cacheRegistry,
|
||||||
).build()
|
userWalletsStore,
|
||||||
val visaApi = VisaApiBuilder(
|
dispatchers,
|
||||||
useDevApi = true,
|
)
|
||||||
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
|
|
||||||
moshi = moshi,
|
|
||||||
).build()
|
|
||||||
|
|
||||||
return DefaultVisaRepository(
|
|
||||||
contractInfoProvider,
|
|
||||||
tangemTechApi,
|
|
||||||
visaApi,
|
|
||||||
cacheRegistry,
|
|
||||||
userWalletsStore,
|
|
||||||
dispatchers,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,9 +7,9 @@ import java.util.Currency
|
||||||
internal fun findCurrencyByNumericCode(code: Int): Currency {
|
internal fun findCurrencyByNumericCode(code: Int): Currency {
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
Currency.getAvailableCurrencies().firstOrNull { it.numericCode == code }
|
Currency.getAvailableCurrencies().firstOrNull { it.numericCode == code }
|
||||||
?: Currency.getInstance(VisaConfig.fiatCurrency.code)
|
?: Currency.getInstance(VisaConstants.fiatCurrency.code)
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Unable to get currency by numeric code on API level ${Build.VERSION.SDK_INT}")
|
Timber.w("Unable to get currency by numeric code on API level ${Build.VERSION.SDK_INT}")
|
||||||
Currency.getInstance(VisaConfig.fiatCurrency.code)
|
Currency.getInstance(VisaConstants.fiatCurrency.code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package com.tangem.data.visa.utils
|
|
||||||
|
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
|
||||||
|
|
||||||
internal object VisaConfig {
|
|
||||||
|
|
||||||
const val NETWORK_NAME = "Polygon PoS"
|
|
||||||
|
|
||||||
const val TOKEN_ID = "tether"
|
|
||||||
|
|
||||||
val fiatCurrency = AppCurrency(
|
|
||||||
code = "EUR",
|
|
||||||
name = "Euro",
|
|
||||||
symbol = "€",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.tangem.data.visa.utils
|
||||||
|
|
||||||
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
|
|
||||||
|
internal object VisaConstants {
|
||||||
|
|
||||||
|
const val NETWORK_NAME = "Polygon PoS"
|
||||||
|
|
||||||
|
const val TOKEN_ID = "tether"
|
||||||
|
|
||||||
|
val fiatCurrency = AppCurrency(
|
||||||
|
code = "EUR",
|
||||||
|
name = "Euro",
|
||||||
|
symbol = "€",
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be `false` in production
|
||||||
|
* Don't forget to change CardTypesResolver.isVisaWallet
|
||||||
|
* */
|
||||||
|
const val IS_DEMO_MODE_ENABLED = false
|
||||||
|
|
||||||
|
const val USE_TEST_ENV = true
|
||||||
|
|
||||||
|
const val DEMO_TESTNET_ADDRESS = "0x51d034eb1563d0d2e66379ef37756d3c14936c44"
|
||||||
|
const val DEMO_TESTNET_PUBLIC_KEY = "03FA1122B809079F79C4E0F657FE11337FEC88C3FB3C6341B2CE2E4F5D9241DD86"
|
||||||
|
|
||||||
|
const val DEMO_MAINNET_ADDRESS = "0x927e3ef2b3d85bacf9e520379f64f6627d323fcd"
|
||||||
|
const val DEMO_MAINNET_PUBLIC_KEY = "02AC61CD57B8011BEE8BB489FB744845CC113AD379132C56015EE70528B6A88E92"
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getDemoAddress(): String {
|
||||||
|
return if (VisaConstants.USE_TEST_ENV) {
|
||||||
|
VisaConstants.DEMO_TESTNET_ADDRESS
|
||||||
|
} else {
|
||||||
|
VisaConstants.DEMO_MAINNET_ADDRESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getDemoPublicKey(): String {
|
||||||
|
return if (VisaConstants.USE_TEST_ENV) {
|
||||||
|
VisaConstants.DEMO_TESTNET_PUBLIC_KEY
|
||||||
|
} else VisaConstants.DEMO_MAINNET_PUBLIC_KEY
|
||||||
|
}
|
||||||
|
|
@ -21,10 +21,10 @@ internal class VisaCurrencyFactory {
|
||||||
|
|
||||||
return VisaCurrency(
|
return VisaCurrency(
|
||||||
symbol = contractInfo.token.symbol,
|
symbol = contractInfo.token.symbol,
|
||||||
networkName = VisaConfig.NETWORK_NAME,
|
networkName = VisaConstants.NETWORK_NAME,
|
||||||
decimals = contractInfo.token.decimals,
|
decimals = contractInfo.token.decimals,
|
||||||
fiatRate = fiatRate,
|
fiatRate = fiatRate,
|
||||||
fiatCurrency = VisaConfig.fiatCurrency,
|
fiatCurrency = VisaConstants.fiatCurrency,
|
||||||
balances = with(contractInfo) {
|
balances = with(contractInfo) {
|
||||||
VisaCurrency.Balances(
|
VisaCurrency.Balances(
|
||||||
total = balances.total,
|
total = balances.total,
|
||||||
|
|
|
||||||
|
|
@ -19,24 +19,25 @@ internal class SetBalancesAndLimitsTransformer(
|
||||||
private val userWallet: UserWallet,
|
private val userWallet: UserWallet,
|
||||||
private val maybeVisaCurrency: Either<Throwable, VisaCurrency>,
|
private val maybeVisaCurrency: Either<Throwable, VisaCurrency>,
|
||||||
private val clickIntents: WalletClickIntents,
|
private val clickIntents: WalletClickIntents,
|
||||||
) : WalletStateTransformer(userWallet.walletId) {
|
) : TypedWalletStateTransformer<WalletState.Visa.Content>(
|
||||||
|
userWalletId = userWallet.walletId,
|
||||||
|
targetStateClass = WalletState.Visa.Content::class,
|
||||||
|
) {
|
||||||
|
|
||||||
override fun transform(prevState: WalletState): WalletState {
|
override fun transformTyped(prevState: WalletState.Visa.Content): WalletState {
|
||||||
return prevState.transformWhenInState<WalletState.Visa.Content> { state ->
|
val visaCurrency = maybeVisaCurrency.getOrElse {
|
||||||
val visaCurrency = maybeVisaCurrency.getOrElse {
|
return prevState.copy(
|
||||||
return state.copy(
|
walletCardState = getErrorWalletCardState(prevState.walletCardState),
|
||||||
walletCardState = getErrorWalletCardState(state.walletCardState),
|
depositButtonState = prevState.depositButtonState.copy(isEnabled = false),
|
||||||
depositButtonState = state.depositButtonState.copy(isEnabled = false),
|
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error,
|
||||||
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
state.copy(
|
|
||||||
walletCardState = getContentWalletCardState(state.walletCardState, visaCurrency),
|
|
||||||
depositButtonState = state.depositButtonState.copy(isEnabled = true),
|
|
||||||
balancesAndLimitBlockState = getContentBlockState(visaCurrency),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return prevState.copy(
|
||||||
|
walletCardState = getContentWalletCardState(prevState.walletCardState, visaCurrency),
|
||||||
|
depositButtonState = prevState.depositButtonState.copy(isEnabled = true),
|
||||||
|
balancesAndLimitBlockState = getContentBlockState(visaCurrency),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getContentBlockState(visaCurrency: VisaCurrency) = BalancesAndLimitsBlockState.Content(
|
private fun getContentBlockState(visaCurrency: VisaCurrency) = BalancesAndLimitsBlockState.Content(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||||
|
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
internal abstract class TypedWalletStateTransformer<S : WalletState>(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
protected val targetStateClass: KClass<S>,
|
||||||
|
) : WalletStateTransformer(userWalletId) {
|
||||||
|
|
||||||
|
abstract fun transformTyped(prevState: S): WalletState
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
final override fun transform(prevState: WalletState): WalletState {
|
||||||
|
return if (prevState::class == targetStateClass) {
|
||||||
|
transformTyped(prevState as S)
|
||||||
|
} else {
|
||||||
|
prevState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@ import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal abstract class WalletStateTransformer(
|
internal abstract class WalletStateTransformer(
|
||||||
protected val userWalletId: UserWalletId,
|
protected val userWalletId: UserWalletId,
|
||||||
|
|
@ -12,7 +11,7 @@ internal abstract class WalletStateTransformer(
|
||||||
|
|
||||||
abstract fun transform(prevState: WalletState): WalletState
|
abstract fun transform(prevState: WalletState): WalletState
|
||||||
|
|
||||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
final override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||||
return prevState.copy(
|
return prevState.copy(
|
||||||
wallets = prevState.wallets
|
wallets = prevState.wallets
|
||||||
.map { state ->
|
.map { state ->
|
||||||
|
|
@ -21,13 +20,4 @@ internal abstract class WalletStateTransformer(
|
||||||
.toImmutableList(),
|
.toImmutableList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected inline fun <reified S : WalletState> WalletState.transformWhenInState(
|
|
||||||
transform: (state: S) -> WalletState,
|
|
||||||
): WalletState = if (this is S) {
|
|
||||||
transform(this)
|
|
||||||
} else {
|
|
||||||
Timber.w("Impossible to transform ${this::class.simpleName} because current is ${S::class.simpleName}")
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -3,8 +3,8 @@ package com.tangem.lib.visa
|
||||||
import com.ihsanbal.logging.Level
|
import com.ihsanbal.logging.Level
|
||||||
import com.ihsanbal.logging.LoggingInterceptor
|
import com.ihsanbal.logging.LoggingInterceptor
|
||||||
import com.tangem.lib.visa.model.VisaContractInfo
|
import com.tangem.lib.visa.model.VisaContractInfo
|
||||||
import com.tangem.lib.visa.utils.VisaConfig
|
import com.tangem.lib.visa.utils.Constants
|
||||||
import com.tangem.lib.visa.utils.VisaConfig.NETWORK_LOGS_TAG
|
import com.tangem.lib.visa.utils.Constants.NETWORK_LOGS_TAG
|
||||||
import com.tangem.lib.visa.utils.toHexString
|
import com.tangem.lib.visa.utils.toHexString
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
|
@ -24,16 +24,16 @@ interface VisaContractInfoProvider {
|
||||||
suspend fun getContractInfo(walletAddress: String): VisaContractInfo
|
suspend fun getContractInfo(walletAddress: String): VisaContractInfo
|
||||||
|
|
||||||
class Builder(
|
class Builder(
|
||||||
|
private val useTestnetRpc: Boolean,
|
||||||
|
private val bridgeProcessorAddress: String,
|
||||||
|
private val paymentAccountRegistryAddress: String,
|
||||||
private val isNetworkLoggingEnabled: Boolean,
|
private val isNetworkLoggingEnabled: Boolean,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val baseUrl: String = VisaConfig.BASE_RPC_URL,
|
private val chainId: Long = Constants.CHAIN_ID,
|
||||||
private val bridgeProcessorAddress: String = VisaConfig.BRIDGE_PROCESSOR_CONTRACT_ADDRESS,
|
private val decimals: Int = Constants.DECIMALS,
|
||||||
private val paymentAccountRegistryAddress: String = VisaConfig.PAYMENT_ACCOUNT_REGISTRY_ADDRESS,
|
private val gasLimit: Long = Constants.GAS_LIMIT,
|
||||||
private val chainId: Long = VisaConfig.CHAIN_ID,
|
private val networkTimeoutSeconds: Long = Constants.NETWORK_TIMEOUT_SECONDS,
|
||||||
private val networkTimeoutSeconds: Long = VisaConfig.NETWORK_TIMEOUT_SECONDS,
|
private val privateKey: String = ByteArray(Constants.PRIVATE_KEY_LENGTH).toHexString(),
|
||||||
private val decimals: Int = VisaConfig.DECIMALS,
|
|
||||||
private val gasLimit: Long = VisaConfig.GAS_LIMIT,
|
|
||||||
private val privateKey: String = ByteArray(VisaConfig.PRIVATE_KEY_LENGTH).toHexString(),
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun build(): VisaContractInfoProvider {
|
fun build(): VisaContractInfoProvider {
|
||||||
|
|
@ -52,6 +52,8 @@ interface VisaContractInfoProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createWeb3J(): Web3j {
|
private fun createWeb3J(): Web3j {
|
||||||
|
val baseUrl: String = if (useTestnetRpc) Constants.TESTNET_RPC_URL else Constants.MAINNET_RPC_URL
|
||||||
|
|
||||||
val httpClient = OkHttpClient.Builder().apply {
|
val httpClient = OkHttpClient.Builder().apply {
|
||||||
connectTimeout(networkTimeoutSeconds, TimeUnit.SECONDS)
|
connectTimeout(networkTimeoutSeconds, TimeUnit.SECONDS)
|
||||||
readTimeout(networkTimeoutSeconds, TimeUnit.SECONDS)
|
readTimeout(networkTimeoutSeconds, TimeUnit.SECONDS)
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import com.ihsanbal.logging.Level
|
||||||
import com.ihsanbal.logging.LoggingInterceptor
|
import com.ihsanbal.logging.LoggingInterceptor
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
|
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
|
||||||
import com.tangem.lib.visa.utils.VisaConfig
|
import com.tangem.lib.visa.utils.Constants
|
||||||
import com.tangem.lib.visa.utils.VisaConfig.NETWORK_LOGS_TAG
|
import com.tangem.lib.visa.utils.Constants.NETWORK_LOGS_TAG
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
|
|
@ -17,7 +17,8 @@ class VisaApiBuilder(
|
||||||
private val useDevApi: Boolean,
|
private val useDevApi: Boolean,
|
||||||
private val isNetworkLoggingEnabled: Boolean,
|
private val isNetworkLoggingEnabled: Boolean,
|
||||||
private val moshi: Moshi,
|
private val moshi: Moshi,
|
||||||
private val networkTimeoutSeconds: Long = VisaConfig.NETWORK_TIMEOUT_SECONDS,
|
private val headers: Map<String, String>,
|
||||||
|
private val networkTimeoutSeconds: Long = Constants.NETWORK_TIMEOUT_SECONDS,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun build(): VisaApi {
|
fun build(): VisaApi {
|
||||||
|
|
@ -35,13 +36,23 @@ class VisaApiBuilder(
|
||||||
if (isNetworkLoggingEnabled) {
|
if (isNetworkLoggingEnabled) {
|
||||||
addInterceptor(createNetworkLoggingInterceptor())
|
addInterceptor(createNetworkLoggingInterceptor())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (headers.isNotEmpty()) {
|
||||||
|
addInterceptor { chain ->
|
||||||
|
val request = chain.request().newBuilder().apply {
|
||||||
|
headers.forEach { (key, value) -> addHeader(key, value) }
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
chain.proceed(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRetrofit(okHttpClient: OkHttpClient): Retrofit {
|
private fun createRetrofit(okHttpClient: OkHttpClient): Retrofit {
|
||||||
val baseUrl = if (useDevApi) VisaConfig.VISA_API_DEV_URL else VisaConfig.VISA_API_PROD_URL
|
val baseUrl = if (useDevApi) Constants.VISA_API_DEV_URL else Constants.VISA_API_PROD_URL
|
||||||
|
|
||||||
return Retrofit.Builder()
|
return Retrofit.Builder()
|
||||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.tangem.lib.visa.utils
|
package com.tangem.lib.visa.utils
|
||||||
|
|
||||||
internal object VisaConfig {
|
internal object Constants {
|
||||||
|
|
||||||
|
const val MAINNET_RPC_URL = "https://polygon-rpc.com/"
|
||||||
|
const val TESTNET_RPC_URL = "https://rpc-amoy.polygon.technology/"
|
||||||
|
|
||||||
const val BASE_RPC_URL = "https://polygon-mumbai.g.alchemy.com/v2/_1qqjXgBC_IikaXChnna8KTcV2eMMIQG/"
|
|
||||||
const val BRIDGE_PROCESSOR_CONTRACT_ADDRESS = "0xe32ecbbc1ec17fa9c160569cd613ad568ca50279"
|
|
||||||
const val PAYMENT_ACCOUNT_REGISTRY_ADDRESS = "0x3f4ae01073d1a9d5a92315fe118e57d1cdec7c44"
|
|
||||||
const val CHAIN_ID = 80_001L
|
const val CHAIN_ID = 80_001L
|
||||||
const val DECIMALS = 9
|
const val DECIMALS = 9
|
||||||
const val GAS_LIMIT = 500_000_000L
|
const val GAS_LIMIT = 500_000_000L
|
||||||
Loading…
Add table
Add a link
Reference in a new issue