Updated on 2026-08-14
This commit is contained in:
parent
547d562d6d
commit
d0ef5c70cd
12 changed files with 175 additions and 60 deletions
|
|
@ -5,6 +5,9 @@ import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
|||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.demo.models.DemoConfig
|
||||
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
|
||||
import com.tangem.domain.dynamicaddresses.GetDynamicReceiveAddressUseCase
|
||||
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||
import com.tangem.domain.notifications.repository.PushNotificationsRepository
|
||||
|
|
@ -257,10 +260,16 @@ internal object TransactionDomainModule {
|
|||
fun provideReceiveAddressesFactory(
|
||||
getEnsNameUseCase: GetEnsNameUseCase,
|
||||
getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase,
|
||||
getDynamicReceiveAddressUseCase: GetDynamicReceiveAddressUseCase,
|
||||
dynamicAddressesRepository: DynamicAddressesRepository,
|
||||
dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
|
||||
): ReceiveAddressesFactory {
|
||||
return ReceiveAddressesFactory(
|
||||
getEnsNameUseCase = getEnsNameUseCase,
|
||||
getViewedTokenReceiveWarningUseCase = getViewedTokenReceiveWarningUseCase,
|
||||
getDynamicReceiveAddressUseCase = getDynamicReceiveAddressUseCase,
|
||||
dynamicAddressesRepository = dynamicAddressesRepository,
|
||||
dynamicAddressesFeatureToggles = dynamicAddressesFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -487,6 +487,7 @@
|
|||
<string name="dynamic_addresses_disable_title">Disable dynamic addresses</string>
|
||||
<string name="dynamic_addresses_disabled_popup_title">Dynamic addresses disabled</string>
|
||||
<string name="dynamic_addresses_enabled_toast_title">Dynamic addresses enabled</string>
|
||||
<string name="dynamic_addresses_receive_badge">Dynamic address</string>
|
||||
<string name="dynamic_addresses_enter_features_privacy_description">Use a new address for each transaction to reduce traceability and improve on-chain privacy.</string>
|
||||
<string name="dynamic_addresses_enter_features_privacy_title">Enhanced Privacy</string>
|
||||
<string name="dynamic_addresses_enter_features_receving_description">Easily receive funds in UTXO-based networks with automatic address generation — no manual address management required.</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M3.443,6.057C3.111,6.482 2.673,6.326 2.516,5.781L1.727,3.071C1.602,2.639 1.865,2.301 2.316,2.32L5.139,2.414C5.702,2.433 5.953,2.827 5.627,3.246L5.064,3.966L5.089,3.979C6.241,4.742 7.649,6.332 7.987,7.384H8.012C8.344,6.339 9.759,4.742 10.91,3.979L10.942,3.96L10.366,3.165C10.053,2.733 10.309,2.345 10.879,2.351L13.702,2.339C14.153,2.339 14.403,2.689 14.266,3.121L13.389,5.8C13.22,6.339 12.769,6.482 12.45,6.038L11.843,5.199L11.705,5.287C10.447,6.088 8.757,8.06 8.757,9.493V12.842C8.757,13.387 8.488,13.681 8,13.681C7.511,13.681 7.248,13.387 7.248,12.842V9.493C7.248,8.06 5.546,6.088 4.294,5.287L4.125,5.18L3.443,6.057Z"
|
||||
android:fillColor="#0099FF"/>
|
||||
</vector>
|
||||
|
|
@ -476,12 +476,12 @@ internal class DefaultWalletManagersFacade @Inject constructor(
|
|||
val dynamicAddressesManager = walletManager as? DynamicAddressesManager ?: return null
|
||||
return dynamicAddressesManager.usedAddresses
|
||||
.filter { usedAddress ->
|
||||
val nodes = runCatching { DerivationPath(usedAddress.path).nodes }.getOrNull()
|
||||
val nodes = runCatching { DerivationPath(usedAddress.derivationPath).nodes }.getOrNull()
|
||||
?: return@filter false
|
||||
nodes.size >= XPUB_PATH_MIN_NODES && nodes[nodes.size - 2].index == RECEIVE_CHAIN_INDEX
|
||||
}
|
||||
.maxByOrNull { usedAddress ->
|
||||
runCatching { DerivationPath(usedAddress.path).nodes.last().index }.getOrDefault(0L)
|
||||
runCatching { DerivationPath(usedAddress.derivationPath).nodes.last().index }.getOrDefault(0L)
|
||||
}
|
||||
?.address
|
||||
}
|
||||
|
|
@ -490,7 +490,7 @@ internal class DefaultWalletManagersFacade @Inject constructor(
|
|||
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = network)
|
||||
val dynamicAddressesManager = walletManager as? DynamicAddressesManager ?: return false
|
||||
return dynamicAddressesManager.usedAddresses.any { usedAddress ->
|
||||
val nodes = runCatching { DerivationPath(usedAddress.path).nodes }.getOrNull()
|
||||
val nodes = runCatching { DerivationPath(usedAddress.derivationPath).nodes }.getOrNull()
|
||||
?: return@any false
|
||||
val isBaseAddress = nodes.size >= XPUB_PATH_MIN_NODES &&
|
||||
nodes[nodes.size - 2].index == RECEIVE_CHAIN_INDEX &&
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ data class TokenReceiveConfig(
|
|||
|
||||
@Serializable
|
||||
data class ReceiveAddressModel(
|
||||
val nameService: NameService,
|
||||
val displayType: DisplayType,
|
||||
val value: String,
|
||||
) {
|
||||
enum class NameService {
|
||||
Default, Legacy, Ens
|
||||
enum class DisplayType {
|
||||
Default, Legacy, Ens, Dynamic,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ dependencies {
|
|||
implementation(projects.libs.crypto)
|
||||
|
||||
implementation(projects.domain.account.status)
|
||||
implementation(projects.domain.dynamicAddresses)
|
||||
implementation(projects.domain.dynamicAddresses.models)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.walletManager)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package com.tangem.domain.transaction.usecase
|
||||
|
||||
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
|
||||
import com.tangem.domain.dynamicaddresses.GetDynamicReceiveAddressUseCase
|
||||
import com.tangem.domain.dynamicaddresses.model.DynamicAddressesStatus
|
||||
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
|
||||
import com.tangem.domain.models.Asset
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
|
|
@ -12,10 +16,15 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase
|
||||
import com.tangem.domain.transaction.R
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
||||
class ReceiveAddressesFactory(
|
||||
private val getEnsNameUseCase: GetEnsNameUseCase,
|
||||
private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase,
|
||||
private val getDynamicReceiveAddressUseCase: GetDynamicReceiveAddressUseCase,
|
||||
private val dynamicAddressesRepository: DynamicAddressesRepository,
|
||||
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
|
||||
) {
|
||||
|
||||
suspend fun create(
|
||||
|
|
@ -32,27 +41,14 @@ class ReceiveAddressesFactory(
|
|||
address = addresses.defaultAddress.value,
|
||||
)
|
||||
|
||||
val receiveAddresses = buildList {
|
||||
ensName?.let { ens ->
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
nameService = ReceiveAddressModel.NameService.Ens,
|
||||
value = ens,
|
||||
),
|
||||
)
|
||||
}
|
||||
addresses.availableAddresses.map { address ->
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
nameService = when (address.type) {
|
||||
NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default
|
||||
NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy
|
||||
},
|
||||
value = address.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
val dynamicAddress = getDynamicAddressIfEnabled(userWalletId, cryptoCurrency)
|
||||
|
||||
val receiveAddresses = if (dynamicAddress != null) {
|
||||
buildDynamicAddressList(ensName, dynamicAddress)
|
||||
} else {
|
||||
buildStandardAddressList(ensName, addresses)
|
||||
}
|
||||
|
||||
return TokenReceiveConfig(
|
||||
shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(),
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
|
|
@ -64,6 +60,52 @@ class ReceiveAddressesFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun getDynamicAddressIfEnabled(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): String? {
|
||||
if (!dynamicAddressesFeatureToggles.isDynamicAddressesEnabled) return null
|
||||
if (cryptoCurrency !is CryptoCurrency.Coin) return null
|
||||
|
||||
val status = dynamicAddressesRepository.getStatus(userWalletId, cryptoCurrency.network).firstOrNull()
|
||||
if (status != DynamicAddressesStatus.ENABLED) return null
|
||||
|
||||
return getDynamicReceiveAddressUseCase(userWalletId, cryptoCurrency.network)
|
||||
.onLeft { TangemLogger.e("Failed to get dynamic receive address: ${it.message}") }
|
||||
.getOrNull()
|
||||
}
|
||||
|
||||
private fun buildDynamicAddressList(ensName: String?, dynamicAddress: String): List<ReceiveAddressModel> =
|
||||
buildList {
|
||||
ensName?.let { ens ->
|
||||
add(ReceiveAddressModel(displayType = ReceiveAddressModel.DisplayType.Ens, value = ens))
|
||||
}
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
displayType = ReceiveAddressModel.DisplayType.Dynamic,
|
||||
value = dynamicAddress,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildStandardAddressList(ensName: String?, addresses: NetworkAddress): List<ReceiveAddressModel> =
|
||||
buildList {
|
||||
ensName?.let { ens ->
|
||||
add(ReceiveAddressModel(displayType = ReceiveAddressModel.DisplayType.Ens, value = ens))
|
||||
}
|
||||
addresses.availableAddresses.map { address ->
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
displayType = when (address.type) {
|
||||
NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.DisplayType.Default
|
||||
NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.DisplayType.Legacy
|
||||
},
|
||||
value = address.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun createForNft(
|
||||
userWalletId: UserWalletId,
|
||||
addresses: NetworkAddress,
|
||||
|
|
@ -82,7 +124,7 @@ class ReceiveAddressesFactory(
|
|||
ensName?.let { ens ->
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
nameService = ReceiveAddressModel.NameService.Ens,
|
||||
displayType = ReceiveAddressModel.DisplayType.Ens,
|
||||
value = ens,
|
||||
),
|
||||
)
|
||||
|
|
@ -90,9 +132,9 @@ class ReceiveAddressesFactory(
|
|||
addresses.availableAddresses.map { address ->
|
||||
add(
|
||||
ReceiveAddressModel(
|
||||
nameService = when (address.type) {
|
||||
NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default
|
||||
NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy
|
||||
displayType = when (address.type) {
|
||||
NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.DisplayType.Default
|
||||
NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.DisplayType.Legacy
|
||||
},
|
||||
value = address.value,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.ReceiveAddressModel.NameService
|
||||
import com.tangem.domain.models.ReceiveAddressModel.DisplayType
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
import com.tangem.domain.pay.model.TangemPayTopUpData
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
|
|
@ -42,7 +42,7 @@ internal class TangemPayAddFundsModel @Inject constructor(
|
|||
depositAddress = params.depositAddress,
|
||||
receiveAddress = listOf(
|
||||
ReceiveAddressModel(
|
||||
nameService = NameService.Default,
|
||||
displayType = DisplayType.Default,
|
||||
value = params.depositAddress,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ internal data class ReceiveAddress(
|
|||
data class Default(override val displayName: TextReference) : Primary
|
||||
|
||||
data class Legacy(override val displayName: TextReference) : Primary
|
||||
|
||||
data class Dynamic(override val displayName: TextReference) : Primary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
internal class TokenReceiveStateFactory(
|
||||
private val currentStateProvider: Provider<TokenReceiveUM>,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
|
|
@ -66,8 +67,9 @@ internal class TokenReceiveStateFactory(
|
|||
compareBy { address ->
|
||||
when (address.type) {
|
||||
is Ens -> 0
|
||||
is Primary.Default -> 1
|
||||
is Primary.Legacy -> 2
|
||||
is Primary.Dynamic -> 1
|
||||
is Primary.Default -> 2
|
||||
is Primary.Legacy -> 3
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -82,32 +84,18 @@ internal class TokenReceiveStateFactory(
|
|||
addresses: List<ReceiveAddressModel>,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ImmutableList<ReceiveAddress> {
|
||||
val needUseToLegacyAndDefaultName = addresses.any { it.nameService == ReceiveAddressModel.NameService.Legacy }
|
||||
val shouldUseToLegacyAndDefaultName = addresses.any { it.displayType == ReceiveAddressModel.DisplayType.Legacy }
|
||||
|
||||
val receiveAddresses = addresses.map { model ->
|
||||
val type = when (model.nameService) {
|
||||
ReceiveAddressModel.NameService.Default -> {
|
||||
val displayName = when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> cryptoCurrency.name
|
||||
is CryptoCurrency.Token -> cryptoCurrency.symbol
|
||||
}
|
||||
|
||||
Primary.Default(
|
||||
displayName = if (needUseToLegacyAndDefaultName) {
|
||||
TextReference.Res(R.string.domain_receive_assets_default_address)
|
||||
} else {
|
||||
TextReference.Combined(
|
||||
wrappedList(
|
||||
TextReference.Str(displayName),
|
||||
TextReference.Str(" "),
|
||||
TextReference.Res(R.string.common_address),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
val type = when (model.displayType) {
|
||||
ReceiveAddressModel.DisplayType.Default -> {
|
||||
Primary.Default(displayName = defaultDisplayName(cryptoCurrency, shouldUseToLegacyAndDefaultName))
|
||||
}
|
||||
ReceiveAddressModel.NameService.Ens -> Ens
|
||||
ReceiveAddressModel.NameService.Legacy -> Primary.Legacy(
|
||||
ReceiveAddressModel.DisplayType.Dynamic -> {
|
||||
Primary.Dynamic(displayName = coinAddressDisplayName(cryptoCurrency))
|
||||
}
|
||||
ReceiveAddressModel.DisplayType.Ens -> Ens
|
||||
ReceiveAddressModel.DisplayType.Legacy -> Primary.Legacy(
|
||||
displayName = resourceReference(
|
||||
R.string.domain_receive_assets_legacy_address,
|
||||
WrappedList(listOf(cryptoCurrency.name)),
|
||||
|
|
@ -124,8 +112,9 @@ internal class TokenReceiveStateFactory(
|
|||
compareBy { address ->
|
||||
when (address.type) {
|
||||
is Ens -> 0
|
||||
is Primary.Default -> 1
|
||||
is Primary.Legacy -> 2
|
||||
is Primary.Dynamic -> 1
|
||||
is Primary.Default -> 2
|
||||
is Primary.Legacy -> 3
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -170,4 +159,29 @@ internal class TokenReceiveStateFactory(
|
|||
isGrayscale = false,
|
||||
shouldShowCustomBadge = false,
|
||||
)
|
||||
|
||||
private fun defaultDisplayName(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
needUseToLegacyAndDefaultName: Boolean,
|
||||
): TextReference {
|
||||
return if (needUseToLegacyAndDefaultName) {
|
||||
TextReference.Res(R.string.domain_receive_assets_default_address)
|
||||
} else {
|
||||
coinAddressDisplayName(cryptoCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
private fun coinAddressDisplayName(cryptoCurrency: CryptoCurrency): TextReference {
|
||||
val displayName = when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> cryptoCurrency.name
|
||||
is CryptoCurrency.Token -> cryptoCurrency.symbol
|
||||
}
|
||||
return TextReference.Combined(
|
||||
wrappedList(
|
||||
TextReference.Str(displayName),
|
||||
TextReference.Str(" "),
|
||||
TextReference.Res(R.string.common_address),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
|
@ -300,6 +301,7 @@ private fun PrimaryAddressesItems(
|
|||
onShareClick = { onShareClick(selectedAddress.value) },
|
||||
primaryType = selectedAddress.type as ReceiveAddress.Type.Primary,
|
||||
address = selectedAddress.value,
|
||||
isDynamicAddress = selectedAddress.type is ReceiveAddress.Type.Primary.Dynamic,
|
||||
snackbarHostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
|
|
@ -345,6 +347,7 @@ private fun AddressItem(
|
|||
onShareClick: () -> Unit,
|
||||
primaryType: ReceiveAddress.Type.Primary,
|
||||
address: String,
|
||||
isDynamicAddress: Boolean,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -369,6 +372,11 @@ private fun AddressItem(
|
|||
|
||||
SpacerH(12.dp)
|
||||
|
||||
if (isDynamicAddress) {
|
||||
DynamicAddressBadge()
|
||||
SpacerH(8.dp)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = primaryType.displayName.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
|
|
@ -564,6 +572,34 @@ private fun ActionButtonWithResizableText(config: ActionButtonConfig, modifier:
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DynamicAddressBadge(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.text.accent.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(percent = 50),
|
||||
)
|
||||
.padding(horizontal = 12.dp, vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
id = R.drawable.ic_dynamic_addresses_badge_16,
|
||||
),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.dynamic_addresses_receive_badge),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# https://github.com/tangem/tangem-sdk-android/
|
||||
# https://github.com/tangem/vico
|
||||
|
||||
tangemBlockchainSdk = "develop-1486"
|
||||
tangemBlockchainSdk = "develop-1487"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-602"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue