Updated on 2026-08-14

This commit is contained in:
Tangem 2022-09-16 14:00:12 +03:00
commit 27f43629fa
29 changed files with 187 additions and 271 deletions

View file

@ -1,18 +1,18 @@
package com.tangem.tap
import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import coil.ImageLoader
import coil.ImageLoaderFactory
import com.appsflyer.AppsFlyerLib
import com.google.firebase.ktx.Firebase
import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
import com.tangem.Log
import com.tangem.LogFormat
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.domain.DomainLayer
import com.tangem.domain.common.LogConfig
import com.tangem.network.common.MoshiConverter
import com.tangem.tap.common.AndroidAssetReader
import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
import com.tangem.tap.common.feedback.FeedbackManager
@ -22,10 +22,9 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.appReducer
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.shop.TangemShopService
import com.tangem.tap.domain.configurable.config.Config
import com.tangem.tap.domain.configurable.config.ConfigManager
import com.tangem.tap.domain.configurable.config.FeaturesLocalLoader
import com.tangem.tap.domain.configurable.config.FeaturesRemoteLoader
import com.tangem.tap.domain.configurable.warningMessage.RemoteWarningLoader
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
import com.tangem.tap.domain.tokens.CurrenciesRepository
import com.tangem.tap.domain.walletconnect.WalletConnectRepository
@ -38,7 +37,7 @@ import timber.log.Timber
val store = Store(
reducer = ::appReducer,
middleware = AppState.getMiddleware(),
state = AppState()
state = AppState(),
)
lateinit var foregroundActivityObserver: ForegroundActivityObserver
@ -49,36 +48,28 @@ lateinit var walletConnectRepository: WalletConnectRepository
lateinit var shopService: TangemShopService
class TapApplication : Application(), ImageLoaderFactory {
override fun onCreate() {
super.onCreate()
DomainLayer.init()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings {
this.minimumFetchIntervalInSeconds = 60
})
} else {
Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings {
this.minimumFetchIntervalInSeconds = 3600
})
}
NetworkConnectivity.createInstance(store, this)
preferencesStorage = PreferencesStorage(this)
currenciesRepository = CurrenciesRepository(
this, store.state.domainNetworks.tangemTechService
)
walletConnectRepository = WalletConnectRepository(this)
foregroundActivityObserver = ForegroundActivityObserver()
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
initFeedbackManager()
loadConfigs()
DomainLayer.init()
NetworkConnectivity.createInstance(store, this)
preferencesStorage = PreferencesStorage(this)
currenciesRepository = CurrenciesRepository(this, store.state.domainNetworks.tangemTechService)
walletConnectRepository = WalletConnectRepository(this)
val configLoader = FeaturesLocalLoader(AndroidAssetReader(this), MoshiConverter.defaultMoshi())
initConfigManager(configLoader, ::initWithConfigDependency)
initWarningMessagesManager()
BlockchainSdkRetrofitBuilder.enableNetworkLogging = LogConfig.network.blockchainSdkNetwork
initAppsFlyer()
}
override fun newImageLoader(): ImageLoader {
@ -88,64 +79,71 @@ class TapApplication : Application(), ImageLoaderFactory {
)
}
private fun loadConfigs() {
val moshi = MoshiConverter.defaultMoshi()
val localLoader = FeaturesLocalLoader(this, moshi)
val remoteLoader = FeaturesRemoteLoader(moshi)
val configManager = ConfigManager(localLoader, remoteLoader)
configManager.load { config ->
private fun initConfigManager(loader: FeaturesLocalLoader, onComplete: (Config) -> Unit) {
val configManager = ConfigManager()
configManager.load(loader) { config ->
store.dispatch(GlobalAction.SetConfigManager(configManager))
shopService = TangemShopService(
application = this,
shopifyShop = config.shopify!!
)
store.state.globalState.feedbackManager?.initChat(
context = this,
zendeskConfig = config.zendesk!!
)
onComplete(config)
}
val warningsManager = WarningMessagesManager(RemoteWarningLoader(moshi))
warningsManager.load { store.dispatch(GlobalAction.SetWarningManager(warningsManager)) }
}
private fun initFeedbackManager() {
val infoHolder = AdditionalFeedbackInfo()
infoHolder.setAppVersion(this)
val logLevels = listOf(
Log.Level.ApduCommand,
Log.Level.Apdu,
Log.Level.Tlv,
Log.Level.Nfc,
Log.Level.Command,
Log.Level.Session,
Log.Level.View,
Log.Level.Network,
Log.Level.Error,
)
val logWriter = TangemLogCollector(
levels = logLevels,
messageFormatter = LogFormat.StairsFormatter(),
)
Log.addLogger(logWriter)
store.dispatch(
GlobalAction.SetFeedbackManager(
FeedbackManager(
infoHolder = infoHolder,
logCollector = logWriter,
preferencesStorage = preferencesStorage,
logEnabled = LogConfig.zendesk,
),
),
)
private fun initWithConfigDependency(config: Config) {
shopService = TangemShopService(this, config.shopify!!)
initAppsFlyer(this, config)
initFeedbackManager(this, preferencesStorage, config)
}
private fun initAppsFlyer() {
val devKey = store.state.globalState.configManager?.config?.appsFlyerDevKey ?: return
AppsFlyerLib.getInstance().init(devKey, null, this)
AppsFlyerLib.getInstance().start(this)
val analyticsHandler = GlobalAnalyticsHandler.createDefaultAnalyticHandlers(this)
private fun initAppsFlyer(context: Context, config: Config) {
AppsFlyerLib.getInstance().init(config.appsFlyerDevKey, null, context)
AppsFlyerLib.getInstance().start(context)
val analyticsHandler = GlobalAnalyticsHandler.createDefaultAnalyticHandlers(context)
store.dispatch(GlobalAction.SetAnanlyticHandlers(analyticsHandler))
}
private fun initFeedbackManager(context: Context, preferencesStorage: PreferencesStorage, config: Config) {
fun initAdditionalFeedbackInfo(context: Context): AdditionalFeedbackInfo = AdditionalFeedbackInfo().apply {
appVersion = try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
pInfo.versionName
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
"x.y.z"
}
}
fun initTangemLogCollector(): TangemLogCollector {
val logLevels = listOf(
Log.Level.ApduCommand,
Log.Level.Apdu,
Log.Level.Tlv,
Log.Level.Nfc,
Log.Level.Command,
Log.Level.Session,
Log.Level.View,
Log.Level.Network,
Log.Level.Error,
)
return TangemLogCollector(logLevels, LogFormat.StairsFormatter())
}
val additionalFeedbackInfo = initAdditionalFeedbackInfo(context)
val tangemLogCollector = initTangemLogCollector()
Log.addLogger(tangemLogCollector)
val feedbackManager = FeedbackManager(
infoHolder = additionalFeedbackInfo,
logCollector = tangemLogCollector,
preferencesStorage = preferencesStorage,
logEnabled = LogConfig.zendesk,
)
feedbackManager.initChat(
context = context,
zendeskConfig = config.zendesk!!,
)
store.dispatch(GlobalAction.SetFeedbackManager(feedbackManager))
}
private fun initWarningMessagesManager() {
store.dispatch(GlobalAction.SetWarningManager(WarningMessagesManager()))
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.tap.common
import android.content.Context
import com.tangem.tap.common.extensions.readAssetAsString
/**
[REDACTED_AUTHOR]
*/
interface AssetReader {
fun readAssetAsString(name: String): String
}
class AndroidAssetReader(
private val context: Context,
) : AssetReader {
override fun readAssetAsString(name: String): String {
return context.readAssetAsString(name)
}
}

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics
import android.app.Application
import android.content.Context
import com.appsflyer.AFInAppEventParameterName
import com.appsflyer.AFInAppEventType
import com.appsflyer.AppsFlyerLib
@ -8,7 +8,7 @@ import com.shopify.buy3.Storefront
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
class AppsFlyerAnalyticsHandler(val context: Application): AnalyticsHandler() {
class AppsFlyerAnalyticsHandler(val context: Context): AnalyticsHandler() {
override fun triggerEvent(
event: AnalyticsEvent,
@ -16,8 +16,7 @@ class AppsFlyerAnalyticsHandler(val context: Application): AnalyticsHandler() {
blockchain: String?,
params: Map<String, String>
) {
AppsFlyerLib.getInstance().logEvent(context,
event.event, prepareParams(card, blockchain))
AppsFlyerLib.getInstance().logEvent(context, event.event, prepareParams(card, blockchain))
}
override fun logCardSdkError(

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics
import android.app.Application
import android.content.Context
import com.shopify.buy3.Storefront
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
@ -42,7 +42,7 @@ class GlobalAnalyticsHandler(val analyticsHandlers: List<AnalyticsHandler>) :
}
companion object {
fun createDefaultAnalyticHandlers(context: Application): GlobalAnalyticsHandler {
fun createDefaultAnalyticHandlers(context: Context): GlobalAnalyticsHandler {
return GlobalAnalyticsHandler(
listOf(
FirebaseAnalyticsHandler,

View file

@ -1,7 +1,5 @@
package com.tangem.tap.common.feedback
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
@ -45,15 +43,6 @@ class AdditionalFeedbackInfo {
var fee: String = ""
var token: String = ""
fun setAppVersion(context: Context) {
try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
appVersion = pInfo.versionName
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
}
fun setCardInfo(data: ScanResponse) {
cardId = data.card.cardId
cardBlockchain = data.walletData?.blockchain ?: ""

View file

@ -31,13 +31,13 @@ sealed class TapError(
object InsufficientBalance : TapError(R.string.send_error_insufficient_balance)
object BlockchainInternalError : TapError(R.string.send_error_blockchain_internal)
object AmountExceedsBalance : TapError(R.string.send_validation_amount_exceeds_balance)
data class AmountLowerExistentialDeposit(override val args: List<Any>) : TapError(R.string.send_error_minimum_balance_format)
object FeeExceedsBalance : TapError(R.string.send_validation_invalid_fee)
object TotalExceedsBalance : TapError(R.string.send_validation_invalid_total)
object InvalidAmountValue : TapError(R.string.send_validation_invalid_amount)
object InvalidFeeValue : TapError(R.string.send_error_invalid_fee_value)
data class DustAmount(override val args: List<Any>) : TapError(R.string.send_error_dust_amount_format)
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,

View file

@ -1,12 +1,8 @@
package com.tangem.tap.domain.configurable.config
import android.content.Context
import com.google.firebase.ktx.Firebase
import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.readAssetAsString
import com.tangem.tap.common.AssetReader
import com.tangem.tap.domain.configurable.Loader
import timber.log.Timber
@ -14,8 +10,8 @@ import timber.log.Timber
[REDACTED_AUTHOR]
*/
class FeaturesLocalLoader(
private val context: Context,
private val moshi: Moshi,
private val assetReader: AssetReader,
private val moshi: Moshi,
) : Loader<ConfigModel> {
override fun load(onComplete: (ConfigModel) -> Unit) {
@ -23,8 +19,8 @@ class FeaturesLocalLoader(
val featureAdapter: JsonAdapter<FeatureModel> = moshi.adapter(FeatureModel::class.java)
val valuesAdapter: JsonAdapter<ConfigValueModel> = moshi.adapter(ConfigValueModel::class.java)
val jsonFeatures = context.readAssetAsString(Loader.featuresName)
val jsonConfigValues = context.readAssetAsString(Loader.configValuesName)
val jsonFeatures = assetReader.readAssetAsString(Loader.featuresName)
val jsonConfigValues = assetReader.readAssetAsString(Loader.configValuesName)
ConfigModel(featureAdapter.fromJson(jsonFeatures), valuesAdapter.fromJson(jsonConfigValues))
} catch (ex: Exception) {
@ -33,31 +29,4 @@ class FeaturesLocalLoader(
}
onComplete(config)
}
}
class FeaturesRemoteLoader(
private val moshi: Moshi,
) : Loader<ConfigModel> {
override fun load(onComplete: (ConfigModel) -> Unit) {
val emptyConfig = ConfigModel.empty()
val remoteConfig = Firebase.remoteConfig
remoteConfig.fetchAndActivate().addOnCompleteListener {
if (it.isSuccessful) {
val config = remoteConfig.getValue(Loader.featuresName)
val jsonConfig = config.asString()
if (jsonConfig.isEmpty()) {
onComplete(emptyConfig)
return@addOnCompleteListener
}
val featureAdapter: JsonAdapter<FeatureModel> = moshi.adapter(FeatureModel::class.java)
onComplete(ConfigModel(featureAdapter.fromJson(jsonConfig), null))
} else {
onComplete(emptyConfig)
}
}.addOnFailureListener {
FirebaseAnalyticsHandler.logException("remote_config_error.features", it)
onComplete(emptyConfig)
}
}
}

View file

@ -24,26 +24,19 @@ data class Config(
val zendesk: ZendeskConfig? = null,
)
class ConfigManager(
private val localLoader: Loader<ConfigModel>,
private val remoteLoader: Loader<ConfigModel>
) {
class ConfigManager {
var config: Config = Config()
private set
private var defaultConfig = Config()
fun load(onComplete: ((config: Config) -> Unit)? = null) {
localLoader.load { configModel ->
fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)? = null) {
configLoader.load { configModel ->
setupFeature(configModel.features)
setupKey(configModel.configValues)
onComplete?.invoke(config)
}
// Uncomment to enable remote config
// remoteLoader.load { config ->
// setupFeature(config.features)
// }
}
fun turnOff(name: String) {

View file

@ -1,52 +0,0 @@
package com.tangem.tap.domain.configurable.warningMessage
import com.google.firebase.ktx.Firebase
import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.domain.configurable.Loader
/**
[REDACTED_AUTHOR]
*/
class RemoteWarningLoader(
private val moshi: Moshi,
) : Loader<List<WarningMessage>> {
override fun load(onComplete: (List<WarningMessage>) -> Unit) {
val emptyConfig = listOf<WarningMessage>()
val remoteConfig = Firebase.remoteConfig
remoteConfig.fetchAndActivate().addOnCompleteListener {
if (!it.isSuccessful) {
onComplete(emptyConfig)
return@addOnCompleteListener
}
val config = remoteConfig.getValue(Loader.warnings)
val jsonConfig = config.asString()
if (jsonConfig.isEmpty()) {
onComplete(emptyConfig)
return@addOnCompleteListener
}
val adapterType = Types.newParameterizedType(List::class.java, WarningMessage::class.java)
val warningsAdapter: JsonAdapter<List<WarningMessage>> = moshi.adapter(adapterType)
try {
val warnings = warningsAdapter.fromJson(jsonConfig) ?: listOf()
onComplete(warnings)
} catch (ex: Exception) {
handleError(ex)
onComplete(emptyConfig)
}
}.addOnFailureListener {
handleError(it)
onComplete(emptyConfig)
}
}
private fun handleError(ex: Exception) {
FirebaseAnalyticsHandler.logException("remote_config_error.warnings", ex)
}
}

View file

@ -1,35 +1,17 @@
package com.tangem.tap.domain.configurable.warningMessage
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.extensions.containsAny
import com.tangem.tap.common.extensions.removeBy
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
class WarningMessagesManager(
private val warningLoader: RemoteWarningLoader,
) {
class WarningMessagesManager {
private val warningsList: MutableList<WarningMessage> = mutableListOf()
fun load(onComplete: VoidCallback? = null) {
// exclude annoying remote debug warnings
if (BuildConfig.DEBUG) {
onComplete?.invoke()
return
}
warningLoader.load { remoteList ->
warningsList.clear()
warningsList.addAll(remoteList)
sortByPriority()
onComplete?.invoke()
}
}
fun addWarning(warning: WarningMessage) {
if (findWarning(warning) == null) {
warningsList.add(warning)
@ -37,7 +19,10 @@ class WarningMessagesManager(
}
}
fun getWarnings(location: WarningMessage.Location, forBlockchains: List<Blockchain> = emptyList()): List<WarningMessage> {
fun getWarnings(
location: WarningMessage.Location,
forBlockchains: List<Blockchain> = emptyList(),
): List<WarningMessage> {
return warningsList
.filter { !it.isHidden && it.location.contains(location) }
.filter {
@ -94,7 +79,7 @@ class WarningMessagesManager(
null,
R.string.alert_title,
R.string.alert_developer_card,
WarningMessage.Origin.Local
WarningMessage.Origin.Local,
)
fun alreadySignedHashesWarning(): WarningMessage = WarningMessage(
@ -106,7 +91,7 @@ class WarningMessagesManager(
null,
R.string.alert_title,
R.string.alert_card_signed_transactions,
WarningMessage.Origin.Local
WarningMessage.Origin.Local,
)
fun signedHashesMultiWalletWarning(): WarningMessage = WarningMessage(
@ -131,7 +116,7 @@ class WarningMessagesManager(
null,
R.string.warning_rate_app_title,
R.string.warning_rate_app_message,
WarningMessage.Origin.Local
WarningMessage.Origin.Local,
)
fun isAlreadySignedHashesWarning(warning: WarningMessage): Boolean {
@ -147,7 +132,7 @@ class WarningMessagesManager(
null,
R.string.warning_failed_to_verify_card_title,
R.string.warning_failed_to_verify_card_message,
WarningMessage.Origin.Local
WarningMessage.Origin.Local,
)
fun remainingSignaturesNotEnough(remainingSignatures: Int): WarningMessage = WarningMessage(
@ -160,7 +145,7 @@ class WarningMessagesManager(
titleResId = R.string.alert_title,
messageResId = R.string.warning_low_signatures_format,
origin = WarningMessage.Origin.Local,
messageFormatArg = remainingSignatures.toString()
messageFormatArg = remainingSignatures.toString(),
)
fun testCardWarning(): WarningMessage = WarningMessage(
@ -172,7 +157,7 @@ class WarningMessagesManager(
null,
R.string.alert_title,
R.string.warning_testnet_card_message,
WarningMessage.Origin.Local
WarningMessage.Origin.Local,
)
fun demoCardWarning(): WarningMessage = WarningMessage(
@ -184,7 +169,7 @@ class WarningMessagesManager(
null,
R.string.alert_title,
R.string.alert_demo_message,
WarningMessage.Origin.Local
WarningMessage.Origin.Local,
)
const val REMAINING_SIGNATURES_WARNING = 10

View file

@ -13,6 +13,7 @@ import com.tangem.common.extensions.toHexString
import com.tangem.domain.common.ScanResponse
import com.tangem.network.common.MoshiConverter
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.tap.common.AssetReader
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.tangemSdkManager
@ -119,10 +120,6 @@ class TwinCardsManager(
}
}
interface AssetReader {
fun readAssetAsString(name: String): String
}
private class Issuer(
val id: String,
val privateKey: String,

View file

@ -4,9 +4,9 @@ import com.tangem.Message
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.AssetReader
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import org.rekotlin.Action

View file

@ -15,10 +15,10 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.AndroidAssetReader
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.readAssetAsString
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.navigation.AppScreen
@ -26,7 +26,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.redux.navigation.ShareElement
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinsCardWidget
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
@ -46,13 +45,6 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
private lateinit var twinsWidget: TwinsCardWidget
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
private val assetReader: AssetReader by lazy {
object : AssetReader {
override fun readAssetAsString(name: String): String =
requireContext().readAssetAsString(name)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
@ -250,7 +242,7 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
store.dispatch(
TwinCardsAction.Wallet.LaunchFirstStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
assetReader
AndroidAssetReader(requireContext()),
)
)
}

View file

@ -120,6 +120,7 @@ private fun filterErrorsForAmountField(errors: EnumSet<TransactionError>): EnumS
TransactionError.TotalExceedsBalance -> {
val notAcceptable = listOf(TransactionError.AmountExceedsBalance, TransactionError.FeeExceedsBalance)
if (!showIntoAmountField.containsAll(notAcceptable)) showIntoAmountField.add(it)
showIntoAmountField.remove(TransactionError.AmountLowerExistentialDeposit)
}
else -> showIntoAmountField.add(it)
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.Amount
@ -301,12 +302,21 @@ fun createValidateTransactionError(
val tapErrors = errorList.map {
when (it) {
TransactionError.AmountExceedsBalance -> TapError.AmountExceedsBalance
TransactionError.AmountLowerExistentialDeposit -> {
if (walletManager is ExistentialDepositProvider) {
val args = listOf(walletManager.getExistentialDeposit().stripZeroPlainString())
TapError.AmountLowerExistentialDeposit(args)
} else {
TapError.UnknownError
}
}
TransactionError.FeeExceedsBalance -> TapError.FeeExceedsBalance
TransactionError.TotalExceedsBalance -> TapError.TotalExceedsBalance
TransactionError.InvalidAmountValue -> TapError.InvalidAmountValue
TransactionError.InvalidFeeValue -> TapError.InvalidFeeValue
TransactionError.DustAmount -> {
TapError.DustAmount(listOf(walletManager.dustValue?.stripZeroPlainString() ?: "0"))
val args = listOf(walletManager.dustValue?.stripZeroPlainString() ?: "0")
TapError.DustAmount(args)
}
TransactionError.DustChange -> TapError.DustChange
else -> TapError.UnknownError

View file

@ -218,7 +218,7 @@ class ReceiptReducer : SendInternalReducer {
}
private fun String.addPrecisionSign(): String {
val result = if (feeState.feeIsApproximate) "$CAN_BE_LOWER_SIGN $this" else ""
val result = if (feeState.feeIsApproximate) "$CAN_BE_LOWER_SIGN $this" else this
return result.trim()
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.common.module.ModuleMessageConverter
import com.tangem.tangem_sdk_new.extensions.localizedDescription
@ -47,9 +48,13 @@ private class BlockchainSdkErrorConverter(
override fun convert(message: BlockchainSdkError): String {
return when (message) {
is BlockchainSdkError.CreateAccountUnderfunded -> {
val reserve = message.minReserve.value?.stripZeroPlainString() ?: "0"
val symbol = message.minReserve.currencySymbol
context.getString(R.string.send_error_no_target_account, reserve, symbol)
val resStringId = when (message.blockchain) {
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.string.no_account_polkadot
else -> R.string.send_error_no_target_account
}
val reserveValueString = message.minReserve.value?.stripZeroPlainString() ?: "0"
val argument = "$reserveValueString ${message.minReserve.currencySymbol}"
context.getString(resStringId, argument)
}
else -> message.customMessage
}

View file

@ -81,7 +81,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value)
if (it.error != null) {
if (it.error == TransactionExtraError.INVALID_XLM_MEMO) {
tilXlmMemo.error = fg.getText(R.string.send_error_invalid_memo)
tilXlmMemo.error = fg.getText(R.string.send_extras_error_invalid_memo)
}
} else {
tilXlmMemo.error = null
@ -91,7 +91,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
if (infoState.xrpDestinationTag.error != null) {
if (infoState.xrpDestinationTag.error == TransactionExtraError.INVALID_DESTINATION_TAG) {
tilDestinationTag.error =
fg.getText(R.string.send_error_invalid_destination_tag)
fg.getText(R.string.send_extras_error_invalid_destination_tag)
}
} else {
tilDestinationTag.error = null
@ -103,7 +103,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
infoState.binanceMemo?.let {
if (infoState.binanceMemo.error != null) {
if (infoState.binanceMemo.error == TransactionExtraError.INVALID_BINANCE_MEMO) {
tilBinanceMemo.error = fg.getText(R.string.send_error_invalid_memo)
tilBinanceMemo.error = fg.getText(R.string.send_extras_error_invalid_memo)
}
} else {
tilBinanceMemo.error = null