Updated on 2026-08-14
This commit is contained in:
parent
3e79411138
commit
297a006e3d
11 changed files with 351 additions and 1 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.managetokens.GetManagedTokensUseCase
|
||||
import com.tangem.domain.managetokens.*
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -17,4 +18,30 @@ internal object ManageTokensDomainModule {
|
|||
fun provideGetManageTokensUseCase(manageTokensRepository: ManageTokensRepository): GetManagedTokensUseCase {
|
||||
return GetManagedTokensUseCase(manageTokensRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideValidateTokenFormatUseCase(customTokensRepository: CustomTokensRepository): ValidateTokenFormUseCase {
|
||||
return ValidateTokenFormUseCase(customTokensRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCreateCurrencyUseCase(customTokensRepository: CustomTokensRepository): CreateCurrencyUseCase {
|
||||
return CreateCurrencyUseCase(customTokensRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFindTokenUseCase(customTokensRepository: CustomTokensRepository): FindTokenUseCase {
|
||||
return FindTokenUseCase(customTokensRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCheckIsCurrencyNotAddedUseCase(
|
||||
customTokensRepository: CustomTokensRepository,
|
||||
): CheckIsCurrencyNotAddedUseCase {
|
||||
return CheckIsCurrencyNotAddedUseCase(customTokensRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.tangem.data.managetokens
|
||||
|
||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal class DefaultCustomTokensRepository : CustomTokensRepository {
|
||||
|
||||
override suspend fun validateContractAddress(contractAddress: String, networkId: Network.ID): Boolean {
|
||||
TODO("Should be implemented in [REDACTED_JIRA]")
|
||||
}
|
||||
|
||||
override suspend fun isCurrencyNotAdded(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
contractAddress: String?,
|
||||
): Boolean {
|
||||
TODO("Should be implemented in [REDACTED_JIRA]")
|
||||
}
|
||||
|
||||
override suspend fun findToken(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): CryptoCurrency.Token? {
|
||||
TODO("Should be implemented in [REDACTED_JIRA]")
|
||||
}
|
||||
|
||||
override suspend fun createCoin(
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): CryptoCurrency.Coin {
|
||||
TODO("Should be implemented in [REDACTED_JIRA]")
|
||||
}
|
||||
|
||||
override suspend fun createCustomToken(
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
formValues: AddCustomTokenForm.Validated.All,
|
||||
): CryptoCurrency.Token {
|
||||
TODO("Should be implemented in [REDACTED_JIRA]")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
package com.tangem.data.managetokens.di
|
||||
|
||||
import com.tangem.data.managetokens.DefaultCustomTokensRepository
|
||||
import com.tangem.data.managetokens.DefaultManageTokensRepository
|
||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -37,4 +39,10 @@ internal object ManageTokensDataModule {
|
|||
dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCustomTokensRepository(): CustomTokensRepository {
|
||||
return DefaultCustomTokensRepository()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.domain.managetokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class CheckIsCurrencyNotAddedUseCase(
|
||||
private val repository: CustomTokensRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
contractAddress: String?,
|
||||
): Either<Throwable, Boolean> = Either.catch {
|
||||
repository.isCurrencyNotAdded(userWalletId, networkId, derivationPath, contractAddress)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.managetokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
class CreateCurrencyUseCase(
|
||||
private val repository: CustomTokensRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
formValues: AddCustomTokenForm.Validated.All?,
|
||||
): Either<Throwable, CryptoCurrency> = Either.catch {
|
||||
if (formValues == null) {
|
||||
repository.createCoin(networkId, derivationPath)
|
||||
} else {
|
||||
repository.createCustomToken(networkId, derivationPath, formValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.domain.managetokens
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensureNotNull
|
||||
import com.tangem.domain.managetokens.model.exceptoin.FindTokenException
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
class FindTokenUseCase(
|
||||
private val repository: CustomTokensRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): Either<FindTokenException, CryptoCurrency.Token> = either {
|
||||
val currency = findCurrency(
|
||||
userWalletId = userWalletId,
|
||||
contractAddress = contractAddress,
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
|
||||
ensureNotNull(currency) {
|
||||
FindTokenException.NotFound
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<FindTokenException>.findCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): CryptoCurrency.Token? = catch(
|
||||
block = {
|
||||
repository.findToken(
|
||||
userWalletId = userWalletId,
|
||||
contractAddress = contractAddress,
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
},
|
||||
catch = {
|
||||
raise(FindTokenException.DataError(it))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.tangem.domain.managetokens
|
||||
|
||||
import arrow.core.EitherNel
|
||||
import arrow.core.nonEmptyListOf
|
||||
import arrow.core.raise.*
|
||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
class ValidateTokenFormUseCase(
|
||||
private val repository: CustomTokensRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
networkId: Network.ID,
|
||||
formValues: AddCustomTokenForm.Raw,
|
||||
): EitherNel<CustomTokenFormValidationException, AddCustomTokenForm.Validated> = either {
|
||||
if (formValues.name.isBlank() && formValues.symbol.isBlank() && formValues.decimals.isBlank()) {
|
||||
val contractAddress = withError({ nonEmptyListOf(it) }) {
|
||||
ensureIsContractAddressValid(formValues.contractAddress, networkId)
|
||||
}
|
||||
|
||||
AddCustomTokenForm.Validated.ContractAddress(contractAddress)
|
||||
} else {
|
||||
zipOrAccumulate(
|
||||
{ ensureIsContractAddressValid(formValues.contractAddress, networkId) },
|
||||
{ ensureIsDecimalsValid(formValues.decimals) },
|
||||
{ ensure(formValues.name.isNotBlank()) { CustomTokenFormValidationException.EmptyName } },
|
||||
{ ensure(formValues.symbol.isNotBlank()) { CustomTokenFormValidationException.EmptySymbol } },
|
||||
) { contractAddress, decimals, _, _ ->
|
||||
AddCustomTokenForm.Validated.All(
|
||||
contractAddress = contractAddress,
|
||||
symbol = formValues.symbol,
|
||||
name = formValues.name,
|
||||
decimals = decimals,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<CustomTokenFormValidationException>.ensureIsContractAddressValid(
|
||||
contractAddress: String,
|
||||
networkId: Network.ID,
|
||||
): String {
|
||||
ensure(contractAddress.isNotBlank()) {
|
||||
CustomTokenFormValidationException.ContractAddress.Empty
|
||||
}
|
||||
|
||||
val isValid = catch({ repository.validateContractAddress(contractAddress, networkId) }) {
|
||||
raise(CustomTokenFormValidationException.DataError(it))
|
||||
}
|
||||
|
||||
ensure(isValid) {
|
||||
CustomTokenFormValidationException.ContractAddress.Invalid
|
||||
}
|
||||
|
||||
return contractAddress
|
||||
}
|
||||
|
||||
private fun Raise<CustomTokenFormValidationException>.ensureIsDecimalsValid(decimals: String): Int {
|
||||
ensure(condition = decimals.isNotBlank()) {
|
||||
CustomTokenFormValidationException.Decimals.Empty
|
||||
}
|
||||
|
||||
val decimalsInt = ensureNotNull(decimals.toIntOrNull()) {
|
||||
CustomTokenFormValidationException.Decimals.Invalid
|
||||
}
|
||||
ensure(condition = decimalsInt in MIN_DECIMALS..MAX_DECIMALS) {
|
||||
CustomTokenFormValidationException.Decimals.Invalid
|
||||
}
|
||||
|
||||
return decimalsInt
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MIN_DECIMALS = 1
|
||||
const val MAX_DECIMALS = 30
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.domain.managetokens.model
|
||||
|
||||
sealed class AddCustomTokenForm {
|
||||
|
||||
data class Raw(
|
||||
val contractAddress: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val decimals: String,
|
||||
) : AddCustomTokenForm()
|
||||
|
||||
sealed class Validated {
|
||||
|
||||
data class ContractAddress(
|
||||
val contractAddress: String,
|
||||
) : Validated()
|
||||
|
||||
data class All(
|
||||
val contractAddress: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val decimals: Int,
|
||||
) : Validated()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.managetokens.model.exceptoin
|
||||
|
||||
sealed class CustomTokenFormValidationException {
|
||||
|
||||
sealed class ContractAddress : CustomTokenFormValidationException() {
|
||||
|
||||
data object Empty : ContractAddress()
|
||||
|
||||
data object Invalid : ContractAddress()
|
||||
}
|
||||
|
||||
sealed class Decimals : CustomTokenFormValidationException() {
|
||||
|
||||
data object Empty : Decimals()
|
||||
|
||||
data object Invalid : Decimals()
|
||||
}
|
||||
|
||||
data object EmptyName : CustomTokenFormValidationException()
|
||||
|
||||
data object EmptySymbol : CustomTokenFormValidationException()
|
||||
|
||||
data class DataError(val cause: Throwable) : CustomTokenFormValidationException()
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.managetokens.model.exceptoin
|
||||
|
||||
sealed class FindTokenException {
|
||||
|
||||
data object NotFound : FindTokenException()
|
||||
|
||||
data class DataError(val cause: Throwable) : FindTokenException()
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.domain.managetokens.repository
|
||||
|
||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface CustomTokensRepository {
|
||||
|
||||
suspend fun validateContractAddress(contractAddress: String, networkId: Network.ID): Boolean
|
||||
|
||||
suspend fun isCurrencyNotAdded(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
contractAddress: String?,
|
||||
): Boolean
|
||||
|
||||
suspend fun findToken(
|
||||
userWalletId: UserWalletId,
|
||||
contractAddress: String,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): CryptoCurrency.Token?
|
||||
|
||||
suspend fun createCoin(networkId: Network.ID, derivationPath: Network.DerivationPath): CryptoCurrency.Coin
|
||||
|
||||
suspend fun createCustomToken(
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
formValues: AddCustomTokenForm.Validated.All,
|
||||
): CryptoCurrency.Token
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue