feat: add wUSDT bridge feature module

- Add feature-bridge-api module with domain models:
  - BridgeConfig: bridge configuration (addresses, fees, limits)
  - BridgeStatus: backing ratio, reserves, operational status
  - BridgeTransaction: deposit/withdrawal records

- Add feature-bridge-impl module with UI:
  - BridgeDepositFragment: shows Polkadot deposit address with QR code
  - BridgeStatusFragment: shows backing ratio and transparency info

Bridge enables 1:1 backed wUSDT on Pezkuwi Asset Hub,
backed by real USDT on Polkadot Asset Hub.

Bridge wallet: 16dSTc3BexjQKiPta7yNncF8nio4YgDQiPbudHzkuh7XJi8K (Polkadot)
wUSDT Asset ID: 1000, Min deposit: 10 USDT, Fee: 0.1%
This commit is contained in:
2026-01-23 09:36:22 +03:00
parent 50c9beb8e1
commit 71ca24cbdb
21 changed files with 1043 additions and 0 deletions
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -0,0 +1,8 @@
package io.novafoundation.nova.feature_bridge_api.di
import io.novafoundation.nova.feature_bridge_api.domain.model.BridgeConfig
interface BridgeFeatureApi {
val bridgeConfig: BridgeConfig
}
@@ -0,0 +1,58 @@
package io.novafoundation.nova.feature_bridge_api.domain.model
import java.math.BigDecimal
/**
* wUSDT Bridge Configuration
*
* This bridge enables 1:1 backed wUSDT on Pezkuwi Asset Hub,
* backed by real USDT on Polkadot Asset Hub.
*/
data class BridgeConfig(
/** Bridge wallet address on Polkadot Asset Hub (for deposits) */
val polkadotDepositAddress: String,
/** Bridge wallet address on Pezkuwi Asset Hub */
val pezkuwiAddress: String,
/** USDT Asset ID on Polkadot Asset Hub */
val polkadotUsdtAssetId: Int,
/** wUSDT Asset ID on Pezkuwi Asset Hub */
val pezkuwiWusdtAssetId: Int,
/** Minimum deposit amount in USDT */
val minDeposit: BigDecimal,
/** Minimum withdrawal amount in USDT */
val minWithdraw: BigDecimal,
/** Bridge fee in basis points (e.g., 10 = 0.1%) */
val feeBasisPoints: Int
) {
companion object {
val DEFAULT = BridgeConfig(
polkadotDepositAddress = "16dSTc3BexjQKiPta7yNncF8nio4YgDQiPbudHzkuh7XJi8K",
pezkuwiAddress = "5Hh9KGn7oBTvtBPNcUvNeTQyw6oQrNfGdtsRU11QMc618Rse",
polkadotUsdtAssetId = 1984,
pezkuwiWusdtAssetId = 1000,
minDeposit = BigDecimal("10"),
minWithdraw = BigDecimal("10"),
feeBasisPoints = 10
)
}
/** Fee percentage as a human-readable string */
val feePercentage: String
get() = "${feeBasisPoints.toDouble() / 100}%"
/** Calculate fee for a given amount */
fun calculateFee(amount: BigDecimal): BigDecimal {
return amount.multiply(BigDecimal(feeBasisPoints)).divide(BigDecimal(10000))
}
/** Calculate net amount after fee */
fun calculateNetAmount(amount: BigDecimal): BigDecimal {
return amount.subtract(calculateFee(amount))
}
}
@@ -0,0 +1,37 @@
package io.novafoundation.nova.feature_bridge_api.domain.model
import java.math.BigDecimal
/**
* Bridge status showing backing ratio and reserves
*/
data class BridgeStatus(
/** Total USDT held in bridge wallet on Polkadot */
val totalUsdtBacking: BigDecimal,
/** Total wUSDT in circulation on Pezkuwi */
val totalWusdtCirculating: BigDecimal,
/** Bridge operational status */
val isOperational: Boolean,
/** Last sync timestamp */
val lastSyncTimestamp: Long
) {
/** Backing ratio (should be >= 100%) */
val backingRatio: BigDecimal
get() = if (totalWusdtCirculating > BigDecimal.ZERO) {
totalUsdtBacking.divide(totalWusdtCirculating, 4, java.math.RoundingMode.HALF_UP)
.multiply(BigDecimal(100))
} else {
BigDecimal(100)
}
/** Reserve (excess USDT in bridge, i.e., collected fees) */
val reserve: BigDecimal
get() = totalUsdtBacking.subtract(totalWusdtCirculating).coerceAtLeast(BigDecimal.ZERO)
/** Is the bridge fully backed? */
val isFullyBacked: Boolean
get() = backingRatio >= BigDecimal(100)
}
@@ -0,0 +1,33 @@
package io.novafoundation.nova.feature_bridge_api.domain.model
import java.math.BigDecimal
enum class BridgeTransactionType {
DEPOSIT, // Polkadot USDT -> Pezkuwi wUSDT
WITHDRAW // Pezkuwi wUSDT -> Polkadot USDT
}
enum class BridgeTransactionStatus {
PENDING,
PROCESSING,
COMPLETED,
FAILED
}
/**
* A bridge transaction (deposit or withdrawal)
*/
data class BridgeTransaction(
val id: String,
val type: BridgeTransactionType,
val status: BridgeTransactionStatus,
val amount: BigDecimal,
val fee: BigDecimal,
val netAmount: BigDecimal,
val sourceAddress: String,
val destinationAddress: String,
val sourceTxHash: String?,
val destinationTxHash: String?,
val createdAt: Long,
val completedAt: Long?
)
@@ -0,0 +1,12 @@
package io.novafoundation.nova.feature_bridge_api.presentation
interface BridgeRouter {
fun openBridgeDeposit()
fun openBridgeWithdraw()
fun openBridgeStatus()
fun back()
}