Updated on 2026-08-14
This commit is contained in:
parent
3d95481b13
commit
f345835792
8 changed files with 2274 additions and 26 deletions
|
|
@ -10,4 +10,5 @@ object ModuleErrorCode {
|
|||
const val NETWORK = 20000
|
||||
const val DOMAIN = 30000
|
||||
const val SALT_PAY = 40000
|
||||
const val ONBOARDING = 50000
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.feature.onboarding.data
|
||||
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.crypto.bip39.BIP39Wordlist
|
||||
import com.tangem.crypto.bip39.DefaultMnemonic
|
||||
import com.tangem.crypto.bip39.EntropyLength
|
||||
import com.tangem.crypto.bip39.Mnemonic
|
||||
import com.tangem.crypto.bip39.Wordlist
|
||||
import com.tangem.datasource.asset.AssetReader
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface MnemonicRepository {
|
||||
|
||||
fun getWordsDictionary(): Set<String>
|
||||
|
||||
@Throws(TangemSdkError.MnemonicException::class)
|
||||
fun generateDefaultMnemonic(): Mnemonic
|
||||
|
||||
@Throws(TangemSdkError.MnemonicException::class)
|
||||
fun createMnemonic(mnemonicString: String): Mnemonic
|
||||
}
|
||||
|
||||
class DefaultMnemonicRepository @Inject constructor(
|
||||
private val assetReader: AssetReader,
|
||||
) : MnemonicRepository {
|
||||
|
||||
private val bip39Wordlist: Wordlist by lazy { BIP39Wordlist(assetReader.openFile(MNEMONIC_FILE_NAME)) }
|
||||
private val bip39Words: Set<String> by lazy { bip39Wordlist.words.toHashSet() }
|
||||
|
||||
override fun getWordsDictionary(): Set<String> = bip39Words
|
||||
|
||||
override fun generateDefaultMnemonic(): Mnemonic {
|
||||
return DefaultMnemonic(EntropyLength.Bits128Length, bip39Wordlist)
|
||||
}
|
||||
|
||||
override fun createMnemonic(mnemonicString: String): Mnemonic = DefaultMnemonic(mnemonicString, bip39Wordlist)
|
||||
|
||||
companion object {
|
||||
private const val MNEMONIC_FILE_NAME = "mnemonic/mnemonic_dictionary_en.txt"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.feature.onboarding.data.di
|
||||
|
||||
import com.tangem.datasource.asset.AssetReader
|
||||
import com.tangem.feature.onboarding.data.DefaultMnemonicRepository
|
||||
import com.tangem.feature.onboarding.data.MnemonicRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class OnboardingDataModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSeedPhraseSdkRepository(assetReader: AssetReader): MnemonicRepository {
|
||||
return DefaultMnemonicRepository(assetReader)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.feature.onboarding.domain
|
||||
|
||||
import com.tangem.common.module.ModuleError
|
||||
import com.tangem.common.module.ModuleErrorCode
|
||||
import com.tangem.common.module.ModuleMessage
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class OnboardingModuleError(
|
||||
subCode: Int,
|
||||
override val message: String,
|
||||
override val data: Any?,
|
||||
) : ModuleMessage, ModuleError() {
|
||||
override val code: Int = ModuleErrorCode.ONBOARDING + subCode
|
||||
|
||||
companion object {
|
||||
// base code used for all errors in the module
|
||||
internal const val ERROR_CODE_SEED_PHRASE = 100
|
||||
// const val CODE_ANY_OTHER = 200..299, 300..399 etc
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SeedPhraseError(
|
||||
subCode: Int = 0,
|
||||
message: String? = null,
|
||||
data: Any? = null,
|
||||
) : OnboardingModuleError(
|
||||
subCode = ERROR_CODE_SEED_PHRASE + subCode,
|
||||
message = message ?: this::class.java.simpleName,
|
||||
data = data,
|
||||
) {
|
||||
object InvalidWordCount : SeedPhraseError(0)
|
||||
object InvalidEntropyLength : SeedPhraseError(1)
|
||||
object InvalidWordsFile : SeedPhraseError(2)
|
||||
object InvalidChecksum : SeedPhraseError(3)
|
||||
object MnenmonicCreationFailed : SeedPhraseError(4)
|
||||
object NormalizationFailed : SeedPhraseError(5)
|
||||
object UnsupportedLanguage : SeedPhraseError(6)
|
||||
data class InvalidWords(val words: Set<String>) : SeedPhraseError(7)
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.feature.onboarding.domain
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface SeedPhraseChecker {
|
||||
suspend fun isSeedPhraseMatch(seedPhrase: List<String>): Boolean
|
||||
suspend fun isPhraseMatch(phrase: String, source: List<String>): Boolean
|
||||
}
|
||||
|
||||
class DummySeedPhraseChecker : SeedPhraseChecker {
|
||||
|
||||
override suspend fun isSeedPhraseMatch(seedPhrase: List<String>): Boolean = false
|
||||
|
||||
override suspend fun isPhraseMatch(phrase: String, source: List<String>): Boolean = source.contains(phrase)
|
||||
}
|
||||
|
|
@ -1,21 +1,109 @@
|
|||
package com.tangem.feature.onboarding.domain
|
||||
|
||||
import com.tangem.feature.onboarding.data.DummySeedPhraseRepository
|
||||
import com.tangem.feature.onboarding.data.SeedPhraseRepository
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.crypto.bip39.Mnemonic
|
||||
import com.tangem.crypto.bip39.MnemonicErrorResult
|
||||
import com.tangem.feature.onboarding.data.MnemonicRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface SeedPhraseInteractor : SeedPhraseChecker {
|
||||
suspend fun generateSeedPhrase(): List<String>
|
||||
interface SeedPhraseInteractor {
|
||||
suspend fun generateMnemonic(): Result<Mnemonic>
|
||||
suspend fun getMnemonicComponents(): Result<List<String>>
|
||||
suspend fun wordIsMatch(word: String): Boolean
|
||||
suspend fun validateMnemonicString(text: String): Result<List<String>>
|
||||
suspend fun getSuggestions(text: String, hasSelection: Boolean, cursorPosition: Int): List<String>
|
||||
suspend fun insertSuggestionWord(text: String, suggestion: String, cursorPosition: Int): InsertSuggestionResult
|
||||
|
||||
companion object {
|
||||
const val MNEMONIC_DELIMITER = " "
|
||||
}
|
||||
}
|
||||
|
||||
class DummySeedPhraseInteractor(
|
||||
private val sdkSeedPhraseRepository: SeedPhraseRepository = DummySeedPhraseRepository(),
|
||||
private val seedPhraseChecker: SeedPhraseChecker = DummySeedPhraseChecker(),
|
||||
) : SeedPhraseInteractor, SeedPhraseChecker by seedPhraseChecker {
|
||||
class DefaultSeedPhraseInteractor @Inject constructor(
|
||||
private val repository: MnemonicRepository,
|
||||
) : SeedPhraseInteractor {
|
||||
|
||||
override suspend fun generateSeedPhrase(): List<String> {
|
||||
return sdkSeedPhraseRepository.getWordList().words
|
||||
private var currentMnemonic: Mnemonic? = null
|
||||
|
||||
override suspend fun generateMnemonic(): Result<Mnemonic> {
|
||||
return try {
|
||||
currentMnemonic = repository.generateDefaultMnemonic()
|
||||
Result.success(currentMnemonic!!)
|
||||
} catch (ex: TangemSdkError.MnemonicException) {
|
||||
Result.failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getMnemonicComponents(): Result<List<String>> {
|
||||
return try {
|
||||
val mnemonic = currentMnemonic ?: repository.generateDefaultMnemonic().apply {
|
||||
currentMnemonic = this
|
||||
}
|
||||
Result.success(mnemonic.mnemonicComponents)
|
||||
} catch (ex: TangemSdkError.MnemonicException) {
|
||||
Result.failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun wordIsMatch(word: String): Boolean {
|
||||
return repository.getWordsDictionary().contains(word)
|
||||
}
|
||||
|
||||
override suspend fun validateMnemonicString(text: String): Result<List<String>> {
|
||||
val inputWords = text.split("\\s+".toRegex()).toSet()
|
||||
val wordsDictionary = repository.getWordsDictionary()
|
||||
|
||||
val invalidWords = inputWords
|
||||
.filter { it.isNotEmpty() && !wordsDictionary.contains(it) }
|
||||
.toSet()
|
||||
|
||||
return if (invalidWords.isNotEmpty()) {
|
||||
Result.failure(SeedPhraseError.InvalidWords(invalidWords))
|
||||
} else {
|
||||
try {
|
||||
val mnemonic = repository.createMnemonic(text)
|
||||
Result.success(mnemonic.mnemonicComponents)
|
||||
} catch (ex: TangemSdkError.MnemonicException) {
|
||||
val error = ex.mnemonicResult.mapToError()
|
||||
Result.failure(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSuggestions(
|
||||
text: String,
|
||||
hasSelection: Boolean,
|
||||
cursorPosition: Int,
|
||||
): List<String> {
|
||||
// TODO: todo
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun insertSuggestionWord(
|
||||
text: String,
|
||||
suggestion: String,
|
||||
cursorPosition: Int,
|
||||
): InsertSuggestionResult {
|
||||
// TODO: todo
|
||||
return InsertSuggestionResult("", 0)
|
||||
}
|
||||
}
|
||||
|
||||
data class InsertSuggestionResult(
|
||||
val text: String,
|
||||
val cursorPosition: Int,
|
||||
)
|
||||
|
||||
private fun MnemonicErrorResult.mapToError(): SeedPhraseError = when (this) {
|
||||
MnemonicErrorResult.InvalidWordCount -> SeedPhraseError.InvalidEntropyLength
|
||||
MnemonicErrorResult.InvalidEntropyLength -> SeedPhraseError.InvalidEntropyLength
|
||||
MnemonicErrorResult.InvalidWordsFile -> SeedPhraseError.InvalidWordsFile
|
||||
MnemonicErrorResult.InvalidChecksum -> SeedPhraseError.InvalidChecksum
|
||||
MnemonicErrorResult.MnenmonicCreationFailed -> SeedPhraseError.MnenmonicCreationFailed
|
||||
MnemonicErrorResult.NormalizationFailed -> SeedPhraseError.NormalizationFailed
|
||||
MnemonicErrorResult.UnsupportedLanguage -> SeedPhraseError.UnsupportedLanguage
|
||||
is MnemonicErrorResult.InvalidWords -> SeedPhraseError.InvalidWords(this.words)
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.feature.onboarding.domain.di
|
||||
|
||||
import com.tangem.feature.onboarding.data.MnemonicRepository
|
||||
import com.tangem.feature.onboarding.domain.DefaultSeedPhraseInteractor
|
||||
import com.tangem.feature.onboarding.domain.SeedPhraseInteractor
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class OnboardingDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSeedPhraseInteractor(repository: MnemonicRepository): SeedPhraseInteractor {
|
||||
return DefaultSeedPhraseInteractor(repository)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue