Initial commit: Pezkuwi Wallet Android

Security hardened release:
- Code obfuscation enabled (minifyEnabled=true, shrinkResources=true)
- Sensitive files excluded (google-services.json, keystores)
- Branch.io key moved to BuildConfig placeholder
- Updated dependencies: OkHttp 4.12.0, Gson 2.10.1, BouncyCastle 1.77
- Comprehensive ProGuard rules for crypto wallet
- Navigation 2.7.7, Lifecycle 2.7.0, ConstraintLayout 2.1.4
This commit is contained in:
2026-02-12 05:19:41 +03:00
commit a294aa1a6b
7687 changed files with 441811 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+27
View File
@@ -0,0 +1,27 @@
android {
namespace 'io.novafoundation.nova.feature_cloud_backup_test'
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation project(":feature-cloud-backup-api")
implementation project(":runtime")
implementation project(":common")
implementation substrateSdkDep
implementation kotlinDep
api jUnitDep
api mockitoDep
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -0,0 +1,306 @@
package io.novafoundation.feature_cloud_backup_test
import io.novafoundation.nova.core.model.CryptoType
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPrivateInfo
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPrivateInfo.ChainAccountSecrets
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPrivateInfo.EthereumSecrets
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPrivateInfo.KeyPairSecrets
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPrivateInfo.SubstrateSecrets
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPublicInfo
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPublicInfo.ChainAccountInfo
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.CloudBackup.WalletPublicInfo.ChainAccountInfo.ChainAccountCryptoType
import io.novafoundation.nova.feature_cloud_backup_api.domain.model.isCompletelyEmpty
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
import io.novasama.substrate_sdk_android.runtime.AccountId
const val TEST_MODIFIED_AT = 0L
@DslMarker
annotation class CloudBackupBuildDsl
@CloudBackupBuildDsl
fun buildTestCloudBackup(builder: CloudBackupBuilder.() -> Unit): CloudBackup {
return CloudBackupBuilder().apply(builder).build()
}
@CloudBackupBuildDsl
class CloudBackupBuilder {
private var privateData: CloudBackup.PrivateData? = null
private var publicData: CloudBackup.PublicData? = null
fun publicData(
builder: CloudBackupPublicDataBuilder.() -> Unit
) {
publicData = CloudBackupPublicDataBuilder().apply(builder).build()
}
fun privateData(
builder: CloudBackupPrivateDataBuilder.() -> Unit
) {
privateData = CloudBackupPrivateDataBuilder().apply(builder).build()
}
fun build(): CloudBackup {
return CloudBackup(
publicData = requireNotNull(publicData),
privateData = requireNotNull(privateData)
)
}
}
@CloudBackupBuildDsl
class CloudBackupPublicDataBuilder {
private var _modifiedAt: Long = TEST_MODIFIED_AT
private val wallets = mutableListOf<WalletPublicInfo>()
fun modifiedAt(value: Long) {
_modifiedAt = value
}
fun wallet(walletId: String, builder: WalletPublicInfoBuilder.() -> Unit): WalletPublicInfo {
val element = WalletPublicInfoBuilder(walletId).apply(builder).build()
wallets.add(element)
return element
}
fun build(): CloudBackup.PublicData {
return CloudBackup.PublicData(_modifiedAt, wallets)
}
}
@CloudBackupBuildDsl
class CloudBackupPrivateDataBuilder {
private val wallets = mutableListOf<WalletPrivateInfo>()
fun wallet(walletId: String, builder: WalletPrivateInfoBuilder.() -> Unit) {
val privateInfo = WalletPrivateInfoBuilder(walletId).apply(builder).build()
if (!privateInfo.isCompletelyEmpty()) {
wallets.add(privateInfo)
}
}
fun build(): CloudBackup.PrivateData {
return CloudBackup.PrivateData(wallets)
}
}
@CloudBackupBuildDsl
class WalletPrivateInfoBuilder(
private val walletId: String,
) {
private var _entropy: ByteArray? = null
private var substrate: SubstrateSecrets? = null
private var ethereum: EthereumSecrets? = null
private val chainAccounts = mutableListOf<ChainAccountSecrets>()
fun entropy(value: ByteArray) {
_entropy = value
}
fun substrate(builder: BackupSubstrateSecretsBuilder.() -> Unit) {
substrate = BackupSubstrateSecretsBuilder().apply(builder).build()
}
fun ethereum(builder: BackupEthereumSecretsBuilder.() -> Unit) {
ethereum = BackupEthereumSecretsBuilder().apply(builder).build()
}
fun chainAccount(accountId: AccountId, builder: (BackupChainAccountSecretsBuilder.() -> Unit)? = null) {
val element = BackupChainAccountSecretsBuilder(accountId).apply { builder?.invoke(this) }.build()
chainAccounts.add(element)
}
fun build(): WalletPrivateInfo {
return WalletPrivateInfo(
entropy = _entropy,
walletId = walletId,
substrate = substrate,
ethereum = ethereum,
chainAccounts = chainAccounts,
)
}
}
@CloudBackupBuildDsl
class BackupSubstrateSecretsBuilder {
private var _keypair: KeyPairSecrets? = null
private var _seed: ByteArray? = null
private var _derivationPath: String? = null
fun seed(value: ByteArray) {
_seed = value
}
fun derivationPath(value: String?) {
_derivationPath = value
}
fun keypair(keypair: KeyPairSecrets) {
_keypair = keypair
}
fun build(): SubstrateSecrets {
return SubstrateSecrets(_seed, _keypair, _derivationPath)
}
}
@CloudBackupBuildDsl
class BackupEthereumSecretsBuilder {
private var _keypair: KeyPairSecrets? = null
private var _derivationPath: String? = null
fun derivationPath(value: String?) {
_derivationPath = value
}
fun keypair(keypair: KeyPairSecrets) {
_keypair = keypair
}
fun build(): EthereumSecrets {
return EthereumSecrets(requireNotNull(_keypair), _derivationPath)
}
}
@CloudBackupBuildDsl
class BackupChainAccountSecretsBuilder(private val accountId: AccountId) {
private var _entropy: ByteArray? = null
private var _keypair: KeyPairSecrets? = null
private var _seed: ByteArray? = null
private var _derivationPath: String? = null
fun entropy(value: ByteArray) {
_entropy = value
}
fun seed(value: ByteArray) {
_seed = value
}
fun derivationPath(value: String?) {
_derivationPath = value
}
fun keypair(keypair: KeyPairSecrets) {
_keypair = keypair
}
fun keypairFromIndex(index: Int) {
_keypair = KeyPairSecrets(
privateKey = ByteArray(32) { index.toByte() },
publicKey = ByteArray(32) { index.toByte() },
nonce = ByteArray(32) { index.toByte() }
)
}
fun build(): ChainAccountSecrets {
return ChainAccountSecrets(accountId, _entropy, _seed, _keypair, _derivationPath)
}
}
@CloudBackupBuildDsl
class WalletPublicInfoBuilder(
private val walletId: String,
) {
private val chainAccounts = mutableListOf<ChainAccountInfo>()
private var _substratePublicKey: ByteArray? = null
private var _substrateCryptoType: CryptoType? = null
private var _substrateAccountId: ByteArray? = null
private var _ethereumPublicKey: ByteArray? = null
private var _ethereumAddress: ByteArray? = null
private var _name: String = ""
private var _isSelected: Boolean = false
private var _type: WalletPublicInfo.Type = WalletPublicInfo.Type.SECRETS
fun chainAccount(chainId: ChainId, builder: WalletChainAccountInfoBuilder.() -> Unit) {
val chainAccountLocal = WalletChainAccountInfoBuilder(chainId).apply(builder).build()
chainAccounts.add(chainAccountLocal)
}
fun substratePublicKey(value: ByteArray?) {
_substratePublicKey = value
}
fun substrateCryptoType(value: CryptoType?) {
_substrateCryptoType = value
}
fun substrateAccountId(value: ByteArray?) {
_substrateAccountId = value
}
fun ethereumPublicKey(value: ByteArray?) {
_ethereumPublicKey = value
}
fun ethereumAddress(value: ByteArray?) {
_ethereumAddress = value
}
fun name(value: String) {
_name = value
}
fun isSelected(value: Boolean) {
_isSelected = value
}
fun type(value: WalletPublicInfo.Type) {
_type = value
}
fun build(): WalletPublicInfo {
return WalletPublicInfo(
walletId = walletId,
substratePublicKey = _substratePublicKey,
substrateAccountId = _substrateAccountId,
substrateCryptoType = _substrateCryptoType,
ethereumAddress = _ethereumAddress,
ethereumPublicKey = _ethereumPublicKey,
name = _name,
type = _type,
chainAccounts = chainAccounts.toSet()
)
}
}
@CloudBackupBuildDsl
class WalletChainAccountInfoBuilder(
private val chainId: ChainId,
) {
private var _publicKey: ByteArray? = null
private var _accountId: ByteArray = ByteArray(32)
private var _cryptoType: ChainAccountCryptoType? = null
fun publicKey(value: ByteArray) {
_publicKey = value
}
fun accountId(value: ByteArray) {
_accountId = value
}
fun cryptoType(cryptoType: ChainAccountCryptoType) {
_cryptoType = cryptoType
}
fun build(): ChainAccountInfo {
return ChainAccountInfo(chainId, _publicKey, _accountId, _cryptoType)
}
}