Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-05 21:33:57 +01:00
commit c979cb620e
363 changed files with 5909 additions and 3610 deletions

View file

@ -1,14 +0,0 @@
package com.tangem.lib.auth
/**
* Provides auth for tangemTech API
*/
interface AuthProvider {
/**
* Returns authToken for tangem tech api
*/
fun getCardPublicKey(): String
fun getCardId(): String
}

1
libs/blockchain-sdk/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,45 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
id("configuration")
}
android {
namespace = "com.tangem.libs.blockchain_sdk"
}
dependencies {
// region Core modules
implementation(projects.core.datasource)
implementation(projects.core.utils)
// endregion
// region AndroidX libraries
implementation(deps.androidx.datastore)
// endregion
// region DI libraries
implementation(deps.hilt.core)
kapt(deps.hilt.kapt)
// endregion
// region Other libraries
implementation(deps.kotlin.coroutines)
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.timber)
// endregion
// region Firebase libraries
implementation(platform(deps.firebase.bom))
implementation(deps.firebase.analytics)
implementation(deps.firebase.crashlytics)
// endregion
// region Tangem libraries
implementation(deps.tangem.blockchain) { exclude(module = "joda-time") }
implementation(deps.tangem.card.core)
// endregion
}

View file

@ -0,0 +1,17 @@
package com.tangem.blockchainsdk
import com.tangem.blockchain.common.WalletManagerFactory
/**
* Blockchain SDK components factory
*
[REDACTED_AUTHOR]
*/
interface BlockchainSDKFactory {
/** Initialize components */
suspend fun init()
/** Get [WalletManagerFactory] synchronously */
suspend fun getWalletManagerFactorySync(): WalletManagerFactory?
}

View file

@ -0,0 +1,102 @@
package com.tangem.blockchainsdk
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.blockchain.common.network.providers.ProviderType
import com.tangem.blockchainsdk.converters.BlockchainProviderTypesConverter
import com.tangem.blockchainsdk.converters.BlockchainSDKConfigConverter
import com.tangem.blockchainsdk.loader.BlockchainProvidersResponseLoader
import com.tangem.blockchainsdk.store.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 com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import timber.log.Timber
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 blockchainProvidersResponseLoader blockchain providers response loader
* @property configStore blockchain sdk config store
* @property blockchainProviderTypesStore blockchain provider types store
* @property walletManagerFactoryCreator wallet manager factory creator
*
[REDACTED_AUTHOR]
*/
internal class DefaultBlockchainSDKFactory(
private val assetLoader: AssetLoader,
private val blockchainProvidersResponseLoader: BlockchainProvidersResponseLoader,
private val configStore: RuntimeStore<BlockchainSdkConfig>,
private val blockchainProviderTypesStore: RuntimeStore<BlockchainProviderTypes>,
private val walletManagerFactoryCreator: WalletManagerFactoryCreator,
dispatchers: CoroutineDispatcherProvider,
) : BlockchainSDKFactory {
private val walletManagerFactory: Flow<WalletManagerFactory?> by lazy(::createWalletManagerFactory)
private val mainScope = CoroutineScope(dispatchers.main)
override suspend fun init() {
coroutineScope {
updateBlockchainSDKConfig()
updateBlockchainProviderTypes()
}
}
override suspend fun getWalletManagerFactorySync(): WalletManagerFactory? = walletManagerFactory.firstOrNull()
private fun createWalletManagerFactory(): Flow<WalletManagerFactory?> {
return combine(
flow = configStore.get(),
flow2 = blockchainProviderTypesStore.get(),
transform = walletManagerFactoryCreator::create,
)
.stateIn(scope = mainScope, started = SharingStarted.Eagerly, initialValue = null)
}
private fun CoroutineScope.updateBlockchainSDKConfig() {
launch {
val config = assetLoader.load<ConfigValueModel>(fileName = CONFIG_FILE_NAME)
if (config == null) {
Timber.e("Error loading BlockchainSDKConfig")
return@launch
}
Timber.d("Update BlockchainSDKConfig")
configStore.store(
value = BlockchainSDKConfigConverter.convert(value = config),
)
}
}
private fun CoroutineScope.updateBlockchainProviderTypes() {
launch {
val response = blockchainProvidersResponseLoader.load()
if (response == null) {
Timber.e("Error loading BlockchainProviderTypes")
return@launch
}
Timber.d("Update BlockchainProviderTypes")
blockchainProviderTypesStore.store(
value = BlockchainProviderTypesConverter.convert(response),
)
}
}
private companion object {
const val CONFIG_FILE_NAME = "tangem-app-config/config_${BuildConfig.ENVIRONMENT}"
}
}

View file

