mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 21:55:53 +00:00
Fix Polkadot staking, add USDT bridge, update dashboard card
- Restore staking module to Nova upstream (fix Polkadot ACTIVE/shimmer) - Add USDT(DOT) <-> USDT(HEZ) bridge option to Buy/Sell screen - Dashboard card: add telegram_welcome image, info text, Non-Citizen role - Add non-citizen info text to all 12 locale files - Simplify ComputationalCache (remove stale scope recreation)
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ class PezkuwiDashboardRepository(
|
||||
val trustScore = queryTrustScore(chainId, accountId)
|
||||
|
||||
return PezkuwiDashboardData(
|
||||
roles = roles,
|
||||
roles = roles.ifEmpty { listOf("Non-Citizen") },
|
||||
trustScore = trustScore
|
||||
)
|
||||
}
|
||||
|
||||
+12
-1
@@ -75,7 +75,8 @@ class RealBuySellSelectorMixin(
|
||||
private suspend fun openAllAssetsSelector() = BuySellSelectorMixin.SelectorPayload(
|
||||
buyItem(enabled = true) { router.openBuyFlow() },
|
||||
sellItem(enabled = buySellRestrictionCheckMixin.isAllowed()) { router.openSellFlow() },
|
||||
bridgeItem(enabled = true) { router.openBridgeFlow() }
|
||||
bridgeItem(enabled = true) { router.openBridgeFlow() },
|
||||
bridgeUsdtItem(enabled = true) { router.openBridgeFlow() }
|
||||
)
|
||||
|
||||
private suspend fun openSpecifiedAssetSelector(selectorType: SelectorType.Asset): BuySellSelectorMixin.SelectorPayload? {
|
||||
@@ -125,6 +126,16 @@ class RealBuySellSelectorMixin(
|
||||
)
|
||||
}
|
||||
|
||||
private fun bridgeUsdtItem(enabled: Boolean, action: () -> Unit): ListSelectorMixin.Item {
|
||||
return ListSelectorMixin.Item(
|
||||
R.drawable.ic_bridge,
|
||||
if (enabled) R.color.icon_primary else R.color.icon_inactive,
|
||||
R.string.wallet_asset_bridge_usdt,
|
||||
if (enabled) R.color.text_primary else R.color.button_text_inactive,
|
||||
action
|
||||
)
|
||||
}
|
||||
|
||||
private fun sellErrorAction(): () -> Unit = {
|
||||
coroutineScope.launch {
|
||||
buySellRestrictionCheckMixin.checkRestrictionAndDo {
|
||||
|
||||
+78
-23
@@ -18,13 +18,22 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
|
||||
override fun initViews() {
|
||||
binder.bridgeToolbar.setHomeButtonListener { viewModel.backClicked() }
|
||||
|
||||
// Direction toggle
|
||||
binder.bridgeDirectionDotToHez.setOnClickListener {
|
||||
viewModel.setDirection(BridgeDirection.DOT_TO_HEZ)
|
||||
// Pair selector
|
||||
binder.bridgePairDotHez.setOnClickListener {
|
||||
viewModel.setPair(BridgePair.DOT_HEZ)
|
||||
}
|
||||
|
||||
binder.bridgeDirectionHezToDot.setOnClickListener {
|
||||
viewModel.setDirection(BridgeDirection.HEZ_TO_DOT)
|
||||
binder.bridgePairUsdt.setOnClickListener {
|
||||
viewModel.setPair(BridgePair.USDT)
|
||||
}
|
||||
|
||||
// Direction toggle
|
||||
binder.bridgeDirectionLeft.setOnClickListener {
|
||||
viewModel.setDirectionLeft()
|
||||
}
|
||||
|
||||
binder.bridgeDirectionRight.setOnClickListener {
|
||||
viewModel.setDirectionRight()
|
||||
}
|
||||
|
||||
// Amount input
|
||||
@@ -54,6 +63,10 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
|
||||
}
|
||||
|
||||
override fun subscribe(viewModel: BridgeViewModel) {
|
||||
viewModel.pair.observe { pair ->
|
||||
updatePairUI(pair)
|
||||
}
|
||||
|
||||
viewModel.direction.observe { direction ->
|
||||
updateDirectionUI(direction)
|
||||
}
|
||||
@@ -74,11 +87,11 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
|
||||
binder.bridgeSwapButton.setState(state)
|
||||
}
|
||||
|
||||
viewModel.showHezToDotWarning.observe { show ->
|
||||
viewModel.showWarning.observe { show ->
|
||||
binder.bridgeHezToDotWarning.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
viewModel.hezToDotBlocked.observe { blocked ->
|
||||
viewModel.warningBlocked.observe { blocked ->
|
||||
if (blocked) {
|
||||
binder.bridgeHezToDotWarning.setBackgroundColor(resources.getColor(R.color.error_block_background, null))
|
||||
} else {
|
||||
@@ -86,40 +99,82 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.blockReason.observe { reason ->
|
||||
if (reason.isNotEmpty()) {
|
||||
binder.bridgeHezToDotWarning.text = reason
|
||||
} else {
|
||||
binder.bridgeHezToDotWarning.text = getString(R.string.bridge_hez_to_dot_warning)
|
||||
viewModel.warningText.observe { text ->
|
||||
if (text.isNotEmpty()) {
|
||||
binder.bridgeHezToDotWarning.text = text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePairUI(pair: BridgePair) {
|
||||
when (pair) {
|
||||
BridgePair.DOT_HEZ -> {
|
||||
binder.bridgePairDotHez.setBackgroundResource(R.drawable.bg_button_primary)
|
||||
binder.bridgePairDotHez.setTextColor(resources.getColor(R.color.text_primary, null))
|
||||
binder.bridgePairUsdt.background = null
|
||||
binder.bridgePairUsdt.setTextColor(resources.getColor(R.color.text_secondary, null))
|
||||
}
|
||||
BridgePair.USDT -> {
|
||||
binder.bridgePairUsdt.setBackgroundResource(R.drawable.bg_button_primary)
|
||||
binder.bridgePairUsdt.setTextColor(resources.getColor(R.color.text_primary, null))
|
||||
binder.bridgePairDotHez.background = null
|
||||
binder.bridgePairDotHez.setTextColor(resources.getColor(R.color.text_secondary, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDirectionUI(direction: BridgeDirection) {
|
||||
val isLeft = direction == BridgeDirection.DOT_TO_HEZ || direction == BridgeDirection.USDT_TO_WUSDT
|
||||
|
||||
if (isLeft) {
|
||||
binder.bridgeDirectionLeft.setBackgroundResource(R.drawable.bg_button_primary)
|
||||
binder.bridgeDirectionLeft.setTextColor(resources.getColor(R.color.text_primary, null))
|
||||
binder.bridgeDirectionRight.background = null
|
||||
binder.bridgeDirectionRight.setTextColor(resources.getColor(R.color.text_secondary, null))
|
||||
} else {
|
||||
binder.bridgeDirectionRight.setBackgroundResource(R.drawable.bg_button_primary)
|
||||
binder.bridgeDirectionRight.setTextColor(resources.getColor(R.color.text_primary, null))
|
||||
binder.bridgeDirectionLeft.background = null
|
||||
binder.bridgeDirectionLeft.setTextColor(resources.getColor(R.color.text_secondary, null))
|
||||
}
|
||||
|
||||
when (direction) {
|
||||
BridgeDirection.DOT_TO_HEZ -> {
|
||||
binder.bridgeDirectionDotToHez.setBackgroundResource(R.drawable.bg_button_primary)
|
||||
binder.bridgeDirectionDotToHez.setTextColor(resources.getColor(R.color.text_primary, null))
|
||||
binder.bridgeDirectionHezToDot.background = null
|
||||
binder.bridgeDirectionHezToDot.setTextColor(resources.getColor(R.color.text_secondary, null))
|
||||
|
||||
binder.bridgeDirectionLeft.text = "DOT → HEZ"
|
||||
binder.bridgeDirectionRight.text = "HEZ → DOT"
|
||||
binder.bridgeFromToken.text = "DOT"
|
||||
binder.bridgeToToken.text = "HEZ"
|
||||
}
|
||||
BridgeDirection.HEZ_TO_DOT -> {
|
||||
binder.bridgeDirectionHezToDot.setBackgroundResource(R.drawable.bg_button_primary)
|
||||
binder.bridgeDirectionHezToDot.setTextColor(resources.getColor(R.color.text_primary, null))
|
||||
binder.bridgeDirectionDotToHez.background = null
|
||||
binder.bridgeDirectionDotToHez.setTextColor(resources.getColor(R.color.text_secondary, null))
|
||||
|
||||
binder.bridgeDirectionLeft.text = "DOT → HEZ"
|
||||
binder.bridgeDirectionRight.text = "HEZ → DOT"
|
||||
binder.bridgeFromToken.text = "HEZ"
|
||||
binder.bridgeToToken.text = "DOT"
|
||||
}
|
||||
BridgeDirection.USDT_TO_WUSDT -> {
|
||||
binder.bridgeDirectionLeft.text = "USDT(Pol) → USDT(Pez)"
|
||||
binder.bridgeDirectionRight.text = "USDT(Pez) → USDT(Pol)"
|
||||
binder.bridgeFromToken.text = "USDT"
|
||||
binder.bridgeToToken.text = "USDT"
|
||||
}
|
||||
BridgeDirection.WUSDT_TO_USDT -> {
|
||||
binder.bridgeDirectionLeft.text = "USDT(Pol) → USDT(Pez)"
|
||||
binder.bridgeDirectionRight.text = "USDT(Pez) → USDT(Pol)"
|
||||
binder.bridgeFromToken.text = "USDT"
|
||||
binder.bridgeToToken.text = "USDT"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class BridgePair {
|
||||
DOT_HEZ,
|
||||
USDT
|
||||
}
|
||||
|
||||
enum class BridgeDirection {
|
||||
DOT_TO_HEZ,
|
||||
HEZ_TO_DOT
|
||||
HEZ_TO_DOT,
|
||||
USDT_TO_WUSDT,
|
||||
WUSDT_TO_USDT
|
||||
}
|
||||
|
||||
+166
-129
@@ -28,34 +28,30 @@ class BridgeViewModel(
|
||||
) : BaseViewModel() {
|
||||
|
||||
companion object {
|
||||
// Bridge wallet account ID (derived from seed, same on all chains)
|
||||
// Address: 5C5CW7xDmiXtCgfUCbKFF4ViJuCJJQpDZqWQ1mSTjehGzE3p (generic format)
|
||||
private const val BRIDGE_ADDRESS_GENERIC = "5C5CW7xDmiXtCgfUCbKFF4ViJuCJJQpDZqWQ1mSTjehGzE3p"
|
||||
|
||||
// Chain IDs
|
||||
val POLKADOT_ASSET_HUB_ID = ChainGeneses.POLKADOT_ASSET_HUB
|
||||
val PEZKUWI_ASSET_HUB_ID = ChainGeneses.PEZKUWI_ASSET_HUB
|
||||
|
||||
// Utility asset ID (native token)
|
||||
const val UTILITY_ASSET_ID = 0
|
||||
|
||||
// Fallback rate: 1 DOT = 3 HEZ (only if CoinGecko unavailable)
|
||||
// USDT asset IDs in chain config
|
||||
const val POLKADOT_USDT_ASSET_ID = 1 // assetId in chains.json for Polkadot AH
|
||||
const val PEZKUWI_USDT_ASSET_ID = 1000 // assetId in chains.json for Pezkuwi AH
|
||||
|
||||
const val FALLBACK_RATE = 3.0
|
||||
|
||||
// Fee: 0.1%
|
||||
const val FEE_PERCENT = 0.001
|
||||
|
||||
// Minimums
|
||||
const val MIN_DOT = 0.1
|
||||
const val MIN_HEZ = 0.3
|
||||
const val MIN_USDT = 1.0
|
||||
|
||||
// CoinGecko API
|
||||
const val COINGECKO_API = "https://api.coingecko.com/api/v3/simple/price?ids=polkadot,hezkurd&vs_currencies=usd"
|
||||
|
||||
// Bridge Status API
|
||||
const val BRIDGE_STATUS_API = "http://217.77.6.126:3030/status"
|
||||
}
|
||||
|
||||
private val _pair = MutableLiveData(BridgePair.DOT_HEZ)
|
||||
val pair: LiveData<BridgePair> = _pair
|
||||
|
||||
private val _direction = MutableLiveData(BridgeDirection.DOT_TO_HEZ)
|
||||
val direction: LiveData<BridgeDirection> = _direction
|
||||
|
||||
@@ -71,122 +67,64 @@ class BridgeViewModel(
|
||||
private val _buttonState = MutableLiveData<ButtonState>()
|
||||
val buttonState: LiveData<ButtonState> = _buttonState
|
||||
|
||||
private val _showHezToDotWarning = MutableLiveData(false)
|
||||
val showHezToDotWarning: LiveData<Boolean> = _showHezToDotWarning
|
||||
private val _showWarning = MutableLiveData(false)
|
||||
val showWarning: LiveData<Boolean> = _showWarning
|
||||
|
||||
private val _hezToDotBlocked = MutableLiveData(false)
|
||||
val hezToDotBlocked: LiveData<Boolean> = _hezToDotBlocked
|
||||
private val _warningBlocked = MutableLiveData(false)
|
||||
val warningBlocked: LiveData<Boolean> = _warningBlocked
|
||||
|
||||
private val _blockReason = MutableLiveData<String>()
|
||||
val blockReason: LiveData<String> = _blockReason
|
||||
|
||||
private val _rateSource = MutableLiveData<String>()
|
||||
val rateSource: LiveData<String> = _rateSource
|
||||
private val _warningText = MutableLiveData<String>()
|
||||
val warningText: LiveData<String> = _warningText
|
||||
|
||||
private var currentAmount: Double = 0.0
|
||||
private var dotToHezRate: Double = FALLBACK_RATE
|
||||
private var isUsingFallback: Boolean = true
|
||||
private var isHezToDotActive: Boolean = false
|
||||
private var isWusdtToUsdtActive: Boolean = false
|
||||
|
||||
init {
|
||||
fetchExchangeRate()
|
||||
fetchBridgeStatus()
|
||||
}
|
||||
|
||||
private fun fetchExchangeRate() {
|
||||
launch {
|
||||
try {
|
||||
val (rate, source) = withContext(Dispatchers.IO) {
|
||||
fetchRateFromCoinGecko()
|
||||
}
|
||||
dotToHezRate = rate
|
||||
isUsingFallback = source == "fallback"
|
||||
_rateSource.postValue(source)
|
||||
updateUI()
|
||||
calculateOutput()
|
||||
} catch (e: Exception) {
|
||||
// Use fallback
|
||||
dotToHezRate = FALLBACK_RATE
|
||||
isUsingFallback = true
|
||||
_rateSource.postValue("fallback")
|
||||
updateUI()
|
||||
fun setPair(newPair: BridgePair) {
|
||||
if (_pair.value != newPair) {
|
||||
_pair.value = newPair
|
||||
// Reset direction to left (forward) when switching pair
|
||||
_direction.value = when (newPair) {
|
||||
BridgePair.DOT_HEZ -> BridgeDirection.DOT_TO_HEZ
|
||||
BridgePair.USDT -> BridgeDirection.USDT_TO_WUSDT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchRateFromCoinGecko(): Pair<Double, String> {
|
||||
return try {
|
||||
val response = URL(COINGECKO_API).readText()
|
||||
val json = JSONObject(response)
|
||||
|
||||
val dotPrice = json.optJSONObject("polkadot")?.optDouble("usd", 0.0) ?: 0.0
|
||||
val hezPrice = json.optJSONObject("hezkurd")?.optDouble("usd", 0.0) ?: 0.0
|
||||
|
||||
when {
|
||||
dotPrice > 0 && hezPrice > 0 -> {
|
||||
// Both prices available - calculate real rate
|
||||
val rate = dotPrice / hezPrice
|
||||
Pair(rate, "coingecko")
|
||||
}
|
||||
dotPrice > 0 -> {
|
||||
// Only DOT price - use fallback for HEZ (1 DOT = 3 HEZ means HEZ = DOT/3)
|
||||
Pair(FALLBACK_RATE, "coingecko+fallback")
|
||||
}
|
||||
else -> {
|
||||
// No prices - use pure fallback
|
||||
Pair(FALLBACK_RATE, "fallback")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Pair(FALLBACK_RATE, "fallback")
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchBridgeStatus() {
|
||||
launch {
|
||||
try {
|
||||
val active = withContext(Dispatchers.IO) {
|
||||
fetchHezToDotStatus()
|
||||
}
|
||||
isHezToDotActive = active
|
||||
updateHezToDotState()
|
||||
} catch (e: Exception) {
|
||||
// If API unavailable, assume not active for safety
|
||||
isHezToDotActive = false
|
||||
updateHezToDotState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchHezToDotStatus(): Boolean {
|
||||
return try {
|
||||
val response = URL(BRIDGE_STATUS_API).readText()
|
||||
val json = JSONObject(response)
|
||||
json.optBoolean("hezToDotActive", false)
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateHezToDotState() {
|
||||
val dir = _direction.value ?: return
|
||||
if (dir == BridgeDirection.HEZ_TO_DOT && !isHezToDotActive) {
|
||||
_hezToDotBlocked.postValue(true)
|
||||
_blockReason.postValue(resourceManager.getString(R.string.bridge_hez_to_dot_blocked))
|
||||
} else {
|
||||
_hezToDotBlocked.postValue(false)
|
||||
_blockReason.postValue("")
|
||||
}
|
||||
updateButtonState()
|
||||
}
|
||||
|
||||
fun setDirection(newDirection: BridgeDirection) {
|
||||
if (_direction.value != newDirection) {
|
||||
_direction.value = newDirection
|
||||
_showHezToDotWarning.value = newDirection == BridgeDirection.HEZ_TO_DOT
|
||||
updateHezToDotState()
|
||||
updateUI()
|
||||
calculateOutput()
|
||||
updateWarningState()
|
||||
}
|
||||
}
|
||||
|
||||
fun setDirectionLeft() {
|
||||
val newDir = when (_pair.value) {
|
||||
BridgePair.DOT_HEZ -> BridgeDirection.DOT_TO_HEZ
|
||||
BridgePair.USDT -> BridgeDirection.USDT_TO_WUSDT
|
||||
null -> BridgeDirection.DOT_TO_HEZ
|
||||
}
|
||||
if (_direction.value != newDir) {
|
||||
_direction.value = newDir
|
||||
updateUI()
|
||||
calculateOutput()
|
||||
updateWarningState()
|
||||
}
|
||||
}
|
||||
|
||||
fun setDirectionRight() {
|
||||
val newDir = when (_pair.value) {
|
||||
BridgePair.DOT_HEZ -> BridgeDirection.HEZ_TO_DOT
|
||||
BridgePair.USDT -> BridgeDirection.WUSDT_TO_USDT
|
||||
null -> BridgeDirection.HEZ_TO_DOT
|
||||
}
|
||||
if (_direction.value != newDir) {
|
||||
_direction.value = newDir
|
||||
updateUI()
|
||||
calculateOutput()
|
||||
updateWarningState()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,26 +139,27 @@ class BridgeViewModel(
|
||||
if (currentAmount <= 0) return
|
||||
|
||||
launch {
|
||||
// Determine which chain and asset to send from based on direction
|
||||
val chainId = when (dir) {
|
||||
BridgeDirection.DOT_TO_HEZ -> POLKADOT_ASSET_HUB_ID // Send DOT from Polkadot Asset Hub
|
||||
BridgeDirection.HEZ_TO_DOT -> PEZKUWI_ASSET_HUB_ID // Send HEZ from Pezkuwi Asset Hub
|
||||
BridgeDirection.DOT_TO_HEZ -> POLKADOT_ASSET_HUB_ID
|
||||
BridgeDirection.HEZ_TO_DOT -> PEZKUWI_ASSET_HUB_ID
|
||||
BridgeDirection.USDT_TO_WUSDT -> POLKADOT_ASSET_HUB_ID
|
||||
BridgeDirection.WUSDT_TO_USDT -> PEZKUWI_ASSET_HUB_ID
|
||||
}
|
||||
|
||||
// Get the chain to convert address to correct format
|
||||
val chain = chainRegistry.getChain(chainId)
|
||||
val assetId = when (dir) {
|
||||
BridgeDirection.DOT_TO_HEZ -> UTILITY_ASSET_ID
|
||||
BridgeDirection.HEZ_TO_DOT -> UTILITY_ASSET_ID
|
||||
BridgeDirection.USDT_TO_WUSDT -> POLKADOT_USDT_ASSET_ID
|
||||
BridgeDirection.WUSDT_TO_USDT -> PEZKUWI_USDT_ASSET_ID
|
||||
}
|
||||
|
||||
// Convert generic address to chain-specific format
|
||||
val chain = chainRegistry.getChain(chainId)
|
||||
val accountId = BRIDGE_ADDRESS_GENERIC.toAccountId()
|
||||
val bridgeAddress = chain.addressOf(accountId)
|
||||
|
||||
// Create asset payload (utility asset = native token)
|
||||
val assetPayload = AssetPayload(chainId, UTILITY_ASSET_ID)
|
||||
|
||||
// Create send payload specifying the origin asset
|
||||
val assetPayload = AssetPayload(chainId, assetId)
|
||||
val sendPayload = SendPayload.SpecifiedOrigin(assetPayload)
|
||||
|
||||
// Open send screen with pre-filled bridge address AND amount
|
||||
router.openSend(sendPayload, bridgeAddress, currentAmount)
|
||||
}
|
||||
}
|
||||
@@ -229,15 +168,108 @@ class BridgeViewModel(
|
||||
router.back()
|
||||
}
|
||||
|
||||
private fun fetchExchangeRate() {
|
||||
launch {
|
||||
try {
|
||||
val (rate, _) = withContext(Dispatchers.IO) {
|
||||
fetchRateFromCoinGecko()
|
||||
}
|
||||
dotToHezRate = rate
|
||||
updateUI()
|
||||
calculateOutput()
|
||||
} catch (e: Exception) {
|
||||
dotToHezRate = FALLBACK_RATE
|
||||
updateUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchRateFromCoinGecko(): Pair<Double, String> {
|
||||
return try {
|
||||
val response = URL(COINGECKO_API).readText()
|
||||
val json = JSONObject(response)
|
||||
val dotPrice = json.optJSONObject("polkadot")?.optDouble("usd", 0.0) ?: 0.0
|
||||
val hezPrice = json.optJSONObject("hezkurd")?.optDouble("usd", 0.0) ?: 0.0
|
||||
when {
|
||||
dotPrice > 0 && hezPrice > 0 -> Pair(dotPrice / hezPrice, "coingecko")
|
||||
else -> Pair(FALLBACK_RATE, "fallback")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Pair(FALLBACK_RATE, "fallback")
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchBridgeStatus() {
|
||||
launch {
|
||||
try {
|
||||
val (hezToDot, wusdtToUsdt) = withContext(Dispatchers.IO) {
|
||||
fetchStatusFromApi()
|
||||
}
|
||||
isHezToDotActive = hezToDot
|
||||
isWusdtToUsdtActive = wusdtToUsdt
|
||||
updateWarningState()
|
||||
} catch (e: Exception) {
|
||||
isHezToDotActive = false
|
||||
isWusdtToUsdtActive = false
|
||||
updateWarningState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchStatusFromApi(): Pair<Boolean, Boolean> {
|
||||
return try {
|
||||
val response = URL(BRIDGE_STATUS_API).readText()
|
||||
val json = JSONObject(response)
|
||||
val hezToDot = json.optBoolean("hezToDotActive", false)
|
||||
val wusdtToUsdt = json.optBoolean("wusdtToUsdtActive", false)
|
||||
Pair(hezToDot, wusdtToUsdt)
|
||||
} catch (e: Exception) {
|
||||
Pair(false, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWarningState() {
|
||||
val dir = _direction.value ?: return
|
||||
|
||||
when (dir) {
|
||||
BridgeDirection.HEZ_TO_DOT -> {
|
||||
_showWarning.postValue(true)
|
||||
if (!isHezToDotActive) {
|
||||
_warningBlocked.postValue(true)
|
||||
_warningText.postValue(resourceManager.getString(R.string.bridge_hez_to_dot_blocked))
|
||||
} else {
|
||||
_warningBlocked.postValue(false)
|
||||
_warningText.postValue(resourceManager.getString(R.string.bridge_hez_to_dot_warning))
|
||||
}
|
||||
}
|
||||
BridgeDirection.WUSDT_TO_USDT -> {
|
||||
_showWarning.postValue(true)
|
||||
if (!isWusdtToUsdtActive) {
|
||||
_warningBlocked.postValue(true)
|
||||
_warningText.postValue(resourceManager.getString(R.string.bridge_wusdt_to_usdt_blocked))
|
||||
} else {
|
||||
_warningBlocked.postValue(false)
|
||||
_warningText.postValue("")
|
||||
_showWarning.postValue(false)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
_showWarning.postValue(false)
|
||||
}
|
||||
}
|
||||
updateButtonState()
|
||||
}
|
||||
|
||||
private fun calculateOutput() {
|
||||
val dir = _direction.value ?: return
|
||||
|
||||
val grossOutput = when (dir) {
|
||||
BridgeDirection.DOT_TO_HEZ -> currentAmount * dotToHezRate
|
||||
BridgeDirection.HEZ_TO_DOT -> currentAmount / dotToHezRate
|
||||
BridgeDirection.USDT_TO_WUSDT -> currentAmount // 1:1
|
||||
BridgeDirection.WUSDT_TO_USDT -> currentAmount // 1:1
|
||||
}
|
||||
|
||||
// Apply fee
|
||||
val netOutput = grossOutput * (1 - FEE_PERCENT)
|
||||
|
||||
_outputAmount.value = if (netOutput > 0) {
|
||||
@@ -250,18 +282,21 @@ class BridgeViewModel(
|
||||
private fun updateUI() {
|
||||
val dir = _direction.value ?: return
|
||||
|
||||
val rateFormatted = BigDecimal(dotToHezRate).setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
|
||||
val reverseRateFormatted = BigDecimal(1.0 / dotToHezRate).setScale(6, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
|
||||
|
||||
when (dir) {
|
||||
BridgeDirection.DOT_TO_HEZ -> {
|
||||
val rateFormatted = BigDecimal(dotToHezRate).setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
|
||||
_exchangeRateText.value = "1 DOT = $rateFormatted HEZ"
|
||||
_minimumText.value = "$MIN_DOT DOT"
|
||||
}
|
||||
BridgeDirection.HEZ_TO_DOT -> {
|
||||
_exchangeRateText.value = "1 HEZ = $reverseRateFormatted DOT"
|
||||
val reverseRate = BigDecimal(1.0 / dotToHezRate).setScale(6, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString()
|
||||
_exchangeRateText.value = "1 HEZ = $reverseRate DOT"
|
||||
_minimumText.value = "$MIN_HEZ HEZ"
|
||||
}
|
||||
BridgeDirection.USDT_TO_WUSDT, BridgeDirection.WUSDT_TO_USDT -> {
|
||||
_exchangeRateText.value = "1:1 (fee 0.1%)"
|
||||
_minimumText.value = "$MIN_USDT USDT"
|
||||
}
|
||||
}
|
||||
|
||||
updateButtonState()
|
||||
@@ -272,12 +307,14 @@ class BridgeViewModel(
|
||||
val minimum = when (dir) {
|
||||
BridgeDirection.DOT_TO_HEZ -> MIN_DOT
|
||||
BridgeDirection.HEZ_TO_DOT -> MIN_HEZ
|
||||
BridgeDirection.USDT_TO_WUSDT, BridgeDirection.WUSDT_TO_USDT -> MIN_USDT
|
||||
}
|
||||
|
||||
_buttonState.value = when {
|
||||
currentAmount <= 0 -> ButtonState.DISABLED
|
||||
currentAmount < minimum -> ButtonState.DISABLED
|
||||
dir == BridgeDirection.HEZ_TO_DOT && !isHezToDotActive -> ButtonState.DISABLED
|
||||
dir == BridgeDirection.WUSDT_TO_USDT && !isWusdtToUsdtActive -> ButtonState.DISABLED
|
||||
else -> ButtonState.NORMAL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,41 @@
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Pair Selector -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/bg_primary_list_item"
|
||||
android:orientation="horizontal"
|
||||
android:padding="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bridgePairDotHez"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_button_primary"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
android:text="DOT / HEZ"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="13sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bridgePairUsdt"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
android:text="USDT"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Direction Toggle -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -35,7 +70,7 @@
|
||||
android:padding="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bridgeDirectionDotToHez"
|
||||
android:id="@+id/bridgeDirectionLeft"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
@@ -48,7 +83,7 @@
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bridgeDirectionHezToDot"
|
||||
android:id="@+id/bridgeDirectionRight"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
|
||||
@@ -15,53 +15,92 @@
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pezkuwiDashboardTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pezkuwi_dashboard_title"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@+id/pezkuwiDashboardRoles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:flexWrap="wrap"
|
||||
app:alignItems="center"
|
||||
app:justifyContent="flex_start" />
|
||||
|
||||
<!-- Top row: Title + Logo -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="vertical">
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pezkuwi_dashboard_trust_score"
|
||||
android:textColor="#B0BEC5"
|
||||
android:textSize="12sp" />
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pezkuwiDashboardTrustValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/pezkuwiDashboardTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pezkuwi_dashboard_title"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@+id/pezkuwiDashboardRoles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
app:flexWrap="wrap"
|
||||
app:alignItems="center"
|
||||
app:justifyContent="flex_start" />
|
||||
|
||||
<!-- Trust Score -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pezkuwi_dashboard_trust_score"
|
||||
android:textColor="#B0BEC5"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pezkuwiDashboardTrustValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textColor="#FFD54F"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pezkuwiDashboardLogo"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:src="@drawable/telegram_welcome"
|
||||
android:scaleType="fitCenter"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/pezkuwi_dashboard_title" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Info text -->
|
||||
<TextView
|
||||
android:id="@+id/pezkuwiDashboardInfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/pezkuwi_dashboard_info"
|
||||
android:textColor="#CFD8DC"
|
||||
android:textSize="11sp"
|
||||
android:lineSpacingExtra="2dp" />
|
||||
|
||||
<!-- Action button -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/pezkuwiDashboardBasvuruButton"
|
||||
style="@style/Widget.Nova.MaterialButton.Primary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/pezkuwi_dashboard_basvuru"
|
||||
android:textAllCaps="false" />
|
||||
</LinearLayout>
|
||||
|
||||
Reference in New Issue
Block a user