Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-19 10:54:20 +03:00
commit a8e9144dad
16 changed files with 78 additions and 26 deletions

View file

@ -2,9 +2,15 @@ package com.tangem.tap.proxy
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.Message
import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras
import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
import com.tangem.blockchain.blockchains.optimism.OptimismWalletManager
import com.tangem.blockchain.blockchains.stellar.StellarMemo
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
@ -18,6 +24,8 @@ import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.features.send.impl.presentation.viewmodel.XlmMemoType
import com.tangem.features.send.impl.presentation.viewmodel.determineXlmMemoType
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.lib.crypto.TransactionManager
import com.tangem.lib.crypto.models.*
@ -143,6 +151,25 @@ class TransactionManagerImpl(
return blockchain.getExploreTxUrl(txAddress)
}
override fun getMemoExtras(networkId: String, memo: String?): TransactionExtras? {
val blockchain = Blockchain.fromNetworkId(networkId)
if (memo == null) return null
return when (blockchain) {
Blockchain.Stellar -> {
val xmlMemo = when (determineXlmMemoType(memo)) {
XlmMemoType.TEXT -> StellarMemo.Text(memo)
XlmMemoType.ID -> StellarMemo.Id(memo.toBigInteger())
}
StellarTransactionExtras(xmlMemo)
}
Blockchain.Binance -> BinanceTransactionExtras(memo)
Blockchain.XRP -> memo.toLongOrNull()?.let { XrpTransactionBuilder.XrpTransactionExtras(it) }
Blockchain.Cosmos -> CosmosTransactionExtras(memo)
Blockchain.TON -> TonTransactionExtras(memo)
else -> null
}
}
override fun getNativeTokenDecimals(networkId: String): Int {
return Blockchain.fromNetworkId(networkId)?.decimals() ?: error("blockchain not found")
}

View file