@ -0,0 +1,37 @@
package com.tangem.blockchainsdk
import com.tangem.blockchain.common.AccountCreator
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 timber.log.Timber
import javax.inject.Inject
/**
* Creator of [WalletManagerFactory]
*
* @property accountCreator account creator
* @property blockchainDataStorage blockchain data storage
* @property blockchainSDKLogger blockchain SDK logger
*
[REDACTED_AUTHOR]
*/
internal class WalletManagerFactoryCreator @Inject constructor(
private val accountCreator: AccountCreator,
private val blockchainDataStorage: BlockchainDataStorage,
private val blockchainSDKLogger: BlockchainSDKLogger,
) {
fun create(config: BlockchainSdkConfig, blockchainProviderTypes: BlockchainProviderTypes): WalletManagerFactory {
Timber.d("Create WalletManagerFactory")
return WalletManagerFactory(
config = config,
blockchainProviderTypes = blockchainProviderTypes,
accountCreator = accountCreator,
blockchainDataStorage = blockchainDataStorage,
loggers = listOf(blockchainSDKLogger),
)
}
}

View file

@ -0,0 +1,34 @@
package com.tangem.blockchainsdk.accountcreator
import com.tangem.blockchain.common.AccountCreator
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.toHexString
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.CreateUserNetworkAccountBody
internal class DefaultAccountCreator(
private val authProvider: AuthProvider,
private val tangemTechApi: TangemTechApi,
) : AccountCreator {
override suspend fun createAccount(blockchain: Blockchain, walletPublicKey: ByteArray): Result<String> {
val request = CreateUserNetworkAccountBody(
networkId = blockchain.id.removeSuffix("/test"),
walletPublicKey = walletPublicKey.toHexString(),
)
return try {
val response = tangemTechApi.createUserNetworkAccount(
cardPublicKey = authProvider.getCardPublicKey(),
cardId = authProvider.getCardId(),
body = request,
).getOrThrow()
Result.Success(response.data.accountId)
} catch (e: Exception) {
Result.Failure(BlockchainSdkError.FailedToCreateAccount)
}
}
}

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

@ -0,0 +1,86 @@
package com.tangem.blockchainsdk.converters
import com.tangem.blockchain.common.*
import com.tangem.datasource.config.models.ConfigValueModel
import com.tangem.utils.converter.Converter
/**
* Converts [ConfigValueModel] to [BlockchainSdkConfig]
*
[REDACTED_AUTHOR]
*/
internal object BlockchainSDKConfigConverter : Converter<ConfigValueModel, BlockchainSdkConfig> {
override fun convert(value: ConfigValueModel): BlockchainSdkConfig {
return BlockchainSdkConfig(
blockchairCredentials = BlockchairCredentials(
apiKey = value.blockchairApiKeys,
authToken = value.blockchairAuthorizationToken,
),
blockcypherTokens = value.blockcypherTokens,
quickNodeSolanaCredentials = QuickNodeCredentials(
apiKey = value.quiknodeApiKey,
subdomain = value.quiknodeSubdomain,
),
quickNodeBscCredentials = QuickNodeCredentials(
apiKey = value.bscQuiknodeApiKey,
subdomain = value.bscQuiknodeSubdomain,
),
infuraProjectId = value.infuraProjectId,
tronGridApiKey = value.tronGridApiKey,
nowNodeCredentials = NowNodeCredentials(value.nowNodesApiKey),
getBlockCredentials = createGetBlockCredentials(value),
kaspaSecondaryApiUrl = value.kaspaSecondaryApiUrl,
tonCenterCredentials = TonCenterCredentials(
mainnetApiKey = value.tonCenterKeys.mainnet,
testnetApiKey = value.tonCenterKeys.testnet,
),
chiaFireAcademyApiKey = value.chiaFireAcademyApiKey,
chiaTangemApiKey = value.chiaTangemApiKey,
)
}
private fun createGetBlockCredentials(configValues: ConfigValueModel): GetBlockCredentials? {
return configValues.getBlockAccessTokens?.let { accessTokens ->
GetBlockCredentials(
xrp = GetBlockAccessToken(jsonRpc = accessTokens.xrp?.jsonRPC),
cardano = GetBlockAccessToken(rosetta = accessTokens.cardano?.rosetta),
avalanche = GetBlockAccessToken(jsonRpc = accessTokens.avalanche?.jsonRPC),
eth = GetBlockAccessToken(jsonRpc = accessTokens.eth?.jsonRPC),
etc = GetBlockAccessToken(jsonRpc = accessTokens.etc?.jsonRPC),
fantom = GetBlockAccessToken(jsonRpc = accessTokens.fantom?.jsonRPC),
rsk = GetBlockAccessToken(jsonRpc = accessTokens.rsk?.jsonRPC),
bsc = GetBlockAccessToken(jsonRpc = accessTokens.bsc?.jsonRPC),
polygon = GetBlockAccessToken(jsonRpc = accessTokens.polygon?.jsonRPC),
gnosis = GetBlockAccessToken(jsonRpc = accessTokens.gnosis?.jsonRPC),
cronos = GetBlockAccessToken(jsonRpc = accessTokens.cronos?.jsonRPC),
solana = GetBlockAccessToken(jsonRpc = accessTokens.solana?.jsonRPC),
ton = GetBlockAccessToken(jsonRpc = accessTokens.ton?.jsonRPC),
tron = GetBlockAccessToken(rest = accessTokens.tron?.rest),
cosmos = GetBlockAccessToken(rest = accessTokens.cosmos?.rest),
near = GetBlockAccessToken(jsonRpc = accessTokens.near?.jsonRPC),
aptos = GetBlockAccessToken(rest = accessTokens.aptos?.rest),
dogecoin = GetBlockAccessToken(
jsonRpc = accessTokens.dogecoin?.jsonRPC,
blockBookRest = accessTokens.dogecoin?.blockBookRest,
),
litecoin = GetBlockAccessToken(
jsonRpc = accessTokens.litecoin?.jsonRPC,
blockBookRest = accessTokens.litecoin?.blockBookRest,
),
dash = GetBlockAccessToken(
jsonRpc = accessTokens.dash?.jsonRPC,
blockBookRest = accessTokens.dash?.blockBookRest,
),
bitcoin = GetBlockAccessToken(
jsonRpc = accessTokens.bitcoin?.jsonRPC,
blockBookRest = accessTokens.bitcoin?.blockBookRest,
),
algorand = GetBlockAccessToken(rest = accessTokens.algorand?.rest),
zkSyncEra = GetBlockAccessToken(jsonRpc = accessTokens.zksync?.jsonRPC),
polygonZkEvm = GetBlockAccessToken(jsonRpc = accessTokens.polygonZkevm?.jsonRPC),
base = GetBlockAccessToken(jsonRpc = accessTokens.base?.jsonRPC),
)
}
}
}

View file

@ -0,0 +1,29 @@
package com.tangem.blockchainsdk.datastorage
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import com.tangem.blockchain.common.datastorage.BlockchainDataStorage
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.utils.getSyncOrNull
/**
* [BlockchainDataStorage] implementation
*
* @property appPreferencesStore app preferences store
*
[REDACTED_AUTHOR]
*/
internal class DefaultBlockchainDataStorage(
private val appPreferencesStore: AppPreferencesStore,
) : BlockchainDataStorage {
override suspend fun getOrNull(key: String): String? {
return appPreferencesStore.getSyncOrNull(key = stringPreferencesKey(name = key))
}
override suspend fun store(key: String, value: String) {
appPreferencesStore.edit {
it[stringPreferencesKey(key)] = value
}
}
}

View file

@ -0,0 +1,59 @@
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.WalletManagerFactoryCreator
import com.tangem.blockchainsdk.accountcreator.DefaultAccountCreator
import com.tangem.blockchainsdk.datastorage.DefaultBlockchainDataStorage
import com.tangem.blockchainsdk.loader.BlockchainProvidersResponseLoader
import com.tangem.blockchainsdk.store.DefaultRuntimeStore
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
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 BlockchainSDKFactoryModule {
@Provides
@Singleton
fun provideBlockchainSDKFactory(
assetLoader: AssetLoader,
blockchainProvidersResponseLoader: BlockchainProvidersResponseLoader,
walletManagerFactoryCreator: WalletManagerFactoryCreator,
dispatchers: CoroutineDispatcherProvider,
): BlockchainSDKFactory {
return DefaultBlockchainSDKFactory(
assetLoader = assetLoader,
blockchainProvidersResponseLoader = blockchainProvidersResponseLoader,
configStore = DefaultRuntimeStore(defaultValue = BlockchainSdkConfig()),
blockchainProviderTypesStore = DefaultRuntimeStore(defaultValue = emptyMap()),
walletManagerFactoryCreator = walletManagerFactoryCreator,
dispatchers = dispatchers,
)
}
@Provides
@Singleton
fun provideWalletManagerFactoryCreator(
authProvider: AuthProvider,
tangemTechApi: TangemTechApi,
appPreferencesStore: AppPreferencesStore,
blockchainSDKLogger: BlockchainSDKLogger,
): WalletManagerFactoryCreator {
return WalletManagerFactoryCreator(
accountCreator = DefaultAccountCreator(authProvider, tangemTechApi),
blockchainDataStorage = DefaultBlockchainDataStorage(appPreferencesStore),
blockchainSDKLogger = blockchainSDKLogger,
)
}
}

