Updated on 2026-08-14

This commit is contained in:
Tangem 2024-04-16 16:01:41 +08:00
parent 59680b5305
commit b601aea5c3
13 changed files with 172 additions and 58 deletions

View file

@ -1,32 +1,45 @@
package com.tangem.blockchainsdk
import com.tangem.blockchain.common.AccountCreator
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.blockchain.common.datastorage.BlockchainDataStorage
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
import com.tangem.blockchainsdk.config.ConfigStorage
import com.tangem.blockchain.common.network.providers.ProviderType
import com.tangem.blockchainsdk.converters.BlockchainProviderTypesConverter
import com.tangem.blockchainsdk.converters.BlockchainSDKConfigConverter
import com.tangem.blockchainsdk.storage.RuntimeStore
import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.config.models.ConfigValueModel
import com.tangem.datasource.config.models.ProviderModel
import com.tangem.libs.blockchain_sdk.BuildConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
internal typealias BlockchainProvidersResponse = Map<String, List<ProviderModel>>
internal typealias BlockchainProviderTypes = Map<Blockchain, List<ProviderType>>
/**
* Implementation of Blockchain SDK components factory
*
* @property assetLoader asset loader
* @property configStorage config storage
* @property accountCreator account creator
* @property blockchainDataStorage blockchain data storage
* @property blockchainSDKLogger blockchain SDK logger
* @property assetLoader asset loader
* @property configStore blockchain sdk config store
* @property blockchainProviderTypesStore blockchain provider types store
* @property accountCreator account creator
* @property blockchainDataStorage blockchain data storage
* @property blockchainSDKLogger blockchain SDK logger
*
[REDACTED_AUTHOR]
*/
internal class DefaultBlockchainSDKFactory(
private val assetLoader: AssetLoader,
private val configStorage: ConfigStorage,
private val configStore: RuntimeStore<BlockchainSdkConfig>,
private val blockchainProviderTypesStore: RuntimeStore<BlockchainProviderTypes>,
private val accountCreator: AccountCreator,
private val blockchainDataStorage: BlockchainDataStorage,
private val blockchainSDKLogger: BlockchainSDKLogger,
@ -35,19 +48,22 @@ internal class DefaultBlockchainSDKFactory(
override val walletManagerFactory: Flow<WalletManagerFactory> by lazy(::createWalletManagerFactory)
override suspend fun init() {
val configValueModel = assetLoader.load<ConfigValueModel>(CONFIG_FILE_NAME) ?: return
configStorage.store(
config = BlockchainSDKConfigConverter.convert(value = configValueModel),
)
coroutineScope {
updateBlockchainSDKConfig()
updateBlockchainProviderTypes()
}
}
override suspend fun getWalletManagerFactorySync(): WalletManagerFactory? = walletManagerFactory.firstOrNull()
private fun createWalletManagerFactory(): Flow<WalletManagerFactory> {
return configStorage.get().map { config ->
return combine(
flow = configStore.get(),
flow2 = blockchainProviderTypesStore.get(),
) { config, blockchainProviderTypes ->
WalletManagerFactory(
config = config,
blockchainProviderTypes = blockchainProviderTypes,
accountCreator = accountCreator,
blockchainDataStorage = blockchainDataStorage,
loggers = listOf(blockchainSDKLogger),
@ -55,8 +71,29 @@ internal class DefaultBlockchainSDKFactory(
}
}
private companion object {
private fun CoroutineScope.updateBlockchainSDKConfig() {
launch {
val config = assetLoader.load<ConfigValueModel>(fileName = CONFIG_FILE_NAME) ?: return@launch
configStore.store(
value = BlockchainSDKConfigConverter.convert(value = config),
)
}
}
private fun CoroutineScope.updateBlockchainProviderTypes() {
launch {
val providerTypes = assetLoader.load<BlockchainProvidersResponse>(fileName = PROVIDER_TYPES_FILE_NAME)
?: return@launch
blockchainProviderTypesStore.store(
value = BlockchainProviderTypesConverter.convert(providerTypes),
)
}
}
private companion object {
const val CONFIG_FILE_NAME = "tangem-app-config/config_${BuildConfig.ENVIRONMENT}"
const val PROVIDER_TYPES_FILE_NAME = "tangem-app-config/providers_order"
}
}

View file

@ -1,18 +0,0 @@
package com.tangem.blockchainsdk.config
import com.tangem.blockchain.common.BlockchainSdkConfig
import kotlinx.coroutines.flow.Flow
/**
* Storage for [BlockchainSdkConfig]
*
[REDACTED_AUTHOR]
*/
internal interface ConfigStorage {
/** Get flow of [BlockchainSdkConfig] */
fun get(): Flow<BlockchainSdkConfig>
/** Store [BlockchainSdkConfig] */
suspend fun store(config: BlockchainSdkConfig)
}

View file

@ -1,21 +0,0 @@
package com.tangem.blockchainsdk.config
import com.tangem.blockchain.common.BlockchainSdkConfig
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
/**
* Runtime storage for [BlockchainSdkConfig]
*
[REDACTED_AUTHOR]
*/
internal class RuntimeConfigStorage : ConfigStorage {
private val configFlow = MutableStateFlow(value = BlockchainSdkConfig())
override fun get(): Flow<BlockchainSdkConfig> = configFlow
override suspend fun store(config: BlockchainSdkConfig) {
configFlow.value = config
}
}

View file

@ -0,0 +1,64 @@
package com.tangem.blockchainsdk.converters
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.network.providers.ProviderType
import com.tangem.blockchainsdk.BlockchainProviderTypes
import com.tangem.blockchainsdk.BlockchainProvidersResponse
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.datasource.config.models.ProviderModel
import com.tangem.utils.converter.Converter
import timber.log.Timber
/**
* Converts [BlockchainProvidersResponse] to [BlockchainProviderTypes]
*
[REDACTED_AUTHOR]
*/
internal object BlockchainProviderTypesConverter :
Converter<BlockchainProvidersResponse, BlockchainProviderTypes> {
override fun convert(value: BlockchainProvidersResponse): BlockchainProviderTypes {
return value.mapNotNull { (networkId, blockchainProviders) ->
val blockchain = Blockchain.fromNetworkId(networkId) ?: return@mapNotNull null
val providerTypes = blockchainProviders.mapNotNull { provider ->
when (provider) {
is ProviderModel.Public -> ProviderType.Public(url = provider.url)
is ProviderModel.Private -> createPrivateProviderType(blockchain = blockchain, name = provider.name)
ProviderModel.UnsupportedType -> {
Timber.e("$blockchain provider type is not supported")
null
}
}
}
blockchain to providerTypes
}
.toMap()
}
@Suppress("CyclomaticComplexMethod")
private fun createPrivateProviderType(blockchain: Blockchain, name: String): ProviderType? {
return when (name) {
"blockchair" -> ProviderType.BitcoinLike.Blockchair
"blockcypher" -> ProviderType.BitcoinLike.Blockcypher
"adalite" -> ProviderType.Cardano.Adalite
"tangemRosetta" -> ProviderType.Cardano.Rosetta
"fireAcademy" -> ProviderType.Chia.FireAcademy
"tangemChia" -> ProviderType.Chia.Tangem
"infura" -> ProviderType.EthereumLike.Infura
"getblock" -> ProviderType.GetBlock
"arkhiaHedera" -> ProviderType.Hedera.Arkhia
"kaspa" -> ProviderType.Kaspa.SecondaryAPI
"nownodes" -> ProviderType.NowNodes
"quicknode" -> ProviderType.QuickNode
"solana" -> ProviderType.Solana.Official
"ton" -> ProviderType.Ton.TonCentral
"tron" -> ProviderType.Tron.TronGrid
else -> {
Timber.e("$blockchain private provider ($name) is not supported")
null
}
}
}
}

View file

@ -77,6 +77,8 @@ internal object BlockchainSDKConfigConverter : Converter<ConfigValueModel, Block
blockBookRest = accessTokens.bitcoin?.blockBookRest,
),
algorand = GetBlockAccessToken(rest = accessTokens.algorand?.rest),
zkSyncEra = GetBlockAccessToken(jsonRpc = accessTokens.zksync?.jsonRPC),
polygonZkEvm = GetBlockAccessToken(jsonRpc = accessTokens.polygonZkevm?.jsonRPC),
)
}
}

View file

@ -1,11 +1,12 @@
package com.tangem.blockchainsdk.di
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.DefaultBlockchainSDKFactory
import com.tangem.blockchainsdk.accountcreator.DefaultAccountCreator
import com.tangem.blockchainsdk.config.RuntimeConfigStorage
import com.tangem.blockchainsdk.datastorage.DefaultBlockchainDataStorage
import com.tangem.blockchainsdk.storage.DefaultRuntimeStore
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.asset.loader.AssetLoader
@ -31,7 +32,8 @@ internal object BlockchainSDKFactoryModule {
): BlockchainSDKFactory {
return DefaultBlockchainSDKFactory(
assetLoader = assetLoader,
configStorage = RuntimeConfigStorage(),
configStore = DefaultRuntimeStore(defaultValue = BlockchainSdkConfig()),
blockchainProviderTypesStore = DefaultRuntimeStore(defaultValue = emptyMap()),
accountCreator = DefaultAccountCreator(authProvider, tangemTechApi),
blockchainDataStorage = DefaultBlockchainDataStorage(appPreferencesStore),
blockchainSDKLogger = blockchainSDKLogger,

View file

@ -0,0 +1,20 @@
package com.tangem.blockchainsdk.storage
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
/**
* Default implementation of RuntimeStore
*
* @param defaultValue default value
*/
internal class DefaultRuntimeStore<T>(defaultValue: T) : RuntimeStore<T> {
private val flow = MutableStateFlow(value = defaultValue)
override fun get(): StateFlow<T> = flow
override suspend fun store(value: T) {
flow.value = value
}
}

View file

@ -0,0 +1,17 @@
package com.tangem.blockchainsdk.storage
import kotlinx.coroutines.flow.StateFlow
/**
* Runtime store
*
[REDACTED_AUTHOR]
*/
internal interface RuntimeStore<T> {
/** Get flow of elements [T] */
fun get(): StateFlow<T>
/** Store [value] */
suspend fun store(value: T)
}

View file

@ -114,6 +114,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
"taraxa/test" -> Blockchain.TaraxaTestnet
"base" -> Blockchain.Base
"base/test" -> Blockchain.BaseTestnet
"koinos" -> Blockchain.Koinos
"koinos/test" -> Blockchain.KoinosTestnet
else -> null
}
}
@ -229,6 +231,8 @@ fun Blockchain.toNetworkId(): String {
Blockchain.TaraxaTestnet -> "taraxa/test"
Blockchain.Base -> "base"
Blockchain.BaseTestnet -> "base/test"
Blockchain.Koinos -> "koinos"
Blockchain.KoinosTestnet -> "koinos/test"
}
}
@ -302,6 +306,7 @@ fun Blockchain.toCoinId(): String {
Blockchain.Flare, Blockchain.FlareTestnet -> "flare-networks"
Blockchain.Taraxa, Blockchain.TaraxaTestnet -> "taraxa"
Blockchain.Base, Blockchain.BaseTestnet -> "base"
Blockchain.Koinos, Blockchain.KoinosTestnet -> "koinos"
}
}
@ -332,4 +337,6 @@ private val excludedBlockchains = listOf(
Blockchain.Nexa,
Blockchain.NexaTestnet,
Blockchain.Radiant,
Blockchain.Koinos,
Blockchain.KoinosTestnet,
)