@ -39,6 +39,12 @@ data class ExchangeDataResponse(
@Json(name = "externalTxUrl")
val externalTxUrl: String?, // null if DEX, url of provider exchange status page if CEX
@Json(name = "txExtraIdName")
val txExtraIdName: String?,
@Json(name = "txExtraId")
val txExtraId: String?,
)
enum class TxType {

View file

@ -45,7 +45,7 @@ object PreferencesKeys {
val IS_WALLET_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isWalletSwapPromoShown") }
val IS_TOKEN_SWAP_PROMO_SHOW_KEY by lazy { stringSetPreferencesKey(name = "isTokenSwapPromoShown") }
val IS_TOKEN_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isTokenSwapPromoShown") }
}
/** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore<Preferences> */

View file

@ -21,9 +21,9 @@ class DefaultSwapPromoRepository(
.map { it && checkPromoPeriod() }
}
override fun isReadyToShowToken(userWalletId: String, currencyId: String): Flow<Boolean> {
return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_SHOW_KEY, emptySet())
.map { !it.contains(userWalletId + currencyId) && checkPromoPeriod() }
override fun isReadyToShowToken(): Flow<Boolean> {
return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_SHOW_KEY, true)
.map { it && checkPromoPeriod() }
}
override suspend fun setNeverToShowWallet() {
@ -33,14 +33,11 @@ class DefaultSwapPromoRepository(
)
}
override suspend fun setNeverToShowToken(userWalletId: String, currencyId: String) {
appPreferencesStore.editData { mutablePreferences ->
val storesCurrencies = mutablePreferences.getOrDefault(IS_TOKEN_SWAP_PROMO_SHOW_KEY, emptySet())
mutablePreferences.set(
key = IS_TOKEN_SWAP_PROMO_SHOW_KEY,
value = storesCurrencies.toMutableSet().apply { add(userWalletId + currencyId) },
)
}
override suspend fun setNeverToShowToken() {
appPreferencesStore.store(
key = IS_TOKEN_SWAP_PROMO_SHOW_KEY,
value = false,
)
}
private fun checkPromoPeriod(): Boolean {

View file

@ -5,9 +5,7 @@ import kotlinx.coroutines.flow.Flow
class ShouldShowSwapPromoTokenUseCase(private val swapPromoRepository: SwapPromoRepository) {
operator fun invoke(userWalletId: String, currencyId: String): Flow<Boolean> =
swapPromoRepository.isReadyToShowToken(userWalletId, currencyId)
operator fun invoke(): Flow<Boolean> = swapPromoRepository.isReadyToShowToken()
suspend fun neverToShow(userWalletId: String, currencyId: String) =
swapPromoRepository.setNeverToShowToken(userWalletId, currencyId)
suspend fun neverToShow() = swapPromoRepository.setNeverToShowToken()
}

View file

@ -5,9 +5,9 @@ import kotlinx.coroutines.flow.Flow
interface SwapPromoRepository {
fun isReadyToShowWallet(): Flow<Boolean>
fun isReadyToShowToken(userWalletId: String, currencyId: String): Flow<Boolean>
fun isReadyToShowToken(): Flow<Boolean>
suspend fun setNeverToShowWallet()
suspend fun setNeverToShowToken(userWalletId: String, currencyId: String)
suspend fun setNeverToShowToken()
}

View file

@ -85,7 +85,7 @@ class GetCurrencyWarningsUseCase(
val currency = currencyStatus.currency
val cryptoStatuses = operations.getCurrenciesStatusesSync()
return combine(
showSwapPromoTokenUseCase(userWalletId.stringValue, currency.id.value).conflate(),
showSwapPromoTokenUseCase().conflate(),
flowOf(marketCryptoCurrencyRepository.isExchangeable(userWalletId, currency)).conflate(),
) { shouldShowSwapPromo, isExchangeable ->
if (shouldShowSwapPromo && isExchangeable) {

View file

@ -30,10 +30,10 @@ import com.tangem.domain.wallets.legacy.WalletsStateHolder
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.converters.*
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.models.domain.*
import com.tangem.feature.swap.domain.models.DataError
import com.tangem.feature.swap.domain.models.ExpressException
import com.tangem.feature.swap.domain.models.createFromAmountWithOffset
import com.tangem.feature.swap.domain.models.domain.*
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext

View file

@ -34,6 +34,8 @@ class ExpressDataConverter : Converter<ExchangeDataResponse, SwapDataModel> {
txTo = transactionDto.txTo,
externalTxId = requireNotNull(transactionDto.externalTxId),
externalTxUrl = requireNotNull(transactionDto.externalTxUrl),
txExtraIdName = transactionDto.txExtraIdName,
txExtraId = transactionDto.txExtraId,
)
}
}

View file

@ -3,8 +3,8 @@ package com.tangem.feature.swap.domain.api
import arrow.core.Either
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.domain.models.domain.*
import com.tangem.feature.swap.domain.models.DataError
import com.tangem.feature.swap.domain.models.domain.*
import java.math.BigDecimal
interface SwapRepository {

View file

@ -25,5 +25,7 @@ sealed class ExpressTransactionModel {
override val txTo: String,
val externalTxId: String,
val externalTxUrl: String,
val txExtraIdName: String?,
val txExtraId: String?,
) : ExpressTransactionModel()
}

View file

@ -499,13 +499,23 @@ internal class SwapInteractorImpl @Inject constructor(
toAddress = currencyToGet.value.networkAddress?.defaultAddress?.value ?: "",
).getOrNull()
val exchangeDataCex = exchangeData?.transaction as? ExpressTransactionModel.CEX ?: return TxState.UnknownError
val txExtras = transactionManager.getMemoExtras(
currencyToSend.currency.network.backendId,
exchangeDataCex.txExtraId,
)
if (txExtras == null && exchangeDataCex.txExtraId != null) {
return TxState.UnknownError
}
val txData = walletManagersFacade.createTransaction(
amount = amount.value.convertToAmount(currencyToSend.currency),
fee = getFeeForTransaction(txFee),
memo = null,
destination = (exchangeData?.transaction as ExpressTransactionModel.CEX).txTo,
destination = exchangeDataCex.txTo,
userWalletId = userWalletId,
network = currencyToSend.currency.network,
)?.copy(
extras = txExtras,
)
val result = sendTransactionUseCase(

View file

@ -563,14 +563,14 @@ internal class TokenDetailsViewModel @Inject constructor(
override fun onSwapPromoDismiss() {
viewModelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow(userWalletId.stringValue, cryptoCurrency.id.value)
shouldShowSwapPromoTokenUseCase.neverToShow()
analyticsEventsHandler.send(TokenSwapPromoAnalyticsEvent.Close)
}
}
override fun onSwapPromoClick() {
viewModelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow(userWalletId.stringValue, cryptoCurrency.id.value)
shouldShowSwapPromoTokenUseCase.neverToShow()
analyticsEventsHandler.send(TokenSwapPromoAnalyticsEvent.Exchange(cryptoCurrency.symbol))
}
onSwapClick()

View file

@ -88,7 +88,7 @@ spr-client = "3.6.2"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "release-app_5.4-417"
tangemBlockchainSdk = "release-app_5.4-419"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_5.4-315"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^

View file

@ -1,5 +1,8 @@
plugins {
alias(deps.plugins.kotlin.jvm)
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.kotlin.serialization)
id("configuration")
}
@ -7,4 +10,7 @@ dependencies {
/** Coroutines */
implementation(deps.kotlin.coroutines)
/** SDK */
implementation(deps.tangem.blockchain)
}

View file

@ -1,5 +1,6 @@
package com.tangem.lib.crypto
import com.tangem.blockchain.common.TransactionExtras
import com.tangem.lib.crypto.models.*
import com.tangem.lib.crypto.models.transactions.SendTxResult
import java.math.BigDecimal
@ -71,4 +72,7 @@ interface TransactionManager {
@Throws(IllegalStateException::class)
fun getExplorerTransactionLink(networkId: String, txAddress: String): String
// TODO: move to another place to use as in Send feature
fun getMemoExtras(networkId: String, memo: String?): TransactionExtras?
}