Updated on 2026-08-14
This commit is contained in:
commit
886f0d66bc
54 changed files with 378 additions and 532 deletions
|
|
@ -4,7 +4,6 @@ import android.content.Intent
|
|||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
|
|
@ -26,7 +25,6 @@ import com.tangem.tap.common.shop.GooglePayService
|
|||
import com.tangem.tap.common.shop.GooglePayService.Companion.LOAD_PAYMENT_DATA_REQUEST_CODE
|
||||
import com.tangem.tap.common.shop.googlepay.GooglePayUtil.createPaymentsClient
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.shop.redux.ShopAction
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.ActivityMainBinding
|
||||
|
|
@ -67,7 +65,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
|
|||
backupService = BackupService.init(tangemSdk, this)
|
||||
|
||||
store.dispatch(GlobalAction.SetResources(getAndroidResources()))
|
||||
store.dispatch(WalletConnectAction.RestoreSessions)
|
||||
store.dispatch(
|
||||
ShopAction.CheckIfGooglePayAvailable(
|
||||
GooglePayService(createPaymentsClient(this), this)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
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.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
|
||||
|
|
@ -21,10 +21,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
|
||||
|
|
@ -37,7 +36,7 @@ import timber.log.Timber
|
|||
val store = Store(
|
||||
reducer = ::appReducer,
|
||||
middleware = AppState.getMiddleware(),
|
||||
state = AppState()
|
||||
state = AppState(),
|
||||
)
|
||||
val logConfig = LogConfig()
|
||||
|
||||
|
|
@ -49,101 +48,100 @@ 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 = BuildConfig.DEBUG
|
||||
|
||||
initAppsFlyer()
|
||||
}
|
||||
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return createCoilImageLoader(context = this)
|
||||
}
|
||||
|
||||
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,
|
||||
),
|
||||
),
|
||||
)
|
||||
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,
|
||||
)
|
||||
feedbackManager.initChat(
|
||||
context = context,
|
||||
zendeskConfig = config.zendesk!!,
|
||||
)
|
||||
store.dispatch(GlobalAction.SetFeedbackManager(feedbackManager))
|
||||
}
|
||||
|
||||
private fun initWarningMessagesManager() {
|
||||
store.dispatch(GlobalAction.SetWarningManager(WarningMessagesManager()))
|
||||
}
|
||||
}
|
||||
|
||||
data class LogConfig(
|
||||
|
|
|
|||
20
app/src/main/java/com/tangem/tap/common/AssetReader.kt
Normal file
20
app/src/main/java/com/tangem/tap/common/AssetReader.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -113,7 +113,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
data = state.dialog.data,
|
||||
session = state.dialog.session,
|
||||
sessionId = state.dialog.sessionId,
|
||||
cardId = state.dialog.cardId,
|
||||
dAppName = state.dialog.dAppName,
|
||||
context = context,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 ?: ""
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
|||
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -88,14 +89,16 @@ class TapWalletManager {
|
|||
|
||||
withMainContext {
|
||||
store.dispatch(WalletAction.ResetState(data.card.cardId))
|
||||
store.dispatch(WalletConnectAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
|
||||
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
|
||||
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
|
||||
store.dispatch(WalletConnectAction.RestoreSessions(data))
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.ShowWalletBackupWarning(
|
||||
show = data.card.settings.isBackupAllowed
|
||||
&& data.card.backupStatus == Card.BackupStatus.NoBackup
|
||||
)
|
||||
&& data.card.backupStatus == Card.BackupStatus.NoBackup,
|
||||
),
|
||||
)
|
||||
loadData(data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -69,7 +71,7 @@ class WalletConnectManager {
|
|||
remotePeerId = null,
|
||||
session = session,
|
||||
client = client,
|
||||
wallet = WalletForSession(cardId = "")
|
||||
wallet = WalletForSession(),
|
||||
)
|
||||
setupConnectionTimeoutCheck(session)
|
||||
}
|
||||
|
|
@ -110,8 +112,12 @@ class WalletConnectManager {
|
|||
}
|
||||
}
|
||||
|
||||
fun restoreSessions() {
|
||||
fun restoreSessions(scanResponse: ScanResponse) {
|
||||
val walletPublicKey = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }?.publicKey
|
||||
?: return
|
||||
val sessions = walletConnectRepository.loadSavedSessions()
|
||||
// filter sessions for this particular card
|
||||
.filter { it.wallet.walletPublicKey.contentEquals(walletPublicKey) }
|
||||
this.sessions = sessions
|
||||
.map { session ->
|
||||
WalletConnectActiveData(
|
||||
|
|
@ -126,8 +132,8 @@ class WalletConnectManager {
|
|||
setListeners(it.client)
|
||||
it.client.connect(it.session, tangemPeerMeta, it.peerId, it.remotePeerId)
|
||||
}
|
||||
}
|
||||
.map { it.session to it }.toMap().toMutableMap()
|
||||
}.associateBy { it.session }.toMutableMap()
|
||||
|
||||
store.dispatchOnMain(WalletConnectAction.SetSessionsRestored(sessions))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ class WalletConnectNetworkUtils {
|
|||
peer: WCPeerMeta,
|
||||
): Blockchain? {
|
||||
return when {
|
||||
peer.url.contains("pancakeswap.finance") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
chainId != null -> {
|
||||
Blockchain.fromChainId(chainId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ class WalletConnectSdkHelper {
|
|||
)
|
||||
)
|
||||
val dialogData = TransactionRequestDialogData(
|
||||
cardId = session.wallet.cardId,
|
||||
dAppName = session.peerMeta.name,
|
||||
dAppUrl = session.peerMeta.url,
|
||||
amount = value.toFormattedString(decimals),
|
||||
|
|
@ -235,7 +234,6 @@ class WalletConnectSdkHelper {
|
|||
|
||||
|
||||
val dialogData = PersonalSignDialogData(
|
||||
cardId = session.wallet.cardId,
|
||||
dAppName = session.peerMeta.name,
|
||||
message = messageString,
|
||||
session = session.session,
|
||||
|
|
@ -272,11 +270,11 @@ class WalletConnectSdkHelper {
|
|||
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? {
|
||||
val key = wallet.derivedPublicKey ?: wallet.walletPublicKey
|
||||
val command = SignHashCommand(hashToSign, wallet.walletPublicKey!!, wallet.derivationPath)
|
||||
return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) {
|
||||
return when (val result = tangemSdkManager.runTaskAsync(command)) {
|
||||
is CompletionResult.Success -> {
|
||||
val hash = result.data.signature
|
||||
return EthereumUtils.prepareSignedMessageData(
|
||||
hash, hashToSign, CryptoUtils.decompressPublicKey(key!!)
|
||||
hash, hashToSign, CryptoUtils.decompressPublicKey(key!!),
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.extensions.getPrimaryCurve
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
|
|
@ -17,51 +14,25 @@ import com.tangem.tap.features.wallet.redux.WalletState
|
|||
class WcWalletManagerFactory(
|
||||
private val factory: WalletManagerFactory,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
) {
|
||||
|
||||
suspend fun getWalletManager(
|
||||
wallet: WalletForSession, blockchain: Blockchain, walletState: WalletState
|
||||
) {
|
||||
fun getWalletManager(
|
||||
wallet: WalletForSession, blockchain: Blockchain, walletState: WalletState,
|
||||
): WalletManager? {
|
||||
val blockchainToMake = if (blockchain == Blockchain.Ethereum && wallet.isTestNet) {
|
||||
Blockchain.EthereumTestnet
|
||||
} else {
|
||||
blockchain
|
||||
}
|
||||
|
||||
val blockchainNetwork = BlockchainNetwork(
|
||||
blockchain = blockchainToMake,
|
||||
derivationPath = wallet.derivationPath?.rawPath,
|
||||
tokens = emptyList()
|
||||
tokens = emptyList(),
|
||||
)
|
||||
|
||||
return if (walletState.cardId == wallet.cardId) {
|
||||
walletState.getWalletManager(blockchainNetwork)
|
||||
} else {
|
||||
val blockchainNetworkWithTokens = currenciesRepository
|
||||
.loadSavedCurrencies(
|
||||
cardId = wallet.cardId,
|
||||
isHdWalletSupported = wallet.derivationPath != null
|
||||
).firstOrNull { it == blockchainNetwork }
|
||||
|
||||
if (blockchainNetworkWithTokens != null) {
|
||||
factory.makeWalletManager(
|
||||
blockchain = blockchainToMake,
|
||||
publicKey = Wallet.PublicKey(
|
||||
wallet.walletPublicKey!!,
|
||||
wallet.derivedPublicKey,
|
||||
wallet.derivationPath
|
||||
),
|
||||
tokens = blockchainNetworkWithTokens.tokens,
|
||||
curve = blockchainToMake.getPrimaryCurve() ?: EllipticCurve.Secp256k1
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
return walletState.getWalletManager(blockchainNetwork)
|
||||
}
|
||||
|
||||
suspend fun getWalletManager(
|
||||
scanResponse: ScanResponse, blockchain: Blockchain, walletState: WalletState
|
||||
scanResponse: ScanResponse, blockchain: Blockchain, walletState: WalletState,
|
||||
): WalletManager? {
|
||||
val card = scanResponse.card
|
||||
val blockchainToMake = if (blockchain == Blockchain.Ethereum && card.isTestCard) {
|
||||
|
|
@ -69,14 +40,13 @@ class WcWalletManagerFactory(
|
|||
} else {
|
||||
blockchain
|
||||
}
|
||||
|
||||
val blockchainNetwork = BlockchainNetwork(
|
||||
blockchain = blockchainToMake,
|
||||
card = card
|
||||
card = card,
|
||||
)
|
||||
|
||||
return if (walletState.cardId == card.cardId) {
|
||||
walletState.getWalletManager(blockchainNetwork)
|
||||
walletState.getWalletManager(blockchainNetwork)
|
||||
} else {
|
||||
if (currenciesRepository
|
||||
.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed)
|
||||
|
|
@ -84,7 +54,7 @@ class WcWalletManagerFactory(
|
|||
) {
|
||||
factory.makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchainNetwork = blockchainNetwork
|
||||
blockchainNetwork = blockchainNetwork,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -98,14 +98,16 @@ class DetailsMiddleware {
|
|||
fun handle(action: DetailsAction.ResetToFactory) {
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory.Start -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory))
|
||||
}
|
||||
is DetailsAction.ResetToFactory.Proceed -> {
|
||||
val card = store.state.detailsState.cardSettingsState?.card ?: return
|
||||
if (card.isTangemTwins()) {
|
||||
store.dispatch(DetailsAction.ReCreateTwinsWallet)
|
||||
return
|
||||
} else {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory))
|
||||
}
|
||||
}
|
||||
is DetailsAction.ResetToFactory.Proceed -> {
|
||||
val card = store.state.detailsState.cardSettingsState?.card ?: return
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.resetToFactorySettings(card)
|
||||
withContext(Dispatchers.Main) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.wallet.R
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
|
|
@ -11,11 +12,9 @@ import com.trustwallet.walletconnect.models.session.WCSession
|
|||
import org.rekotlin.Action
|
||||
|
||||
sealed class WalletConnectAction : Action {
|
||||
|
||||
object ResetState : WalletConnectAction()
|
||||
data class HandleDeepLink(val wcUri: String?) : WalletConnectAction()
|
||||
|
||||
object RestoreSessions : WalletConnectAction()
|
||||
|
||||
data class RestoreSessions(val scanResponse: ScanResponse) : WalletConnectAction()
|
||||
data class StartWalletConnect(
|
||||
val copiedUri: String?,
|
||||
) : WalletConnectAction()
|
||||
|
|
|
|||
|
|
@ -22,14 +22,13 @@ import com.tangem.tap.features.demo.DemoHelper
|
|||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
class WalletConnectMiddleware {
|
||||
private val walletConnectManager = WalletConnectManager()
|
||||
private var walletConnectManager = WalletConnectManager()
|
||||
val walletConnectMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
|
|
@ -43,8 +42,9 @@ class WalletConnectMiddleware {
|
|||
if (DemoHelper.tryHandle(state, action)) return
|
||||
|
||||
when (action) {
|
||||
is WalletConnectAction.ResetState -> walletConnectManager = WalletConnectManager()
|
||||
is WalletConnectAction.RestoreSessions -> {
|
||||
walletConnectManager.restoreSessions()
|
||||
walletConnectManager.restoreSessions(action.scanResponse)
|
||||
}
|
||||
is WalletConnectAction.HandleDeepLink -> {
|
||||
if (!action.wcUri.isNullOrBlank()) {
|
||||
|
|
@ -74,7 +74,13 @@ class WalletConnectMiddleware {
|
|||
is WalletConnectAction.ChooseNetwork -> {
|
||||
val data = state()?.walletConnectState?.newSessionData ?: return
|
||||
scope.launch {
|
||||
prepareWalletManager(data.scanResponse, store.state.walletState, action.blockchain, data.session)
|
||||
prepareWalletManager(
|
||||
scanResponse = data.scanResponse,
|
||||
walletState = store.state.walletState,
|
||||
blockchain = action.blockchain,
|
||||
session = data.session,
|
||||
walletConnectManager = walletConnectManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.ShowClipboardOrScanQrDialog -> {
|
||||
|
|
@ -110,7 +116,8 @@ class WalletConnectMiddleware {
|
|||
)
|
||||
}
|
||||
is WalletConnectAction.ScanCard -> {
|
||||
scanCard(action.session, action.chainId)
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
scanCard(scanResponse, action.session, action.chainId)
|
||||
}
|
||||
is WalletConnectAction.ApproveSession -> {
|
||||
walletConnectManager.approve(action.session)
|
||||
|
|
@ -150,7 +157,6 @@ class WalletConnectMiddleware {
|
|||
data = messageData,
|
||||
session = action.sessionData.session,
|
||||
sessionId = action.id,
|
||||
cardId = action.sessionData.wallet.cardId,
|
||||
dAppName = action.sessionData.peerMeta.name,
|
||||
),
|
||||
),
|
||||
|
|
@ -164,7 +170,6 @@ class WalletConnectMiddleware {
|
|||
data = messageData,
|
||||
session = action.sessionData.session,
|
||||
sessionId = action.id,
|
||||
cardId = action.sessionData.wallet.cardId,
|
||||
dAppName = action.sessionData.peerMeta.name,
|
||||
),
|
||||
),
|
||||
|
|
@ -189,28 +194,26 @@ class WalletConnectMiddleware {
|
|||
currenciesRepository = currenciesRepository,
|
||||
)
|
||||
val walletState = store.state.walletState
|
||||
scope.launch {
|
||||
val walletManager = factory.getWalletManager(
|
||||
wallet = action.session.wallet,
|
||||
blockchain = blockchain,
|
||||
walletState = walletState,
|
||||
).guard {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.AddNetwork(blockchain.fullName),
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
val updatedWallet = action.session.wallet.copy(
|
||||
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
||||
derivedPublicKey = walletManager.wallet.publicKey.derivedKey,
|
||||
derivationPath = walletManager.wallet.publicKey.derivationPath,
|
||||
blockchain = action.blockchain,
|
||||
val walletManager = factory.getWalletManager(
|
||||
wallet = action.session.wallet,
|
||||
blockchain = blockchain,
|
||||
walletState = walletState,
|
||||
).guard {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.AddNetwork(blockchain.fullName),
|
||||
),
|
||||
)
|
||||
val updatedSession = action.session.copy(wallet = updatedWallet)
|
||||
store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession))
|
||||
return
|
||||
}
|
||||
val updatedWallet = action.session.wallet.copy(
|
||||
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
||||
derivedPublicKey = walletManager.wallet.publicKey.derivedKey,
|
||||
derivationPath = walletManager.wallet.publicKey.derivationPath,
|
||||
blockchain = action.blockchain,
|
||||
)
|
||||
val updatedSession = action.session.copy(wallet = updatedWallet)
|
||||
store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession))
|
||||
}
|
||||
is WalletConnectAction.UpdateBlockchain -> {
|
||||
walletConnectManager.updateBlockchain(action.updatedSession)
|
||||
|
|
@ -218,24 +221,16 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun scanCard(session: WalletConnectSession, chainId: Int?) {
|
||||
private fun scanCard(scanResponse: ScanResponse, session: WalletConnectSession, chainId: Int?) {
|
||||
val blockchain = WalletConnectNetworkUtils.parseBlockchain(
|
||||
chainId = chainId,
|
||||
peer = session.peerMeta,
|
||||
) ?: Blockchain.Ethereum
|
||||
).guard {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork))
|
||||
return
|
||||
}
|
||||
|
||||
store.dispatch(
|
||||
GlobalAction.ScanCard(
|
||||
additionalBlockchainsToDerive = listOf(blockchain),
|
||||
onSuccess = { scanResponse ->
|
||||
handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain)
|
||||
},
|
||||
onFailure = {
|
||||
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
|
||||
},
|
||||
R.string.wallet_connect_scan_card_message,
|
||||
),
|
||||
)
|
||||
handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain)
|
||||
}
|
||||
|
||||
private suspend fun getAvailableBlockchains(card: Card, walletState: WalletState): List<Blockchain> {
|
||||
|
|
@ -259,6 +254,7 @@ class WalletConnectMiddleware {
|
|||
walletState: WalletState,
|
||||
blockchain: Blockchain,
|
||||
session: WalletConnectSession,
|
||||
walletConnectManager: WalletConnectManager,
|
||||
) {
|
||||
val factory = WcWalletManagerFactory(
|
||||
factory = store.state.globalState.tapWalletManager.walletManagerFactory,
|
||||
|
|
@ -273,21 +269,20 @@ class WalletConnectMiddleware {
|
|||
)
|
||||
return
|
||||
}
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey =
|
||||
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
null
|
||||
} else {
|
||||
walletManager.wallet.publicKey.blockchainKey
|
||||
}
|
||||
val walletForSession = WalletForSession(
|
||||
cardId = scanResponse.card.cardId,
|
||||
walletPublicKey = wallet.publicKey.seedKey,
|
||||
derivedPublicKey = derivedKey,
|
||||
derivationPath = wallet.publicKey.derivationPath,
|
||||
derivationStyle = scanResponse.card.derivationStyle,
|
||||
blockchain = wallet.blockchain,
|
||||
)
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey =
|
||||
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
null
|
||||
} else {
|
||||
walletManager.wallet.publicKey.blockchainKey
|
||||
}
|
||||
val walletForSession = WalletForSession(
|
||||
walletPublicKey = wallet.publicKey.seedKey,
|
||||
derivedPublicKey = derivedKey,
|
||||
derivationPath = wallet.publicKey.derivationPath,
|
||||
derivationStyle = scanResponse.card.derivationStyle,
|
||||
blockchain = wallet.blockchain,
|
||||
)
|
||||
|
||||
withMainContext {
|
||||
val updatedSession = session.copy(wallet = walletForSession)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ class WalletConnectReducer {
|
|||
if (action !is WalletConnectAction) return state
|
||||
|
||||
return when (action) {
|
||||
is WalletConnectAction.ResetState -> return WalletConnectState()
|
||||
is WalletConnectAction.ApproveSession.Success -> {
|
||||
state.copy(
|
||||
loading = false,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ data class WalletConnectSession(
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class WalletForSession(
|
||||
val cardId: String,
|
||||
val walletPublicKey: ByteArray? = null,
|
||||
val derivedPublicKey: ByteArray? = null,
|
||||
val derivationPath: DerivationPath? = null,
|
||||
|
|
@ -59,7 +58,6 @@ data class WalletForSession(
|
|||
|
||||
other as WalletForSession
|
||||
|
||||
if (cardId != other.cardId) return false
|
||||
if (walletPublicKey != null) {
|
||||
if (other.walletPublicKey == null) return false
|
||||
if (!walletPublicKey.contentEquals(other.walletPublicKey)) return false
|
||||
|
|
@ -76,8 +74,7 @@ data class WalletForSession(
|
|||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = cardId.hashCode()
|
||||
result = 31 * result + (walletPublicKey?.contentHashCode() ?: 0)
|
||||
var result = (walletPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (derivedPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (derivationPath?.hashCode() ?: 0)
|
||||
result = 31 * result + isTestNet.hashCode()
|
||||
|
|
@ -112,7 +109,6 @@ sealed class WalletConnectDialog : StateDialog {
|
|||
val data: BinanceMessageData,
|
||||
val session: WCSession,
|
||||
val sessionId: Long,
|
||||
val cardId: String,
|
||||
val dAppName: String,
|
||||
) : WalletConnectDialog()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
class WalletConnectSessionsAdapter
|
||||
: ListAdapter<WalletConnectSession, WalletConnectSessionsAdapter.SessionsViewHolder>(
|
||||
DiffUtilCallback
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SessionsViewHolder {
|
||||
val layout = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_wallet_connect_session, parent, false)
|
||||
return SessionsViewHolder(layout)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SessionsViewHolder, position: Int) {
|
||||
holder.bind(currentList[position])
|
||||
}
|
||||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<WalletConnectSession>() {
|
||||
override fun areContentsTheSame(
|
||||
oldItem: WalletConnectSession, newItem: WalletConnectSession,
|
||||
) = oldItem == newItem
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: WalletConnectSession, newItem: WalletConnectSession,
|
||||
) = oldItem == newItem
|
||||
}
|
||||
|
||||
class SessionsViewHolder(val view: View) :
|
||||
RecyclerView.ViewHolder(view) {
|
||||
|
||||
fun bind(session: WalletConnectSession) {
|
||||
view.findViewById<TextView>(R.id.tv_card_id).text = view.context.getString(
|
||||
R.string.wallet_connect_card_number, session.wallet.cardId
|
||||
)
|
||||
view.findViewById<TextView>(R.id.tv_d_app_name).text = session.peerMeta.name
|
||||
|
||||
view.findViewById<Chip>(R.id.btn_disconnect).setOnClickListener {
|
||||
store.dispatch(WalletConnectAction.DisconnectSession(session.session))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +169,6 @@ fun WalletConnectScreenPreview() {
|
|||
sessions = listOf(
|
||||
WcSessionForScreen(
|
||||
description = "session from some dApp",
|
||||
cardId = "12312312321",
|
||||
sessionId = "",
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -11,14 +11,12 @@ data class WalletConnectScreenState(
|
|||
|
||||
data class WcSessionForScreen(
|
||||
val description: String,
|
||||
val cardId: String,
|
||||
val sessionId: String,
|
||||
) {
|
||||
companion object {
|
||||
fun fromSession(session: WalletConnectSession): WcSessionForScreen {
|
||||
return WcSessionForScreen(
|
||||
description = session.peerMeta.name,
|
||||
cardId = session.wallet.cardId,
|
||||
sessionId = session.session.toUri(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ class ApproveWcSessionDialog {
|
|||
fun create(session: WalletConnectSession, networks: List<Blockchain>, context: Context): AlertDialog {
|
||||
val message = context.getString(
|
||||
R.string.wallet_connect_request_session_start,
|
||||
session.wallet.cardId,
|
||||
session.peerMeta.name,
|
||||
session.peerMeta.url,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class BnbTransactionDialog {
|
|||
data: BinanceMessageData,
|
||||
session: WCSession,
|
||||
sessionId: Long,
|
||||
cardId: String,
|
||||
dAppName: String,
|
||||
context: Context,
|
||||
): AlertDialog {
|
||||
|
|
@ -40,7 +39,7 @@ class BnbTransactionDialog {
|
|||
|
||||
val fullMessage = context.getString(
|
||||
R.string.wallet_connect_bnb_sign_message,
|
||||
dAppName, cardId, message
|
||||
dAppName, message,
|
||||
)
|
||||
val positiveButtonTitle = context.getText(R.string.common_sign)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,12 @@ class PersonalSignDialog {
|
|||
context: Context,
|
||||
): AlertDialog {
|
||||
val message =
|
||||
context.getString(R.string.wallet_connect_alert_sign_message, data.cardId) +
|
||||
context.getString(R.string.wallet_connect_personal_sign_message,
|
||||
data.dAppName,
|
||||
data.message)
|
||||
context.getString(R.string.wallet_connect_alert_sign_message) +
|
||||
context.getString(
|
||||
R.string.wallet_connect_personal_sign_message,
|
||||
data.dAppName,
|
||||
data.message,
|
||||
)
|
||||
return AlertDialog.Builder(context).apply {
|
||||
setTitle(context.getString(R.string.wallet_connect))
|
||||
setMessage(message)
|
||||
|
|
@ -37,7 +39,6 @@ class PersonalSignDialog {
|
|||
}
|
||||
|
||||
data class PersonalSignDialogData(
|
||||
val cardId: String,
|
||||
val dAppName: String,
|
||||
val message: String,
|
||||
val session: WCSession,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ class TransactionDialog {
|
|||
): AlertDialog {
|
||||
val message = context.getString(
|
||||
R.string.wallet_connect_create_tx_message,
|
||||
data.cardId,
|
||||
data.dAppName,
|
||||
data.dAppUrl,
|
||||
data.amount,
|
||||
|
|
@ -53,7 +52,6 @@ class TransactionDialog {
|
|||
}
|
||||
|
||||
data class TransactionRequestDialogData(
|
||||
val cardId: String,
|
||||
val dAppName: String,
|
||||
val dAppUrl: String,
|
||||
val amount: String,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ data class ButtonData(
|
|||
interface DialogMessageData
|
||||
|
||||
data class WcTransactionDialogMessageData(
|
||||
val cardId: String,
|
||||
val dAppName: String,
|
||||
val dAppUrl: String,
|
||||
val amount: String,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,19 @@ package com.tangem.tap.features.send.redux.reducers
|
|||
import com.tangem.blockchain.blockchains.stellar.StellarMemo
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.features.send.redux.SendScreenAction
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.*
|
||||
import com.tangem.tap.features.send.redux.states.*
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.BinanceMemo
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Prepare
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Release
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XlmMemo
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XrpDestinationTag
|
||||
import com.tangem.tap.features.send.redux.states.BinanceMemoState
|
||||
import com.tangem.tap.features.send.redux.states.InputViewValue
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtraError
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
|
||||
import com.tangem.tap.features.send.redux.states.XlmMemoState
|
||||
import com.tangem.tap.features.send.redux.states.XlmMemoType
|
||||
import com.tangem.tap.features.send.redux.states.XrpDestinationTagState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -53,42 +64,33 @@ class TransactionExtrasReducer : SendInternalReducer {
|
|||
}
|
||||
|
||||
private fun handleXlmMemo(
|
||||
action: XlmMemo,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
action: XlmMemo,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
): SendState {
|
||||
fun clearMemo(memo: XlmMemoState): XlmMemoState = memo.copy(text = null, id = null, error = null)
|
||||
|
||||
val result = when (action) {
|
||||
// is XlmMemo.ChangeSelectedMemo -> {
|
||||
// val inputViewValue = InputViewValue("", false)
|
||||
// val memo = infoState.xlmMemo?.copy(
|
||||
// viewFieldValue = inputViewValue,
|
||||
// selectedMemoType = action.memoType,
|
||||
// ) ?: XlmMemoState(inputViewValue, action.memoType)
|
||||
//
|
||||
// infoState.copy(xlmMemo = clearMemo(memo))
|
||||
// }
|
||||
is XlmMemo.HandleUserInput -> {
|
||||
val inputViewValue = InputViewValue(action.data, true)
|
||||
var memo = infoState.xlmMemo?.copy(viewFieldValue = inputViewValue)
|
||||
?: XlmMemoState(inputViewValue)
|
||||
?: XlmMemoState(inputViewValue)
|
||||
memo = clearMemo(memo)
|
||||
memo = when (infoState.xlmMemo?.selectedMemoType) {
|
||||
XlmMemoType.TEXT -> memo.copy(text = StellarMemo.Text(action.data))
|
||||
XlmMemoType.ID -> {
|
||||
val id = action.data.toBigIntegerOrNull()
|
||||
if (id != null) {
|
||||
if (id > XlmMemoState.MAX_NUMBER) {
|
||||
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
|
||||
} else {
|
||||
memo.copy(id = StellarMemo.Id(id))
|
||||
}
|
||||
memo = when (memo.selectedMemoType) {
|
||||
XlmMemoType.TEXT -> {
|
||||
if (XlmMemoState.isAssignableValue(action.data)) {
|
||||
memo.copy(text = StellarMemo.Text(action.data))
|
||||
} else {
|
||||
memo
|
||||
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
|
||||
}
|
||||
}
|
||||
XlmMemoType.ID -> {
|
||||
if (XlmMemoState.isAssignableValue(action.data)) {
|
||||
memo.copy(id = StellarMemo.Id(action.data.toBigInteger()))
|
||||
} else {
|
||||
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
|
||||
}
|
||||
}
|
||||
null -> memo
|
||||
}
|
||||
infoState.copy(xlmMemo = memo)
|
||||
}
|
||||
|
|
@ -117,16 +119,16 @@ class TransactionExtrasReducer : SendInternalReducer {
|
|||
}
|
||||
|
||||
private fun handleXrpTag(
|
||||
action: XrpDestinationTag,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
action: XrpDestinationTag,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
): SendState {
|
||||
val result = when (action) {
|
||||
is XrpDestinationTag.HandleUserInput -> {
|
||||
val tag = action.data.toLongOrNull()
|
||||
if (tag != null) {
|
||||
val input = InputViewValue(action.data, true)
|
||||
val tagState = if (tag <= XrpDestinationTagState.MAX_NUMBER){
|
||||
val tagState = if (tag <= XrpDestinationTagState.MAX_NUMBER) {
|
||||
XrpDestinationTagState(input, tag)
|
||||
} else {
|
||||
XrpDestinationTagState(input, error = TransactionExtraError.INVALID_DESTINATION_TAG)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
package com.tangem.tap.features.send.redux.states
|
||||
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarMemo
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
|
||||
import java.math.BigInteger
|
||||
|
||||
data class AddressPayIdState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val normalFieldValue: String? = null,
|
||||
val truncatedFieldValue: String? = null,
|
||||
val destinationWalletAddress: String? = null,
|
||||
val error: AddressPayIdVerifyAction.Error? = null,
|
||||
val truncateHandler: ((String) -> String)? = null,
|
||||
val sendingToPayIdEnabled: Boolean = false,
|
||||
val pasteIsEnabled: Boolean = false,
|
||||
val inputIsEnabled: Boolean = true
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val normalFieldValue: String? = null,
|
||||
val truncatedFieldValue: String? = null,
|
||||
val destinationWalletAddress: String? = null,
|
||||
val error: AddressPayIdVerifyAction.Error? = null,
|
||||
val truncateHandler: ((String) -> String)? = null,
|
||||
val sendingToPayIdEnabled: Boolean = false,
|
||||
val pasteIsEnabled: Boolean = false,
|
||||
val inputIsEnabled: Boolean = true,
|
||||
) : SendScreenState {
|
||||
|
||||
override val stateId: StateId = StateId.ADDRESS_PAY_ID
|
||||
|
|
@ -26,9 +27,9 @@ data class AddressPayIdState(
|
|||
}
|
||||
|
||||
data class TransactionExtrasState(
|
||||
val xlmMemo: XlmMemoState? = null,
|
||||
val binanceMemo: BinanceMemoState? = null,
|
||||
val xrpDestinationTag: XrpDestinationTagState? = null
|
||||
val xlmMemo: XlmMemoState? = null,
|
||||
val binanceMemo: BinanceMemoState? = null,
|
||||
val xrpDestinationTag: XrpDestinationTagState? = null,
|
||||
) : IdStateHolder {
|
||||
override val stateId: StateId = StateId.TRANSACTION_EXTRAS
|
||||
}
|
||||
|
|
@ -38,11 +39,10 @@ enum class XlmMemoType {
|
|||
}
|
||||
|
||||
data class XlmMemoState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val selectedMemoType: XlmMemoType = XlmMemoType.ID,
|
||||
val text: StellarMemo.Text? = null,
|
||||
val id: StellarMemo.Id? = null,
|
||||
val error: TransactionExtraError? = null,
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val text: StellarMemo.Text? = null,
|
||||
val id: StellarMemo.Id? = null,
|
||||
val error: TransactionExtraError? = null,
|
||||
) {
|
||||
val memo: StellarMemo?
|
||||
get() = when (selectedMemoType) {
|
||||
|
|
@ -50,16 +50,37 @@ data class XlmMemoState(
|
|||
XlmMemoType.ID -> id
|
||||
}
|
||||
|
||||
val selectedMemoType: XlmMemoType
|
||||
get() = determineMemoType(viewFieldValue.value)
|
||||
|
||||
companion object {
|
||||
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
|
||||
fun determineMemoType(value: String): XlmMemoType = when {
|
||||
value.isNotEmpty() && value.isDigitsOnly() -> XlmMemoType.ID
|
||||
else -> XlmMemoType.TEXT
|
||||
}
|
||||
|
||||
fun isAssignableValue(value: String): Boolean = when (determineMemoType(value)) {
|
||||
XlmMemoType.TEXT -> {
|
||||
// from org.stellar.sdk.MemoText
|
||||
value.toByteArray().size <= 28
|
||||
}
|
||||
XlmMemoType.ID -> {
|
||||
try {
|
||||
// from com.tangem.blockchain.blockchains.stellar.StellarMemo.toStellarSdkMemo
|
||||
value.toBigInteger() in BigInteger.ZERO..(Long.MAX_VALUE.toBigInteger() * 2.toBigInteger())
|
||||
} catch (ex: NumberFormatException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class BinanceMemoState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val memo: BigInteger? = null,
|
||||
val error: TransactionExtraError? = null
|
||||
) {
|
||||
val error: TransactionExtraError? = null,
|
||||
) {
|
||||
companion object {
|
||||
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
|
||||
}
|
||||
|
|
@ -67,9 +88,9 @@ data class BinanceMemoState(
|
|||
|
||||
// tag must contains only digits
|
||||
data class XrpDestinationTagState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val tag: Long? = null,
|
||||
val error: TransactionExtraError? = null
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val tag: Long? = null,
|
||||
val error: TransactionExtraError? = null,
|
||||
) {
|
||||
companion object {
|
||||
const val MAX_NUMBER: Long = 4294967295
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.features.send.ui.stateSubscribers
|
|||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.text.InputType
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
|
@ -31,7 +30,6 @@ import com.tangem.tap.features.send.redux.states.SendState
|
|||
import com.tangem.tap.features.send.redux.states.StateId
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtraError
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
|
||||
import com.tangem.tap.features.send.redux.states.XlmMemoType
|
||||
import com.tangem.tap.features.send.ui.FeeUiHelper
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
|
||||
|
|
@ -80,14 +78,10 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
showView(binanceMemoContainer, infoState.binanceMemo)
|
||||
|
||||
infoState.xlmMemo?.let {
|
||||
etXlmMemo.inputType = when (it.selectedMemoType) {
|
||||
XlmMemoType.TEXT -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||
XlmMemoType.ID -> InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
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_id)
|
||||
tilXlmMemo.error = fg.getText(R.string.send_extras_error_invalid_memo)
|
||||
}
|
||||
} else {
|
||||
tilXlmMemo.error = null
|
||||
|
|
@ -97,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
|
||||
|
|
@ -109,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_id)
|
||||
tilBinanceMemo.error = fg.getText(R.string.send_extras_error_invalid_memo)
|
||||
}
|
||||
} else {
|
||||
tilBinanceMemo.error = null
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:background="@color/backgroundLightGray"
|
||||
android:ellipsize="middle"
|
||||
android:inputType="text|textNoSuggestions"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:singleLine="true"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="send_error_invalid_fee_value">Falsche Gebühr</string>
|
||||
<string name="send_error_dust_amount_format">Minimaler Betrag ist %s</string>
|
||||
<string name="send_error_dust_change">Restbestand zu klein</string>
|
||||
<string name="send_error_no_target_account">Das Zielkonto ist nicht erstellt. Der abzusendende Betrag soll %s %s + Gebühr oder mehr sein</string>
|
||||
<string name="send_error_no_target_account">Das Zielkonto ist nicht erstellt. Der abzusendende Betrag soll %s + Gebühr oder mehr sein</string>
|
||||
<string name="send_error_no_account_xlm">Um ein Konto zu erstellen, senden Sie 1+ XLM an diese Adresse</string>
|
||||
<string name="send_error_fee_request_failed">Erhalt der Gebühr fehlgeschlagen</string>
|
||||
<string name="send_error_unknown">Unbekannter Fehler</string>
|
||||
|
|
@ -17,4 +17,11 @@
|
|||
<string name="send_validation_amount_exceeds_balance">Der Betrag geht über die Bilanz hinaus</string>
|
||||
<string name="send_validation_invalid_total">Der Gesamtbetrag geht über die Bilanz hinaus</string>
|
||||
<string name="send_validation_invalid_fee">Die Gebühr geht über die Bilanz hinaus</string>
|
||||
<string name="send_error_minimum_balance_format">Minimum balance is %s</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_error_invalid_memo">Invalid Memo. It won\'t be added to the transaction</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Invalid Tag. It won\'t be added to the transaction</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
<string name="no_account_polkadot">Destination account is not active. Send %s or more to activate the account.</string>
|
||||
</resources>
|
||||
|
|
@ -86,7 +86,5 @@
|
|||
<string name="card_settings_reset_card_to_factory">Reset to Factory Settings</string>
|
||||
<string name="card_settings_reset_card_to_factory_footer">Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code.</string>
|
||||
<string name="wallet_connect_select_network">Select network</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
|
||||
<string name="saltpay_backup_warning" translatable="false">To start working with the card scan the first card and make a backup</string>
|
||||
</resources>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="send_error_invalid_fee_value">Commission non valide</string>
|
||||
<string name="send_error_dust_amount_format">Le montant minimal est de %s</string>
|
||||
<string name="send_error_dust_change">Le reste est trop petit</string>
|
||||
<string name="send_error_no_target_account">Le compte cible n\'a pas été créé. Le montant à envoyer doit être de %s %s + commissions ou plus</string>
|
||||
<string name="send_error_no_target_account">Le compte cible n\'a pas été créé. Le montant à envoyer doit être de %s + commissions ou plus</string>
|
||||
<string name="send_error_no_account_xlm">Pour créer un compte, envoyez 1+ XLM à cette adresse</string>
|
||||
<string name="send_error_fee_request_failed">Échec de réception des commissions</string>
|
||||
<string name="send_error_unknown">Erreur inconnue</string>
|
||||
|
|
@ -17,4 +17,11 @@
|
|||
<string name="xtz_withdrawal_message_warning">Pour ne pas payer une commission élevée la prochaine fois que vous rechargez votre portefeuille, veuillez réduire le montant de %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_reduce">Réduire de %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_ignore">Non, envoyer toute la somme</string>
|
||||
<string name="send_error_minimum_balance_format">Minimum balance is %s</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_error_invalid_memo">Invalid Memo. It won\'t be added to the transaction</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Invalid Tag. It won\'t be added to the transaction</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
<string name="no_account_polkadot">Destination account is not active. Send %s or more to activate the account.</string>
|
||||
</resources>
|
||||
|
|
@ -86,7 +86,5 @@
|
|||
<string name="card_settings_reset_card_to_factory">Reset to Factory Settings</string>
|
||||
<string name="card_settings_reset_card_to_factory_footer">Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code.</string>
|
||||
<string name="wallet_connect_select_network">Select network</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
|
||||
<string name="saltpay_backup_warning" translatable="false">To start working with the card scan the first card and make a backup</string>
|
||||
</resources>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="send_error_dust_change">L\'importo residuo è molto basso</string>
|
||||
<string name="send_error_unknown">Errore sconosciuto</string>
|
||||
<string name="send_validation_amount_exceeds_balance">L\'importo supera il saldo</string>
|
||||
<string name="send_error_no_target_account">Per creare un account, invia %s %s a questo indirizzo</string>
|
||||
<string name="send_error_no_target_account">Per creare un account, invia %s a questo indirizzo</string>
|
||||
<string name="send_error_fee_request_failed">Impossibile ottenere la commissione</string>
|
||||
<string name="send_error_no_account_xlm">Per creare un account, invia 1+ XLM a questo indirizzo</string>
|
||||
<string name="send_validation_invalid_address">Indirizzo non valido</string>
|
||||
|
|
@ -17,4 +17,11 @@
|
|||
<string name="xtz_withdrawal_message_warning">Per evitare di pagare una commissione maggiore la prossima volta che ricarichi il tuo portafoglio, riduci l\'importo di %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_reduce">Riduci di %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_ignore">No, invia l\'intero importo</string>
|
||||
<string name="send_error_minimum_balance_format">Minimum balance is %s</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_error_invalid_memo">Invalid Memo. It won\'t be added to the transaction</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Invalid Tag. It won\'t be added to the transaction</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
<string name="no_account_polkadot">Destination account is not active. Send %s or more to activate the account.</string>
|
||||
</resources>
|
||||
|
|
@ -87,6 +87,5 @@
|
|||
<string name="card_settings_reset_card_to_factory_footer">Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code.</string>
|
||||
<string name="wallet_connect_select_network">Select network</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
|
||||
<string name="saltpay_backup_warning" translatable="false">To start working with the card scan the first card and make a backup</string>
|
||||
</resources>
|
||||
|
|
@ -147,10 +147,6 @@
|
|||
<string name="common_add">Добавить</string>
|
||||
<string name="common_remove">Удалить</string>
|
||||
<string name="common_search">Поиск</string>
|
||||
<string name="send_extras_hint_memo">Памятка</string>
|
||||
<string name="send_extras_hint_destination_tag">Тег назначения</string>
|
||||
<string name="send_error_invalid_destination_tag">Недопустимый тег назначения. Он не будет добавлен в транзакцию.</string>
|
||||
<string name="send_error_invalid_memo_id">Недопустимый идентификатор памятки. Он не будет добавлен в транзакцию.</string>
|
||||
<string name="details_section_title_app">Приложение</string>
|
||||
<string name="details_row_title_send_feedback">Отправить отзыв</string>
|
||||
<string name="alert_app_feedback_sent_title">Успешно отправлено</string>
|
||||
|
|
@ -328,8 +324,8 @@
|
|||
<string name="token_symbol_address_format">%1s (%2s)</string>
|
||||
<string name="alert_signed_hashes_message">Эта карта не является векселем на предъявителя. В настоящее время мы не можем сопоставить количество подписей на карте с информацией в блокчейне. Это нормально, но в редких случаях может означать, что предыдущий владелец скрывает автономную подпись, что является проблемой безопасности.\n\nНе принимайте эту карту в качестве физического платежа от кого-то, кому Вы не доверяете.\n\nВо всех остальных отношениях - это совершенно безопасно.\n\nTangem — единственный аппаратный кошелек, предлагающий защиту от подсчета подписей.</string>
|
||||
<string name="wallet_connect">WalletConnect</string>
|
||||
<string name="wallet_connect_create_tx_message">Карта %1s\nЗапрос на создание транзакции для %2s\n%3s\n\nСумма: %4s\nКомиссия: %5s\nВсего: %6s\nБаланс: %7s</string>
|
||||
<string name="wallet_connect_request_session_start">Запрос на запуск сеанса для карты с идентификатором %1s\nдля %2s\n\nURL: %3s</string>
|
||||
<string name="wallet_connect_create_tx_message">Запрос на создание транзакции для %1s\n%2s\n\nСумма: %3s\nКомиссия: %4s\nВсего: %5s\nБаланс: %6s</string>
|
||||
<string name="wallet_connect_request_session_start">Запрос на запуск сеанса для %1s\n\nURL: %2s</string>
|
||||
<string name="wallet_connect_sessions_title">Сессии WalletConnect</string>
|
||||
<string name="wallet_connect_session_opened">Сеанс WalletConnect открыт с %s</string>
|
||||
<string name="wallet_connect_no_sessions_title">Упс. Нет сессий.</string>
|
||||
|
|
@ -338,7 +334,7 @@
|
|||
<string name="wallet_connect_card_number">Карта: %s</string>
|
||||
<string name="wallet_connect_scan_card_message">Коснитесь карты, чтобы привязать ее WalletConnect</string>
|
||||
<string name="wallet_connect_create_tx_not_enough_funds">Не удается отправить транзакцию. Недостаточно средств.</string>
|
||||
<string name="wallet_connect_alert_sign_message">Просьба подписать сообщение\nкартой %s\n\n</string>
|
||||
<string name="wallet_connect_alert_sign_message">Просьба подписать сообщение\n</string>
|
||||
<string name="wallet_connect_personal_sign_message">Сообщение для %s:\n%s</string>
|
||||
<string name="wallet_connect_clipboard_alert">Буфер обмена содержит код WalletConnect. Использовать скопированное значение или отсканировать QR-код</string>
|
||||
<string name="wallet_connect_paste_from_clipboard">Вставить из буфера обмена</string>
|
||||
|
|
@ -348,7 +344,7 @@
|
|||
<string name="wallet_connect_same_wcuri">Операция не может быть завершена. \n\nВы уже установили сеанс WalletConnect с этими параметрами.</string>
|
||||
<string name="wallet_connect_error_timeout">Не удалось установить сеанс WalletConnect: ошибка времени выполнения. Пожалуйста, повторите попытку позже.</string>
|
||||
<string name="wallet_connect_bnb_transaction_signed">Транзакция BNB успешно подписана и отправлена в DApp</string>
|
||||
<string name="wallet_connect_bnb_sign_message">DApp %s, запрашивает\nподпись транзакции BNB с\nкартой: %s\n\n%s</string>
|
||||
<string name="wallet_connect_bnb_sign_message">DApp %s, запрашивает\nподпись транзакции BNB с\n%s</string>
|
||||
<string name="wallet_connect_bnb_transaction_message">Сведения о транзакции:\nОт: %s\nКому: %s\nСумма: %s</string>
|
||||
<string name="wallet_connect_bnb_trade_order_message">Торговый ордер на %s\nЦена: %s\nСумма к получению: %s\nСумма к оплате: %s</string>
|
||||
<string name="feedback_subject_rate_negative">Мои предложения</string>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="send_error_dust_change">Сдача слишком мала</string>
|
||||
<string name="send_error_no_account_xlm">Для создания учетной записи отправьте 1+ XLM на этот адрес</string>
|
||||
<string name="send_error_fee_request_failed">Не удалось получить комиссию</string>
|
||||
<string name="send_error_no_target_account">Целевая учетная запись не создана. Сумма для отправки должна быть %1$s %2$s + плата за создание или больше</string>
|
||||
<string name="send_error_no_target_account">Целевая учетная запись не создана. Сумма для отправки должна быть %s + плата за создание или больше</string>
|
||||
<string name="send_error_unknown">Неизвестная ошибка</string>
|
||||
<string name="send_validation_invalid_address">Неверный адрес</string>
|
||||
<string name="send_validation_invalid_amount">Недопустимая сумма</string>
|
||||
|
|
@ -17,4 +17,6 @@
|
|||
<string name="xtz_withdrawal_message_warning">Чтобы не платить повышенную комиссию при следующем пополнении кошелька, уменьшите сумму на %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_reduce">Уменьшить на %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_ignore">Нет, отправить все</string>
|
||||
<string name="send_error_minimum_balance_format">Минимальный баланс: %s</string>
|
||||
<string name="no_account_polkadot">Аккаунт получателя не активирован. Отправьте %s или более для активации аккаунта.</string>
|
||||
</resources>
|
||||
|
|
@ -97,6 +97,10 @@
|
|||
<string name="card_settings_reset_card_to_factory">Сброс к заводским настройкам</string>
|
||||
<string name="card_settings_reset_card_to_factory_footer">Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек или использовать эту карту для восстановления кода доступа.</string>
|
||||
<string name="wallet_connect_select_network">Выберите сеть</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_error_invalid_memo">Недопустимый Memo. Он не будет добавлен в транзакцию.</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Недопустимый Tag. Он не будет добавлен в транзакцию.</string>
|
||||
<string name="warning_existential_deposit_message">Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s %s, он будет деактивирован, а все оставшиеся средства будут уничтожены.</string>
|
||||
|
||||
<string name="saltpay_backup_warning" translatable="false">Для начала работы с картой сначала отсканируйте первую карту и сделайте бэкап</string>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="send_error_dust_change">Change is too small</string>
|
||||
<string name="send_error_no_account_xlm">To create account send 1+ XLM to this address</string>
|
||||
<string name="send_error_fee_request_failed">Failed to get fee</string>
|
||||
<string name="send_error_no_target_account">Target account is not created. Amount to send should be %s %s + fee or more to create</string>
|
||||
<string name="send_error_no_target_account">Target account is not created. Amount to send should be %s + fee or more to create</string>
|
||||
<string name="send_error_unknown">Unknown error</string>
|
||||
<string name="send_validation_invalid_address">Invalid address</string>
|
||||
<string name="send_validation_invalid_amount">Invalid amount</string>
|
||||
|
|
@ -17,4 +17,11 @@
|
|||
<string name="xtz_withdrawal_message_warning">To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_reduce">Reduce by %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_ignore">No, send all</string>
|
||||
<string name="send_error_minimum_balance_format">Minimum balance is %s</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_error_invalid_memo">Invalid Memo. It won\'t be added to the transaction</string>
|
||||
<string name="send_extras_error_invalid_destination_tag">Invalid Tag. It won\'t be added to the transaction</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
<string name="no_account_polkadot">Destination account is not active. Send %s or more to activate the account.</string>
|
||||
</resources>
|
||||
|
|
@ -99,7 +99,5 @@
|
|||
<string name="card_settings_reset_card_to_factory">Reset to Factory Settings</string>
|
||||
<string name="card_settings_reset_card_to_factory_footer">Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code.</string>
|
||||
<string name="wallet_connect_select_network">Select network</string>
|
||||
<string name="warning_existential_deposit_message">%s network has a concept of Existential Deposit. If your account drops below %s %s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
|
||||
<string name="saltpay_backup_warning" translatable="false">To start working with the card scan the first card and make a backup</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -40,12 +40,6 @@
|
|||
<string name="common_remove" translatable="false">Remove</string>
|
||||
<string name="common_search" translatable="false">Search</string>
|
||||
|
||||
|
||||
<string name="send_extras_hint_memo" translatable="false">Memo</string>
|
||||
<string name="send_extras_hint_destination_tag" translatable="false">Destination tag</string>
|
||||
<string name="send_error_invalid_destination_tag" translatable="false">Invalid destination tag. It won\'t be added to the transaction</string>
|
||||
<string name="send_error_invalid_memo_id" translatable="false">Invalid Memo ID. It won\'t be added to the transaction</string>
|
||||
|
||||
<string name="details_section_title_app" translatable="false">App</string>
|
||||
<string name="details_row_title_send_feedback" translatable="false">Send feedback</string>
|
||||
<string name="alert_app_feedback_sent_title" translatable="false">Sent successfully</string>
|
||||
|
|
@ -140,24 +134,23 @@
|
|||
<string name="wallet_connect_card_number" translatable="false">Card: %s</string>
|
||||
<string name="wallet_connect_scan_card_message" translatable="false">Tap card to bind to wallet connect</string>
|
||||
|
||||
<string name="wallet_connect_create_tx_message" translatable="false">Card: %1s\n
|
||||
Request to create transaction for %2s
|
||||
\n%3s
|
||||
<string name="wallet_connect_create_tx_message" translatable="false">
|
||||
Request to create transaction for %1s
|
||||
\n%2s
|
||||
|
||||
\n\nAmount: %4s
|
||||
\nFee: %5s
|
||||
\nTotal: %6s
|
||||
\nBalance: %7s</string>
|
||||
\n\nAmount: %3s
|
||||
\nFee: %4s
|
||||
\nTotal: %5s
|
||||
\nBalance: %6s</string>
|
||||
|
||||
<string name="wallet_connect_create_tx_not_enough_funds" translatable="false">Can\'t send transaction. Not enough funds.</string>
|
||||
|
||||
<string name="wallet_connect_request_session_start" translatable="false">Request to start a session for card with ID %1s\n
|
||||
for %2s\n\n
|
||||
<string name="wallet_connect_request_session_start" translatable="false">Request to start a session for %1s\n\n
|
||||
|
||||
URL: %3s</string>
|
||||
URL: %2s</string>
|
||||
|
||||
|
||||
<string name="wallet_connect_alert_sign_message" translatable="false">Requesting to sign a message\nwith card %s\n\n</string>
|
||||
<string name="wallet_connect_alert_sign_message" translatable="false">Requesting to sign a message\n</string>
|
||||
<string name="wallet_connect_personal_sign_message" translatable="false">Message for %s:\n%s</string>
|
||||
|
||||
|
||||
|
|
@ -305,8 +298,7 @@
|
|||
|
||||
<string name="wallet_connect_bnb_transaction_signed" translatable="false">The BNB transaction has been successfully signed and sent to the Dapp</string>
|
||||
<string name="wallet_connect_bnb_sign_message" translatable="false">Dapp %s, requesting to\n
|
||||
sign BNB transaction with\n
|
||||
Card: %s\n
|
||||
sign BNB transaction
|
||||
\n
|
||||
%s</string>
|
||||
<string name="wallet_connect_bnb_transaction_message" translatable="false">Transaction details:\n
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue