Updated on 2026-08-14
This commit is contained in:
commit
9ba18ee14f
157 changed files with 3478 additions and 2220 deletions
|
|
@ -39,21 +39,26 @@ sealed class TapError(
|
|||
object DustChange : TapError(R.string.send_error_dust_change)
|
||||
data class CreateAccountUnderfunded(override val args: List<Any>) : TapError(R.string.send_error_no_target_account)
|
||||
|
||||
data class UnsupportedState(
|
||||
val stateError: String,
|
||||
val customMessage: String = "Unsupported state:"
|
||||
) : TapError(R.string.common_custom_string, listOf("$customMessage $stateError"))
|
||||
|
||||
sealed class XmlError {
|
||||
object AssetAccountNotCreated : TapError(R.string.send_error_no_account_xlm)
|
||||
}
|
||||
|
||||
sealed class WalletManager {
|
||||
object CreationError: CustomError("Can't create wallet manager")
|
||||
class NoAccountError(amountToCreateAccount: String): CustomError(amountToCreateAccount)
|
||||
class InternalError(message: String): CustomError(message)
|
||||
object BlockchainIsUnreachable: TapError(R.string.wallet_balance_blockchain_unreachable)
|
||||
object BlockchainIsUnreachableTryLater: TapError(R.string.wallet_balance_blockchain_unreachable_try_later)
|
||||
object CreationError : CustomError("Can't create wallet manager")
|
||||
class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount)
|
||||
class InternalError(message: String) : CustomError(message)
|
||||
object BlockchainIsUnreachable : TapError(R.string.wallet_balance_blockchain_unreachable)
|
||||
object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later)
|
||||
}
|
||||
|
||||
data class ValidateTransactionErrors(
|
||||
override val errorList: List<TapError>,
|
||||
override val builder: (List<String>) -> String
|
||||
override val errorList: List<TapError>,
|
||||
override val builder: (List<String>) -> String
|
||||
) : TapError(-1), MultiMessageError
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
|
|
@ -85,6 +91,12 @@ class TapWalletManager {
|
|||
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
|
||||
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
|
||||
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.ShowWalletBackupWarning(
|
||||
show = data.card.settings.isBackupAllowed
|
||||
&& data.card.backupStatus == Card.BackupStatus.NoBackup
|
||||
)
|
||||
)
|
||||
loadData(data)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.squareup.picasso.Target
|
||||
import com.tangem.common.services.Result
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class UrlBitmapLoader {
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
fun loadBitmap(url: String, callback: (Result<Bitmap>) -> Unit) {
|
||||
val target = DownloadTarget(callback)
|
||||
protectedFromGarbageCollectorTargets.add(target)
|
||||
mainHandler.post { Picasso.get().load(url).into(target) }
|
||||
}
|
||||
|
||||
fun loadBitmap(url: String, target: DownloadTarget) {
|
||||
protectedFromGarbageCollectorTargets.add(target)
|
||||
Picasso.get().load(url).into(target)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val protectedFromGarbageCollectorTargets = mutableListOf<Target>()
|
||||
|
||||
// It adds the ability to trigger multiple downloads with a unique callback.
|
||||
open class DownloadTarget(
|
||||
val callback: (Result<Bitmap>) -> Unit,
|
||||
) : Target {
|
||||
|
||||
override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
|
||||
callback(Result.Success(bitmap))
|
||||
protectedFromGarbageCollectorTargets.remove(this)
|
||||
}
|
||||
|
||||
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
callback.invoke(Result.Failure(e ?: Exception("Unknown exception")))
|
||||
protectedFromGarbageCollectorTargets.remove(this)
|
||||
}
|
||||
|
||||
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.tap.domain.configurable.config
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.shop.shopify.ShopifyShop
|
||||
import com.tangem.tap.common.zendesk.ZendeskConfig
|
||||
import com.tangem.tap.domain.configurable.Loader
|
||||
|
||||
/**
|
||||
|
|
@ -11,15 +11,17 @@ import com.tangem.tap.domain.configurable.Loader
|
|||
data class Config(
|
||||
val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
|
||||
val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
|
||||
val onramperApiKey: String = "pk_test_Ix2aCF3ej_5tcDKkBR7MChIvf5Nb0oPORPQ3Oal5G8I0",
|
||||
val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
|
||||
val mercuryoWidgetId: String = "",
|
||||
val mercuryoSecret: String = "",
|
||||
val appsFlyerDevKey: String = "",
|
||||
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
|
||||
val isSendingToPayIdEnabled: Boolean = true,
|
||||
val isTopUpEnabled: Boolean = false,
|
||||
@Deprecated("Not relevant since version 3.23")
|
||||
val isCreatingTwinCardsAllowed: Boolean = false,
|
||||
val shopify: ShopifyShop? = null
|
||||
val shopify: ShopifyShop? = null,
|
||||
val zendesk: ZendeskConfig? = null,
|
||||
)
|
||||
|
||||
class ConfigManager(
|
||||
|
|
@ -32,11 +34,11 @@ class ConfigManager(
|
|||
|
||||
private var defaultConfig = Config()
|
||||
|
||||
fun load(onComplete: VoidCallback? = null) {
|
||||
localLoader.load { config ->
|
||||
setupFeature(config.features)
|
||||
setupKey(config.configValues)
|
||||
onComplete?.invoke()
|
||||
fun load(onComplete: ((config: Config) -> Unit)? = null) {
|
||||
localLoader.load { configModel ->
|
||||
setupFeature(configModel.features)
|
||||
setupKey(configModel.configValues)
|
||||
onComplete?.invoke(config)
|
||||
}
|
||||
// Uncomment to enable remote config
|
||||
// remoteLoader.load { config ->
|
||||
|
|
@ -82,8 +84,9 @@ class ConfigManager(
|
|||
config = config.copy(
|
||||
coinMarketCapKey = values.coinMarketCapKey,
|
||||
moonPayApiKey = values.moonPayApiKey,
|
||||
onramperApiKey = values.onramperApiKey,
|
||||
moonPayApiSecretKey = values.moonPayApiSecretKey,
|
||||
mercuryoWidgetId = values.mercuryoWidgetId,
|
||||
mercuryoSecret = values.mercuryoSecret,
|
||||
blockchainSdkConfig = BlockchainSdkConfig(
|
||||
blockchairApiKey = values.blockchairApiKey,
|
||||
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
|
||||
|
|
@ -92,12 +95,14 @@ class ConfigManager(
|
|||
),
|
||||
appsFlyerDevKey = values.appsFlyerDevKey,
|
||||
shopify = values.shopifyShop,
|
||||
zendesk = values.zendesk,
|
||||
)
|
||||
defaultConfig = defaultConfig.copy(
|
||||
coinMarketCapKey = values.coinMarketCapKey,
|
||||
moonPayApiKey = values.moonPayApiKey,
|
||||
onramperApiKey = values.onramperApiKey,
|
||||
moonPayApiSecretKey = values.moonPayApiSecretKey,
|
||||
mercuryoWidgetId = values.mercuryoWidgetId,
|
||||
mercuryoSecret = values.mercuryoSecret,
|
||||
blockchainSdkConfig = BlockchainSdkConfig(
|
||||
blockchairApiKey = values.blockchairApiKey,
|
||||
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
|
||||
|
|
@ -106,6 +111,7 @@ class ConfigManager(
|
|||
),
|
||||
appsFlyerDevKey = values.appsFlyerDevKey,
|
||||
shopify = values.shopifyShop,
|
||||
zendesk = values.zendesk,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.domain.configurable.config
|
||||
|
||||
import com.tangem.tap.common.shop.shopify.ShopifyShop
|
||||
import com.tangem.tap.common.zendesk.ZendeskConfig
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -15,15 +16,17 @@ class FeatureModel(
|
|||
|
||||
class ConfigValueModel(
|
||||
val coinMarketCapKey: String,
|
||||
val mercuryoWidgetId: String,
|
||||
val mercuryoSecret: String,
|
||||
val moonPayApiKey: String,
|
||||
val onramperApiKey: String,
|
||||
val moonPayApiSecretKey: String,
|
||||
val blockchairApiKey: String?,
|
||||
val blockchairAuthorizationToken: String?,
|
||||
val blockcypherTokens: Set<String>?,
|
||||
val infuraProjectId: String?,
|
||||
val appsFlyerDevKey: String,
|
||||
val shopifyShop: ShopifyShop?
|
||||
val shopifyShop: ShopifyShop?,
|
||||
val zendesk: ZendeskConfig?,
|
||||
)
|
||||
|
||||
class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.tap.common.extensions.containsAny
|
|||
import com.tangem.tap.common.extensions.removeBy
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -188,31 +187,6 @@ class WarningMessagesManager(
|
|||
WarningMessage.Origin.Local
|
||||
)
|
||||
|
||||
fun restoreFundsWarning(): WarningMessage = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Temporary,
|
||||
priority = WarningMessage.Priority.Warning,
|
||||
listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.alert_title,
|
||||
messageResId = R.string.alert_funds_restoration_message,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
buttonTextId = R.string.warning_button_learn_more
|
||||
)
|
||||
|
||||
const val REMAINING_SIGNATURES_WARNING = 10
|
||||
private const val RESTORE_FUNDS_GUIDE_URL_RU =
|
||||
"https://tangem.com/ru/kak-vosstanovit-tokeny-otpravlennye-ne-na-tot-adres-v-tangem-wallet"
|
||||
private const val RESTORE_FUNDS_GUIDE_URL_EN =
|
||||
"https://tangem.com/en/how-to-recover-crypto-sent-to-the-wrong-address-in-tangem-wallet"
|
||||
|
||||
fun getRestoreFundsGuideUrl(locale: String): String {
|
||||
return if (locale == Locale("ru").language) {
|
||||
RESTORE_FUNDS_GUIDE_URL_RU
|
||||
} else {
|
||||
RESTORE_FUNDS_GUIDE_URL_EN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Blockchain.Arbitrum
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeStatus
|
||||
import com.tangem.tap.store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun CurrencyExchangeManager.buyIsAllowed(currency: Currency): Boolean {
|
||||
return this.status?.buyIsAllowed(currency) ?: false
|
||||
}
|
||||
|
||||
fun CurrencyExchangeManager.sellIsAllowed(currency: Currency): Boolean {
|
||||
return this.status?.sellIsAllowed(currency) ?: false
|
||||
}
|
||||
|
||||
fun CurrencyExchangeStatus.buyIsAllowed(currency: Currency): Boolean {
|
||||
if (store.state.globalState.configManager?.config?.isTopUpEnabled == false) return false
|
||||
if (currency.blockchain == Arbitrum) return false
|
||||
if (!isBuyAllowed) return false
|
||||
|
||||
//TODO: temporary, for the 3.32 release, unlock all buy button
|
||||
return true
|
||||
|
||||
return when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val blockchain = currency.blockchain
|
||||
when {
|
||||
blockchain.isTestnet() -> blockchain.getTestnetTopUpUrl() != null
|
||||
blockchain == Blockchain.Unknown -> false
|
||||
else -> availableToBuy.contains(currency.currencySymbol)
|
||||
}
|
||||
}
|
||||
is Currency.Token -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun CurrencyExchangeStatus.sellIsAllowed(currency: Currency): Boolean {
|
||||
if (store.state.globalState.configManager?.config?.isTopUpEnabled == false) return false
|
||||
if (!isSellAllowed) return false
|
||||
|
||||
return when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val blockchain = currency.blockchain
|
||||
when {
|
||||
blockchain.isTestnet() -> false
|
||||
blockchain == Blockchain.Unknown || currency.blockchain == Blockchain.BSC -> false
|
||||
else -> availableToSell.contains(currency.currencySymbol)
|
||||
}
|
||||
}
|
||||
is Currency.Token -> false
|
||||
}
|
||||
}
|
||||
|
|
@ -112,31 +112,44 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
|
|||
this.card = card
|
||||
val curves = card.getCurvesForNonCreatedWallets()
|
||||
|
||||
if (curves.isEmpty()) {
|
||||
val createWalletResponses = card.wallets.map { CreateWalletResponse(card.cardId, it) }
|
||||
proceedWithCreatedWallets(createWalletResponses, session, callback)
|
||||
return
|
||||
}
|
||||
|
||||
CreateWalletsTask(curves).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val createWalletResponses = result.data.createWalletResponses
|
||||
when {
|
||||
card.settings.isBackupAllowed -> {
|
||||
linkPrimaryCard(createWalletResponses, session, callback)
|
||||
}
|
||||
card.settings.isHDWalletAllowed -> {
|
||||
deriveKeys(createWalletResponses, session, callback)
|
||||
}
|
||||
else -> {
|
||||
callback(
|
||||
CompletionResult.Success(
|
||||
CreateProductWalletTaskResponse(card = session.environment.card!!)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
proceedWithCreatedWallets(result.data.createWalletResponses, session, callback)
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithCreatedWallets(
|
||||
createWalletResponses: List<CreateWalletResponse>,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit,
|
||||
) {
|
||||
when {
|
||||
card.settings.isBackupAllowed -> {
|
||||
linkPrimaryCard(createWalletResponses, session, callback)
|
||||
}
|
||||
card.settings.isHDWalletAllowed -> {
|
||||
deriveKeys(createWalletResponses, session, callback)
|
||||
}
|
||||
else -> {
|
||||
callback(
|
||||
CompletionResult.Success(
|
||||
CreateProductWalletTaskResponse(card = session.environment.card!!)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun linkPrimaryCard(
|
||||
createWalletResponse: List<CreateWalletResponse>,
|
||||
session: CardSession,
|
||||
|
|
|
|||
|
|
@ -185,8 +185,9 @@ class CurrenciesRepository(
|
|||
tokens.map {
|
||||
async {
|
||||
tangemNetworkService.getTokens(
|
||||
it.contractAddress,
|
||||
it.blockchainDao.toBlockchain().toNetworkId()
|
||||
contractAddress = it.contractAddress,
|
||||
networkId = it.blockchainDao.toBlockchain().toNetworkId(),
|
||||
active = true,
|
||||
)
|
||||
}
|
||||
}.map { it.await() }
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class LoadAvailableCoinsService(
|
|||
val networkIds = supportedBlockchains.toSet().map { it.toNetworkId() }
|
||||
return networkService.getListOfCoins(
|
||||
networkIds = networkIds,
|
||||
active = true,
|
||||
offset = offset,
|
||||
limit = LOAD_PER_PAGE,
|
||||
searchText = searchInput
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ import android.content.Context
|
|||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.network.common.MoshiConverter
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletForSession
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import timber.log.Timber
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class WalletConnectRepository(val context: Application) {
|
||||
private val moshi = MoshiConverter.defaultMoshi()
|
||||
|
|
@ -35,17 +39,29 @@ class WalletConnectRepository(val context: Application) {
|
|||
fun loadSavedSessions(): List<WalletConnectSession> {
|
||||
return try {
|
||||
val json = context.readFileText(FILE_NAME_PREFIX_SESSIONS)
|
||||
.hexToUtf8()
|
||||
walletConnectAdapter.fromJson(json)!!.map { it.toSession() }
|
||||
} catch (exception: Exception) {
|
||||
Timber.e(exception)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveSessions(sessions: List<WalletConnectSession>) {
|
||||
val json = walletConnectAdapter.toJson(sessions.map { SessionDao.fromSession(it) })
|
||||
.utf8ToHex() // convert to hex to solve problems with saving text with emojis
|
||||
Timber.e("WC sessions, saving following json: $json")
|
||||
context.rewriteFile(json, FILE_NAME_PREFIX_SESSIONS)
|
||||
}
|
||||
|
||||
private fun String.utf8ToHex(): String {
|
||||
return this.toByteArray().toHexString()
|
||||
}
|
||||
|
||||
private fun String.hexToUtf8(): String {
|
||||
return this.hexToBytes().toString(Charset.defaultCharset())
|
||||
}
|
||||
|
||||
private fun Context.readFileText(fileName: String): String =
|
||||
this.openFileInput(fileName).bufferedReader().readText()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue