mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-23 01:27:57 +00:00
Initial commit: Pezkuwi Wallet Android
Complete rebrand of Nova Wallet for Pezkuwichain ecosystem. ## Features - Full Pezkuwichain support (HEZ & PEZ tokens) - Polkadot ecosystem compatibility - Staking, Governance, DeFi, NFTs - XCM cross-chain transfers - Hardware wallet support (Ledger, Polkadot Vault) - WalletConnect v2 - Push notifications ## Languages - English, Turkish, Kurmanci (Kurdish), Spanish, French, German, Russian, Japanese, Chinese, Korean, Portuguese, Vietnamese Based on Nova Wallet by Novasama Technologies GmbH © Dijital Kurdistan Tech Institute 2026
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest>
|
||||
|
||||
</manifest>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.network.blockhain.binding
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindNumber
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindString
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.cast
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.incompatible
|
||||
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.Contribution.Companion.DIRECT_SOURCE_ID
|
||||
import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.fromHex
|
||||
import java.math.BigInteger
|
||||
|
||||
class DirectContribution(
|
||||
val amount: BigInteger,
|
||||
val memo: String,
|
||||
) {
|
||||
|
||||
val sourceId = DIRECT_SOURCE_ID
|
||||
}
|
||||
|
||||
fun bindContribution(scale: String, runtime: RuntimeSnapshot): DirectContribution {
|
||||
val type = runtime.typeRegistry["(Balance, Vec<u8>)"] ?: incompatible()
|
||||
|
||||
val dynamicInstance = type.fromHex(runtime, scale).cast<List<*>>()
|
||||
|
||||
return DirectContribution(
|
||||
amount = bindNumber(dynamicInstance[0]),
|
||||
memo = bindString(dynamicInstance[1])
|
||||
)
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.network.blockhain.binding
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.ParaId
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindAccountId
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindNumber
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.castToStruct
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.fromHexOrIncompatible
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.storageReturnType
|
||||
import io.novafoundation.nova.common.utils.Modules
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.primitives.u32
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.toByteArray
|
||||
import java.math.BigInteger
|
||||
|
||||
class FundInfo(
|
||||
val depositor: AccountId,
|
||||
val deposit: BigInteger,
|
||||
val raised: BigInteger,
|
||||
val lastSlot: BigInteger,
|
||||
val firstSlot: BigInteger,
|
||||
val end: BigInteger,
|
||||
val cap: BigInteger,
|
||||
val verifier: Any?,
|
||||
val trieIndex: TrieIndex,
|
||||
val paraId: ParaId,
|
||||
val bidderAccountId: AccountId,
|
||||
val pre9180BidderAccountId: AccountId,
|
||||
)
|
||||
|
||||
fun bindFundInfo(dynamic: Any?, runtime: RuntimeSnapshot, paraId: ParaId): FundInfo {
|
||||
val dynamicInstance = dynamic.castToStruct()
|
||||
|
||||
val fundIndex = bindTrieIndex(dynamicInstance["fundIndex"] ?: dynamicInstance["trieIndex"])
|
||||
|
||||
return FundInfo(
|
||||
depositor = bindAccountId(dynamicInstance["depositor"]),
|
||||
deposit = bindNumber(dynamicInstance["deposit"]),
|
||||
raised = bindNumber(dynamicInstance["raised"]),
|
||||
end = bindNumber(dynamicInstance["end"]),
|
||||
cap = bindNumber(dynamicInstance["cap"]),
|
||||
firstSlot = bindNumber(dynamicInstance["firstPeriod"] ?: dynamicInstance["firstSlot"]),
|
||||
lastSlot = bindNumber(dynamicInstance["lastPeriod"] ?: dynamicInstance["lastSlot"]),
|
||||
verifier = dynamicInstance["verifier"],
|
||||
trieIndex = fundIndex,
|
||||
bidderAccountId = createBidderAccountId(runtime, fundIndex),
|
||||
pre9180BidderAccountId = createBidderAccountId(runtime, paraId),
|
||||
paraId = paraId
|
||||
)
|
||||
}
|
||||
|
||||
fun bindFundInfo(scale: String, runtime: RuntimeSnapshot, paraId: ParaId): FundInfo {
|
||||
val type = runtime.metadata.storageReturnType(Modules.CROWDLOAN, "Funds")
|
||||
|
||||
val dynamicInstance = type.fromHexOrIncompatible(scale, runtime)
|
||||
|
||||
return bindFundInfo(dynamicInstance, runtime, paraId)
|
||||
}
|
||||
|
||||
private val ADDRESS_PADDING = ByteArray(32)
|
||||
private val ADDRESS_PREFIX = "modlpy/cfund".encodeToByteArray()
|
||||
|
||||
private fun createBidderAccountId(runtime: RuntimeSnapshot, index: BigInteger): AccountId {
|
||||
val fullKey = ADDRESS_PREFIX + u32.toByteArray(runtime, index) + ADDRESS_PADDING
|
||||
|
||||
return fullKey.copyOfRange(0, 32)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.network.blockhain.binding
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.BalanceOf
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindAccountId
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindList
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindNumber
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.cast
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
|
||||
class LeaseEntry(
|
||||
val accountId: AccountId,
|
||||
val locked: BalanceOf
|
||||
)
|
||||
|
||||
fun bindLeases(decoded: Any?): List<LeaseEntry?> {
|
||||
return bindList(decoded) {
|
||||
it?.let {
|
||||
val (accountIdRaw, balanceRaw) = it.cast<List<*>>()
|
||||
|
||||
LeaseEntry(
|
||||
accountId = bindAccountId(accountIdRaw),
|
||||
locked = bindNumber(balanceRaw)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.network.blockhain.binding
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.HelperBinding
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindNumber
|
||||
import java.math.BigInteger
|
||||
|
||||
typealias TrieIndex = BigInteger
|
||||
|
||||
@HelperBinding
|
||||
fun bindTrieIndex(dynamicInstance: Any?) = bindNumber(dynamicInstance)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.network.updater
|
||||
|
||||
import io.novafoundation.nova.core.updater.UpdateScope
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.feature_crowdloan_api.data.network.updater.AssetBalanceScope.ScopeValue
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.model.Asset
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AssetBalanceScope : UpdateScope<ScopeValue> {
|
||||
|
||||
class ScopeValue(val metaAccount: MetaAccount, val asset: Asset)
|
||||
|
||||
override fun invalidationFlow(): Flow<ScopeValue>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.network.updater
|
||||
|
||||
import io.novafoundation.nova.core.updater.UpdateSystem
|
||||
|
||||
interface ContributionsUpdateSystemFactory {
|
||||
fun create(): UpdateSystem
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.network.updater
|
||||
|
||||
import io.novafoundation.nova.core.updater.Updater
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
|
||||
interface ContributionsUpdaterFactory {
|
||||
fun create(chain: Chain, assetBalanceScope: AssetBalanceScope): Updater<AssetBalanceScope.ScopeValue>
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.repository
|
||||
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.Contribution
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.FullChainAssetId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ContributionsRepository {
|
||||
|
||||
fun loadContributionsGraduallyFlow(
|
||||
chain: Chain,
|
||||
accountId: ByteArray,
|
||||
): Flow<Pair<String, Result<List<Contribution>>>>
|
||||
|
||||
fun observeContributions(metaAccount: MetaAccount): Flow<List<Contribution>>
|
||||
|
||||
fun observeContributions(metaAccount: MetaAccount, chain: Chain, asset: Chain.Asset): Flow<List<Contribution>>
|
||||
|
||||
suspend fun getDirectContributions(chain: Chain, asset: Chain.Asset, accountId: ByteArray): Result<List<Contribution>>
|
||||
|
||||
suspend fun deleteContributions(assetIds: List<FullChainAssetId>)
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.repository
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.ParaId
|
||||
import io.novafoundation.nova.feature_crowdloan_api.data.network.blockhain.binding.FundInfo
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
||||
interface CrowdloanRepository {
|
||||
|
||||
suspend fun allFundInfos(chainId: ChainId): Map<ParaId, FundInfo>
|
||||
|
||||
suspend fun getWinnerInfo(chainId: ChainId, funds: Map<ParaId, FundInfo>): Map<ParaId, Boolean>
|
||||
|
||||
suspend fun getParachainMetadata(chain: Chain): Map<ParaId, ParachainMetadata>
|
||||
|
||||
suspend fun leasePeriodToBlocksConverter(chainId: ChainId): LeasePeriodToBlocksConverter
|
||||
|
||||
fun fundInfoFlow(chainId: ChainId, parachainId: ParaId): Flow<FundInfo>
|
||||
|
||||
suspend fun minContribution(chainId: ChainId): BigInteger
|
||||
}
|
||||
|
||||
class ParachainMetadata(
|
||||
val paraId: ParaId,
|
||||
val movedToParaId: ParaId?,
|
||||
val iconLink: String,
|
||||
val name: String,
|
||||
val description: String,
|
||||
val rewardRate: BigDecimal?,
|
||||
val website: String,
|
||||
val customFlow: String?,
|
||||
val token: String,
|
||||
val extras: Map<String, String>,
|
||||
)
|
||||
|
||||
fun ParachainMetadata.getExtra(key: String) = extras[key] ?: throw IllegalArgumentException("No key $key found in parachain metadata extras for $name")
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.repository
|
||||
|
||||
import io.novafoundation.nova.feature_crowdloan_api.data.network.blockhain.binding.FundInfo
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||
|
||||
suspend fun CrowdloanRepository.hasWonAuction(chainId: ChainId, fundInfo: FundInfo): Boolean {
|
||||
val paraId = fundInfo.paraId
|
||||
|
||||
return getWinnerInfo(chainId, mapOf(paraId to fundInfo)).getValue(paraId)
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.repository
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.BlockNumber
|
||||
import java.math.BigInteger
|
||||
|
||||
class LeasePeriodToBlocksConverter(
|
||||
private val blocksPerLease: BigInteger,
|
||||
private val blocksOffset: BigInteger
|
||||
) {
|
||||
|
||||
fun startBlockFor(leasePeriod: BigInteger): BlockNumber {
|
||||
return blocksPerLease * leasePeriod + blocksOffset
|
||||
}
|
||||
|
||||
fun leaseIndexFromBlock(blockNumber: BlockNumber): BigInteger {
|
||||
return (blockNumber - blocksOffset) / blocksPerLease
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.data.source.contribution
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.ParaId
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import java.math.BigInteger
|
||||
|
||||
interface ExternalContributionSource {
|
||||
|
||||
class ExternalContribution(
|
||||
val sourceId: String,
|
||||
val amount: BigInteger,
|
||||
val paraId: ParaId,
|
||||
)
|
||||
|
||||
/**
|
||||
* null in case every chain is supported
|
||||
*/
|
||||
val supportedChains: Set<ChainId>?
|
||||
|
||||
val sourceId: String
|
||||
|
||||
suspend fun getContributions(
|
||||
chain: Chain,
|
||||
accountId: AccountId,
|
||||
): Result<List<ExternalContribution>>
|
||||
}
|
||||
|
||||
fun ExternalContributionSource.supports(chain: Chain) = supportedChains == null || chain.id in supportedChains!!
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.di
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Crowdloan
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.di
|
||||
|
||||
import io.novafoundation.nova.feature_crowdloan_api.data.repository.ContributionsRepository
|
||||
import io.novafoundation.nova.feature_crowdloan_api.data.repository.CrowdloanRepository
|
||||
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.ContributionsInteractor
|
||||
|
||||
interface CrowdloanFeatureApi {
|
||||
|
||||
fun repository(): CrowdloanRepository
|
||||
|
||||
fun contributionsInteractor(): ContributionsInteractor
|
||||
|
||||
fun contributionsRepository(): ContributionsRepository
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.domain.contributions
|
||||
|
||||
import io.novafoundation.nova.common.address.AccountIdKey
|
||||
import io.novafoundation.nova.common.address.intoKey
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.BlockNumber
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.ParaId
|
||||
import io.novafoundation.nova.common.utils.formatting.TimerValue
|
||||
import io.novafoundation.nova.core_db.model.ContributionLocal
|
||||
import io.novafoundation.nova.feature_crowdloan_api.data.repository.ParachainMetadata
|
||||
import io.novafoundation.nova.runtime.ext.utilityAsset
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.util.BlockDurationEstimator
|
||||
import io.novafoundation.nova.runtime.util.timerUntil
|
||||
import java.math.BigInteger
|
||||
|
||||
class Contribution(
|
||||
val chain: Chain,
|
||||
val asset: Chain.Asset,
|
||||
val amountInPlanks: BigInteger,
|
||||
val paraId: ParaId,
|
||||
val sourceId: String,
|
||||
val unlockBlock: BlockNumber,
|
||||
val leaseDepositor: AccountIdKey,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val DIRECT_SOURCE_ID = "direct"
|
||||
const val LIQUID_SOURCE_ID = "liquid"
|
||||
const val PARALLEL_SOURCE_ID = "parallel"
|
||||
}
|
||||
}
|
||||
|
||||
class ContributionMetadata(
|
||||
val claimStatus: ContributionClaimStatus,
|
||||
val parachainMetadata: ParachainMetadata?,
|
||||
)
|
||||
|
||||
sealed class ContributionClaimStatus {
|
||||
|
||||
object Claimable : ContributionClaimStatus()
|
||||
|
||||
class ReturnsIn(val timer: TimerValue) : ContributionClaimStatus()
|
||||
}
|
||||
|
||||
class ContributionWithMetadata(
|
||||
val contribution: Contribution,
|
||||
val metadata: ContributionMetadata
|
||||
)
|
||||
|
||||
fun BlockDurationEstimator.claimStatusOf(contribution: Contribution): ContributionClaimStatus {
|
||||
return if (contribution.unlockBlock > currentBlock) {
|
||||
ContributionClaimStatus.ReturnsIn(timerUntil(contribution.unlockBlock))
|
||||
} else {
|
||||
ContributionClaimStatus.Claimable
|
||||
}
|
||||
}
|
||||
|
||||
class ContributionsWithTotalAmount<T>(val totalContributed: BigInteger, val contributions: List<T>) {
|
||||
companion object {
|
||||
fun <T> empty(): ContributionsWithTotalAmount<T> {
|
||||
return ContributionsWithTotalAmount(BigInteger.ZERO, emptyList())
|
||||
}
|
||||
|
||||
fun fromContributions(contributions: List<Contribution>): ContributionsWithTotalAmount<Contribution> {
|
||||
return ContributionsWithTotalAmount(
|
||||
totalContributed = contributions.sumOf { it.amountInPlanks },
|
||||
contributions = contributions
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun mapContributionToLocal(metaId: Long, contribution: Contribution): ContributionLocal {
|
||||
return ContributionLocal(
|
||||
metaId,
|
||||
contribution.chain.id,
|
||||
contribution.asset.id,
|
||||
contribution.paraId,
|
||||
contribution.amountInPlanks,
|
||||
contribution.sourceId,
|
||||
unlockBlock = contribution.unlockBlock,
|
||||
leaseDepositor = contribution.leaseDepositor.value
|
||||
)
|
||||
}
|
||||
|
||||
fun mapContributionFromLocal(
|
||||
contribution: ContributionLocal,
|
||||
chain: Chain,
|
||||
): Contribution {
|
||||
return Contribution(
|
||||
chain,
|
||||
chain.utilityAsset,
|
||||
contribution.amountInPlanks,
|
||||
contribution.paraId,
|
||||
contribution.sourceId,
|
||||
unlockBlock = contribution.unlockBlock,
|
||||
leaseDepositor = contribution.leaseDepositor.intoKey()
|
||||
)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.feature_crowdloan_api.domain.contributions
|
||||
|
||||
import io.novafoundation.nova.core.updater.Updater
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainAssetId
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ContributionsInteractor {
|
||||
|
||||
fun runUpdate(): Flow<Updater.SideEffect>
|
||||
|
||||
fun observeSelectedChainContributionsWithMetadata(): Flow<ContributionsWithTotalAmount<ContributionWithMetadata>>
|
||||
|
||||
fun observeChainContributions(
|
||||
metaAccount: MetaAccount,
|
||||
chainId: ChainId,
|
||||
assetId: ChainAssetId
|
||||
): Flow<ContributionsWithTotalAmount<Contribution>>
|
||||
}
|
||||
Reference in New Issue
Block a user