View file

@ -0,0 +1,97 @@
package com.tangem.blockchainsdk.loader
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchainsdk.BlockchainProvidersResponse
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.tangemTech.TangemTechServiceApi
import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runCatching
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
/**
* Loader of [BlockchainProvidersResponse]
*
* @property tangemTechServiceApi tangem tech api
* @property authProvider auth provider
* @property assetLoader asset loader for local config loading
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
*/
@Singleton
internal class BlockchainProvidersResponseLoader @Inject constructor(
private val tangemTechServiceApi: TangemTechServiceApi,
private val authProvider: AuthProvider,
private val assetLoader: AssetLoader,
private val dispatchers: CoroutineDispatcherProvider,
) {
private val firebaseCrashlytics by lazy(FirebaseCrashlytics::getInstance)
/** Load [BlockchainProvidersResponse] */
suspend fun load(): BlockchainProvidersResponse? {
val localResponse = loadLocal() ?: return null
return runCatching(dispatcher = dispatchers.io, block = ::loadRemote)
.fold(
onSuccess = { remoteResponse -> mergeResponses(local = localResponse, remote = remoteResponse) },
onFailure = {
Timber.e(it, "Failed to load blockchain provider types from backend")
localResponse
},
)
}
private suspend fun loadLocal(): BlockchainProvidersResponse? {
return assetLoader.load<BlockchainProvidersResponse>(fileName = PROVIDER_TYPES_FILE_NAME)
}
private suspend fun loadRemote(): BlockchainProvidersResponse {
return tangemTechServiceApi.getBlockchainProviders(
cardPublicKey = authProvider.getCardPublicKey(),
cardId = authProvider.getCardId(),
)
}
/** Merge blockchains with non-empty providers [remote] from remote with blockchains from local [local] */
private fun mergeResponses(
local: BlockchainProvidersResponse,
remote: BlockchainProvidersResponse,
): BlockchainProvidersResponse {
/*
* Example:
* val remote = mapOf("a" to 1, "b" to 2, "c" to 3)
* val local = mapOf("a" to 11, "e" to 4, "f" to 5)
*
* local + remote // { a = 1, e = 4, f = 5, b = 2, c = 3 }
*/
val result = local + remote.filterValues { it.isNotEmpty() }
if (result != remote) {
val missingBlockchains = result.keys - remote.keys
val blockchainsWithoutProviders = remote.filterValues { it.isEmpty() }.keys
recordException(missingBlockchains = missingBlockchains + blockchainsWithoutProviders)
}
return result
}
private fun recordException(missingBlockchains: Set<String>) {
val exception = IllegalStateException(
"Remote config does not contain required blockchains or providers information: " +
missingBlockchains.joinToString(),
)
Timber.e(exception)
firebaseCrashlytics.recordException(exception)
}
private companion object {
const val PROVIDER_TYPES_FILE_NAME = "tangem-app-config/providers_order"
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.blockchainsdk.store
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.store
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

@ -0,0 +1,345 @@
package com.tangem.blockchainsdk.utils
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import java.math.BigDecimal
@Suppress("ComplexMethod", "LongMethod")
fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
return when (networkId) {
"arbitrum-one" -> Blockchain.Arbitrum
"arbitrum-one/test" -> Blockchain.ArbitrumTestnet
"avalanche", "avalanche-2" -> Blockchain.Avalanche
"avalanche/test", "avalanche-2/test" -> Blockchain.AvalancheTestnet
"binancecoin" -> Blockchain.Binance
"binancecoin/test" -> Blockchain.BinanceTestnet
"binance-smart-chain" -> Blockchain.BSC
"binance-smart-chain/test" -> Blockchain.BSCTestnet
"ethereum" -> Blockchain.Ethereum
"ethereum/test" -> Blockchain.EthereumTestnet
"ethereum-classic" -> Blockchain.EthereumClassic
"ethereum-classic/test" -> Blockchain.EthereumClassicTestnet
"polygon-pos", "matic-network" -> Blockchain.Polygon
"polygon-pos/test", "matic-network/test" -> Blockchain.PolygonTestnet
"solana" -> Blockchain.Solana
"solana/test" -> Blockchain.SolanaTestnet
"fantom" -> Blockchain.Fantom
"fantom/test" -> Blockchain.FantomTestnet
"bitcoin" -> Blockchain.Bitcoin
"bitcoin/test" -> Blockchain.BitcoinTestnet
"bitcoin-cash" -> Blockchain.BitcoinCash
"bitcoin-cash/test" -> Blockchain.BitcoinCashTestnet
"cardano" -> Blockchain.Cardano
"dogecoin" -> Blockchain.Dogecoin
"ducatus" -> Blockchain.Ducatus
"litecoin" -> Blockchain.Litecoin
"rootstock" -> Blockchain.RSK
"stellar" -> Blockchain.Stellar
"stellar/test" -> Blockchain.StellarTestnet
"tezos" -> Blockchain.Tezos
"tron" -> Blockchain.Tron
"tron/test" -> Blockchain.TronTestnet
"xrp", "ripple" -> Blockchain.XRP
"xdai" -> Blockchain.Gnosis
"ethereum-pow-iou" -> Blockchain.EthereumPow
"ethereum-pow-iou/test" -> Blockchain.EthereumPowTestnet
"ethereumfair", "dischain" -> Blockchain.Dischain // for old client compatibility
"polkadot" -> Blockchain.Polkadot
"polkadot/test" -> Blockchain.PolkadotTestnet
"kusama" -> Blockchain.Kusama
"optimistic-ethereum" -> Blockchain.Optimism
"optimistic-ethereum/test" -> Blockchain.OptimismTestnet
"dash" -> Blockchain.Dash
"kaspa" -> Blockchain.Kaspa
"the-open-network" -> Blockchain.TON
"the-open-network/test" -> Blockchain.TONTestnet
"kava" -> Blockchain.Kava
"kava/test" -> Blockchain.KavaTestnet
"ravencoin" -> Blockchain.Ravencoin
"ravencoin/test" -> Blockchain.RavencoinTestnet
"cosmos" -> Blockchain.Cosmos
"cosmos/test" -> Blockchain.CosmosTestnet
"terra" -> Blockchain.TerraV1
"terra-2" -> Blockchain.TerraV2
"cronos" -> Blockchain.Cronos
"telos" -> Blockchain.Telos
"telos/test" -> Blockchain.TelosTestnet
"aleph-zero" -> Blockchain.AlephZero
"aleph-zero/test" -> Blockchain.AlephZeroTestnet
"octaspace" -> Blockchain.OctaSpace
"octaspace/test" -> Blockchain.OctaSpaceTestnet
"chia" -> Blockchain.Chia
"chia/test" -> Blockchain.ChiaTestnet
"near-protocol" -> Blockchain.Near
"near-protocol/test" -> Blockchain.NearTestnet
"decimal" -> Blockchain.Decimal
"decimal/test" -> Blockchain.DecimalTestnet
"xdc-network" -> Blockchain.XDC
"xdc-network/test" -> Blockchain.XDCTestnet
"vechain" -> Blockchain.VeChain
"vechain/test" -> Blockchain.VeChainTestnet
"aptos" -> Blockchain.Aptos
"aptos/test" -> Blockchain.AptosTestnet
"playa3ull-games" -> Blockchain.Playa3ull
"shibarium" -> Blockchain.Shibarium
"shibarium/test" -> Blockchain.ShibariumTestnet
"algorand" -> Blockchain.Algorand
"algorand/test" -> Blockchain.AlgorandTestnet
"hedera-hashgraph" -> Blockchain.Hedera
"hedera-hashgraph/test" -> Blockchain.HederaTestnet
"aurora" -> Blockchain.Aurora
"aurora/test" -> Blockchain.AuroraTestnet
"areon-network" -> Blockchain.Areon
"areon-network/test" -> Blockchain.AreonTestnet
"pulsechain" -> Blockchain.PulseChain
"pulsechain/test" -> Blockchain.PulseChainTestnet
"zksync" -> Blockchain.ZkSyncEra
"zksync/test" -> Blockchain.ZkSyncEraTestnet
"moonbeam" -> Blockchain.Moonbeam
"moonbeam/test" -> Blockchain.MoonbeamTestnet
"manta-network" -> Blockchain.Manta
"manta-network/test" -> Blockchain.MantaTestnet
"polygon-zkevm" -> Blockchain.PolygonZkEVM
"polygon-zkevm/test" -> Blockchain.PolygonZkEVMTestnet
"nexa" -> Blockchain.Nexa // FIXME
"nexa/test" -> Blockchain.NexaTestnet // FIXME
"radiant" -> Blockchain.Radiant
"moonriver" -> Blockchain.Moonriver
"moonriver/test" -> Blockchain.MoonriverTestnet
"mantle" -> Blockchain.Mantle
"mantle/test" -> Blockchain.MantleTestnet
"flare-network" -> Blockchain.Flare
"flare-network/test" -> Blockchain.FlareTestnet
"taraxa" -> Blockchain.Taraxa
"taraxa/test" -> Blockchain.TaraxaTestnet
"base" -> Blockchain.Base
"base/test" -> Blockchain.BaseTestnet
"koinos" -> Blockchain.Koinos
"koinos/test" -> Blockchain.KoinosTestnet
else -> null
}
}
@Suppress("ComplexMethod", "LongMethod")
fun Blockchain.toNetworkId(): String {
return when (this) {
Blockchain.Unknown -> "unknown"
Blockchain.Arbitrum -> "arbitrum-one"
Blockchain.ArbitrumTestnet -> "arbitrum-one/test"
Blockchain.Avalanche -> "avalanche"
Blockchain.AvalancheTestnet -> "avalanche/test"
Blockchain.Binance -> "binancecoin"
Blockchain.BinanceTestnet -> "binancecoin/test"
Blockchain.BSC -> "binance-smart-chain"
Blockchain.BSCTestnet -> "binance-smart-chain/test"
Blockchain.Bitcoin -> "bitcoin"
Blockchain.BitcoinTestnet -> "bitcoin/test"
Blockchain.BitcoinCash -> "bitcoin-cash"
Blockchain.BitcoinCashTestnet -> "bitcoin-cash/test"
Blockchain.Cardano -> "cardano"
Blockchain.Dogecoin -> "dogecoin"
Blockchain.Ducatus -> "ducatus"
Blockchain.Ethereum -> "ethereum"
Blockchain.EthereumTestnet -> "ethereum/test"
Blockchain.EthereumClassic -> "ethereum-classic"
Blockchain.EthereumClassicTestnet -> "ethereum-classic/test"
Blockchain.Fantom -> "fantom"
Blockchain.FantomTestnet -> "fantom/test"
Blockchain.Litecoin -> "litecoin"
Blockchain.Polygon -> "polygon-pos"
Blockchain.PolygonTestnet -> "polygon-pos/test"
Blockchain.RSK -> "rootstock"
Blockchain.Stellar -> "stellar"
Blockchain.StellarTestnet -> "stellar/test"
Blockchain.Solana -> "solana"
Blockchain.SolanaTestnet -> "solana/test"
Blockchain.Tezos -> "tezos"
Blockchain.XRP -> "xrp"
Blockchain.Tron -> "tron"
Blockchain.TronTestnet -> "tron/test"
Blockchain.Gnosis -> "xdai"
Blockchain.EthereumPow -> "ethereum-pow-iou"
Blockchain.EthereumPowTestnet -> "ethereum-pow-iou/test"
Blockchain.Dischain -> "ethereumfair" // for backend compatibility
Blockchain.Polkadot -> "polkadot"
Blockchain.PolkadotTestnet -> "polkadot/test"
Blockchain.Kusama -> "kusama"
Blockchain.Optimism -> "optimistic-ethereum"
Blockchain.OptimismTestnet -> "optimistic-ethereum/test"
Blockchain.Dash -> "dash"
Blockchain.Kaspa -> "kaspa"
Blockchain.TON -> "the-open-network"
Blockchain.TONTestnet -> "the-open-network/test"
Blockchain.Kava -> "kava"
Blockchain.KavaTestnet -> "kava/test"
Blockchain.Ravencoin -> "ravencoin"
Blockchain.RavencoinTestnet -> "ravencoin/test"
Blockchain.Cosmos -> "cosmos"
Blockchain.CosmosTestnet -> "cosmos/test"
Blockchain.TerraV1 -> "terra"
Blockchain.TerraV2 -> "terra-2"
Blockchain.Cronos -> "cronos"
Blockchain.Telos -> "telos"
Blockchain.TelosTestnet -> "telos/test"
Blockchain.AlephZero -> "aleph-zero"
Blockchain.AlephZeroTestnet -> "aleph-zero/test"
Blockchain.OctaSpace -> "octaspace"
Blockchain.OctaSpaceTestnet -> "octaspace/test"
Blockchain.Chia -> "chia"
Blockchain.ChiaTestnet -> "chia/test"
Blockchain.Near -> "near-protocol"
Blockchain.NearTestnet -> "near-protocol/test"
Blockchain.Decimal -> "decimal"
Blockchain.DecimalTestnet -> "decimal/test"
Blockchain.XDC -> "xdc-network"
Blockchain.XDCTestnet -> "xdc-network/test"
Blockchain.VeChain -> "vechain"
Blockchain.VeChainTestnet -> "vechain/test"
Blockchain.Aptos -> "aptos"
Blockchain.AptosTestnet -> "aptos/test"
Blockchain.Playa3ull -> "playa3ull-games"
Blockchain.Shibarium -> "shibarium"
Blockchain.ShibariumTestnet -> "shibarium/test"
Blockchain.Algorand -> "algorand"
Blockchain.AlgorandTestnet -> "algorand/test"
Blockchain.Hedera -> "hedera-hashgraph"
Blockchain.HederaTestnet -> "hedera-hashgraph/test"
Blockchain.Aurora -> "aurora"
Blockchain.AuroraTestnet -> "aurora/test"
Blockchain.Areon -> "areon-network"
Blockchain.AreonTestnet -> "areon-network/test"
Blockchain.PulseChain -> "pulsechain"
Blockchain.PulseChainTestnet -> "pulsechain/test"
Blockchain.ZkSyncEra -> "zksync"
Blockchain.ZkSyncEraTestnet -> "zksync/test"
Blockchain.Moonbeam -> "moonbeam"
Blockchain.MoonbeamTestnet -> "moonbeam/test"
Blockchain.Manta -> "manta-network"
Blockchain.MantaTestnet -> "manta-network/test"
Blockchain.PolygonZkEVM -> "polygon-zkevm"
Blockchain.PolygonZkEVMTestnet -> "polygon-zkevm/test"
Blockchain.Nexa -> "nexa" // FIXME
Blockchain.NexaTestnet -> "nexa/test" // FIXME
Blockchain.Radiant -> "radiant"
Blockchain.Moonriver -> "moonriver"
Blockchain.MoonriverTestnet -> "moonriver/test"
Blockchain.Mantle -> "mantle"
Blockchain.MantleTestnet -> "mantle/test"
Blockchain.Flare -> "flare-network"
Blockchain.FlareTestnet -> "flare-network/test"
Blockchain.Taraxa -> "taraxa"
Blockchain.TaraxaTestnet -> "taraxa/test"
Blockchain.Base -> "base"
Blockchain.BaseTestnet -> "base/test"
Blockchain.Koinos -> "koinos"
Blockchain.KoinosTestnet -> "koinos/test"
}
}
@Suppress("ComplexMethod", "LongMethod")
fun Blockchain.toCoinId(): String {
return when (this) {
Blockchain.Binance, Blockchain.BinanceTestnet, Blockchain.BSC, Blockchain.BSCTestnet -> "binancecoin"
Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> "bitcoin"
Blockchain.BitcoinCash, Blockchain.BitcoinCashTestnet -> "bitcoin-cash"
Blockchain.Ethereum, Blockchain.EthereumTestnet -> "ethereum"
Blockchain.EthereumClassic, Blockchain.EthereumClassicTestnet -> "ethereum-classic"
Blockchain.Stellar, Blockchain.StellarTestnet -> "stellar"
Blockchain.Cardano -> "cardano"
Blockchain.Polygon, Blockchain.PolygonTestnet -> "matic-network"
Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> "arbitrum-one"
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> "avalanche-2"
Blockchain.Solana, Blockchain.SolanaTestnet -> "solana"
Blockchain.Fantom, Blockchain.FantomTestnet -> "fantom"
Blockchain.Tron, Blockchain.TronTestnet -> "tron"
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> "polkadot"
Blockchain.Ducatus -> "ducatus"
Blockchain.Litecoin -> "litecoin"
Blockchain.RSK -> "rootstock"
Blockchain.Tezos -> "tezos"
Blockchain.XRP -> "ripple"
Blockchain.Dogecoin -> "dogecoin"
Blockchain.Gnosis -> "xdai"
Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> "ethereum-pow-iou"
Blockchain.Dischain -> "ethereumfair" // for backend compatibility
Blockchain.Kusama -> "kusama"
Blockchain.Optimism, Blockchain.OptimismTestnet -> "optimistic-ethereum"
Blockchain.Dash -> "dash"
Blockchain.Kaspa -> "kaspa"
Blockchain.TON, Blockchain.TONTestnet -> "the-open-network"
Blockchain.Kava, Blockchain.KavaTestnet -> "kava"
Blockchain.Ravencoin, Blockchain.RavencoinTestnet -> "ravencoin"
Blockchain.Cosmos, Blockchain.CosmosTestnet -> "cosmos"
Blockchain.TerraV1 -> "terra-luna"
Blockchain.TerraV2 -> "terra-luna-2"
Blockchain.Cronos -> "crypto-com-chain"
Blockchain.Telos, Blockchain.TelosTestnet -> "telos"
Blockchain.AlephZero, Blockchain.AlephZeroTestnet -> "aleph-zero"
Blockchain.OctaSpace, Blockchain.OctaSpaceTestnet -> "octaspace"
Blockchain.Chia, Blockchain.ChiaTestnet -> "chia"
Blockchain.Near -> "near"
Blockchain.NearTestnet -> "near/test"
Blockchain.Decimal, Blockchain.DecimalTestnet -> "decimal"
Blockchain.XDC, Blockchain.XDCTestnet -> "xdce-crowd-sale"
Blockchain.VeChain, Blockchain.VeChainTestnet -> "vechain"
Blockchain.Aptos -> "aptos"
Blockchain.AptosTestnet -> "aptos/test"
Blockchain.Playa3ull -> "playa3ull-games-2"
Blockchain.Shibarium -> "bone-shibaswap"
Blockchain.ShibariumTestnet -> "bone-shibaswap/test"
Blockchain.Algorand -> "algorand"
Blockchain.AlgorandTestnet -> "algorand/test"
Blockchain.Unknown -> "unknown"
Blockchain.Hedera -> "hedera-hashgraph"
Blockchain.HederaTestnet -> "hedera-hashgraph/test"
Blockchain.Aurora, Blockchain.AuroraTestnet -> "aurora-ethereum"
Blockchain.Areon, Blockchain.AreonTestnet -> "areon-network"
Blockchain.PulseChain, Blockchain.PulseChainTestnet -> "pulsechain"
Blockchain.ZkSyncEra, Blockchain.ZkSyncEraTestnet -> "zksync-ethereum"
Blockchain.Moonbeam, Blockchain.MoonbeamTestnet -> "moonbeam"
Blockchain.Manta, Blockchain.MantaTestnet -> "manta-network-ethereum"
Blockchain.PolygonZkEVM, Blockchain.PolygonZkEVMTestnet -> "polygon-zkevm-ethereum"
Blockchain.Nexa, Blockchain.NexaTestnet -> "nexa" // FIXME
Blockchain.Radiant -> "radiant"
Blockchain.Moonriver, Blockchain.MoonriverTestnet -> "moonriver"
Blockchain.Mantle, Blockchain.MantleTestnet -> "mantle"
Blockchain.Flare, Blockchain.FlareTestnet -> "flare-networks"
Blockchain.Taraxa, Blockchain.TaraxaTestnet -> "taraxa"
Blockchain.Base, Blockchain.BaseTestnet -> "base-ethereum"
Blockchain.Koinos, Blockchain.KoinosTestnet -> "koinos"
}
}
fun Blockchain.isSupportedInApp(): Boolean {
return !excludedBlockchains.contains(this)
}
fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? {
return when (this) {
Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(NODL_AMOUNT_TO_CREATE_ACCOUNT) else BigDecimal.ONE
Blockchain.XRP -> BigDecimal.TEN
Blockchain.Near, Blockchain.NearTestnet -> 0.00182.toBigDecimal()
Blockchain.Aptos, Blockchain.AptosTestnet -> BigDecimal.ZERO
else -> null
}
}
fun Blockchain.minimalAmount(): BigDecimal {
return BigDecimal.ONE.movePointLeft(decimals())
}
private const val NODL = "NODL"
private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5
private val excludedBlockchains = listOf(
Blockchain.Unknown,
Blockchain.Nexa,
Blockchain.NexaTestnet,
Blockchain.Radiant,
Blockchain.Manta,
Blockchain.MantaTestnet,
Blockchain.Mantle,
Blockchain.MantleTestnet,
Blockchain.Koinos,
Blockchain.KoinosTestnet,
)

View file

@ -1,6 +1,5 @@
package com.tangem.lib.crypto
import com.tangem.lib.crypto.models.Currency
import com.tangem.lib.crypto.models.ProxyAmount
/**
@ -8,28 +7,11 @@ import com.tangem.lib.crypto.models.ProxyAmount
*/
interface UserWalletManager {
/**
* Returns all user tokens (merged from local and backend)
*/
@Throws(IllegalStateException::class)
suspend fun getUserTokens(networkId: String, derivationPath: String?, isExcludeCustom: Boolean): List<Currency>
@Throws(IllegalStateException::class)
fun getNativeTokenForNetwork(networkId: String): Currency
/**
* Returns user walletId or empty string
*/
fun getWalletId(): String
/**
* Checks that token added to user wallet
*
* @param currency to receive referral payments
*/
@Throws(IllegalStateException::class)
suspend fun isTokenAdded(currency: Currency, derivationPath: String?): Boolean
suspend fun hideAllTokens()
/**
@ -41,21 +23,6 @@ interface UserWalletManager {
@Throws(IllegalStateException::class)
suspend fun getWalletAddress(networkId: String, derivationPath: String?): String
/**
* Return balances from wallet found by networkId
*
* @param networkId
* @param extraTokens tokens you want to check balance that not exists in wallet
* @param derivationPath if null uses default
* @return map of <Symbol, [ProxyAmount]>
*/
@Throws(IllegalStateException::class)
suspend fun getCurrentWalletTokensBalance(
networkId: String,
extraTokens: List<Currency>,
derivationPath: String?,
): Map<String, ProxyAmount>
@Throws(IllegalStateException::class)
suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount?