diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 8fb1513e6c..2ad63bea82 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -1,5 +1,9 @@ + This token already exists + + The entered contract address is present in Nova as a %s token. Are you sure you want to modify it? + For security reasons generated operations valid for only %s.\nPlease generate new QR code and sign it with %s Invalid QR code, please make sure you are scanning QR code from %s I have an error in %s @@ -356,7 +360,6 @@ Add token - This token already exist The entered contract address is present in Nova as a %s token. Invalid contract address diff --git a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/AddTokensInteractor.kt b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/AddTokensInteractor.kt index d1f1941e04..1d28de365d 100644 --- a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/AddTokensInteractor.kt +++ b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/AddTokensInteractor.kt @@ -4,7 +4,7 @@ import io.novafoundation.nova.common.address.format.EthereumAddressFormat import io.novafoundation.nova.common.validation.ValidationSystem import io.novafoundation.nova.feature_assets.domain.tokens.add.validations.AddEvmTokenValidationSystem import io.novafoundation.nova.feature_assets.domain.tokens.add.validations.CoinGeckoLinkValidationFactory -import io.novafoundation.nova.feature_assets.domain.tokens.add.validations.evmAssetNotExist +import io.novafoundation.nova.feature_assets.domain.tokens.add.validations.evmAssetNotExists import io.novafoundation.nova.feature_assets.domain.tokens.add.validations.validCoinGeckoLink import io.novafoundation.nova.feature_assets.domain.tokens.add.validations.validErc20Contract import io.novafoundation.nova.feature_assets.domain.tokens.add.validations.validTokenDecimals @@ -94,7 +94,7 @@ class RealAddTokensInteractor( override fun getValidationSystem(): AddEvmTokenValidationSystem { return ValidationSystem { - evmAssetNotExist(chainAssetRepository) + evmAssetNotExists(chainRegistry) validErc20Contract(ethereumAddressFormat, erc20Standard, chainRegistry) validTokenDecimals() validCoinGeckoLink(coinGeckoLinkValidationFactory) diff --git a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/validations/AddEvmTokensValidatoins.kt b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/validations/AddEvmTokensValidatoins.kt index 8d5e4ee610..b571e94b12 100644 --- a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/validations/AddEvmTokensValidatoins.kt +++ b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/tokens/add/validations/AddEvmTokensValidatoins.kt @@ -4,8 +4,7 @@ import io.novafoundation.nova.common.address.format.EthereumAddressFormat import io.novafoundation.nova.common.validation.ValidationSystem import io.novafoundation.nova.common.validation.ValidationSystemBuilder import io.novafoundation.nova.feature_assets.domain.tokens.add.CustomErc20Token -import io.novafoundation.nova.feature_wallet_api.domain.interfaces.ChainAssetRepository -import io.novafoundation.nova.feature_wallet_api.domain.validation.evmAssetNotExist +import io.novafoundation.nova.feature_wallet_api.domain.validation.evmAssetNotExists import io.novafoundation.nova.feature_wallet_api.domain.validation.validErc20Contract import io.novafoundation.nova.feature_wallet_api.domain.validation.validTokenDecimals import io.novafoundation.nova.runtime.ethereum.contract.erc20.Erc20Standard @@ -18,7 +17,7 @@ typealias AddEvmTokenValidationSystemBuilder = ValidationSystemBuilder { - resourceManager.getString(R.string.asset_add_evm_token_already_exist_title) to + val title = resourceManager.getString(R.string.asset_add_evm_token_already_exist_title) + val message = if (failure.canModify) { + resourceManager.getString(R.string.asset_add_evm_token_already_exist_modifiable_message, failure.alreadyExistingSymbol) + } else { resourceManager.getString(R.string.asset_add_evm_token_already_exist_message, failure.alreadyExistingSymbol) + } + + title to message } is AddEvmTokensValidationFailure.InvalidTokenContractAddress -> { resourceManager.getString(R.string.asset_add_evm_token_invalid_contract_address_title) to diff --git a/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/interfaces/ChainAssetRepository.kt b/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/interfaces/ChainAssetRepository.kt index d14a929283..09f2768517 100644 --- a/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/interfaces/ChainAssetRepository.kt +++ b/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/interfaces/ChainAssetRepository.kt @@ -9,7 +9,5 @@ interface ChainAssetRepository { suspend fun insertCustomAsset(chainAsset: Chain.Asset) - suspend fun getAssetSymbol(id: FullChainAssetId): String? - suspend fun getEnabledAssets(): List } diff --git a/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/validation/EvmAssetExistenceValidation.kt b/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/validation/EvmAssetExistenceValidation.kt index d2905160bc..a99a51f5fe 100644 --- a/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/validation/EvmAssetExistenceValidation.kt +++ b/feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/domain/validation/EvmAssetExistenceValidation.kt @@ -5,18 +5,21 @@ import io.novafoundation.nova.common.validation.ValidationStatus import io.novafoundation.nova.common.validation.ValidationSystemBuilder import io.novafoundation.nova.common.validation.valid import io.novafoundation.nova.common.validation.validationError -import io.novafoundation.nova.feature_wallet_api.domain.interfaces.ChainAssetRepository +import io.novafoundation.nova.common.validation.validationWarning +import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry +import io.novafoundation.nova.runtime.multiNetwork.assetOrNull import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.chainAssetIdOfErc20Token import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain +import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain.Asset.Source import io.novafoundation.nova.runtime.multiNetwork.chain.model.FullChainAssetId -typealias AssetNotExistError = (existingSymbol: String) -> E +typealias AssetNotExistError = (existingSymbol: String, canModify: Boolean) -> E class EvmAssetExistenceValidation( - private val assetRepository: ChainAssetRepository, + private val chainRegistry: ChainRegistry, private val chain: (P) -> Chain, private val contractAddress: (P) -> String, - private val assetNotExistError: AssetNotExistError, + private val assetAlreadyExists: AssetNotExistError, private val addressMappingError: (P) -> E, ) : Validation { @@ -24,12 +27,13 @@ class EvmAssetExistenceValidation( return try { val assetId = chainAssetIdOfErc20Token(contractAddress(value)) val fullAssetId = FullChainAssetId(chain(value).id, assetId) - val alreadyExistingSymbol = assetRepository.getAssetSymbol(fullAssetId) + val alreadyExistingAsset = chainRegistry.assetOrNull(fullAssetId) - if (alreadyExistingSymbol != null) { - validationError(assetNotExistError(alreadyExistingSymbol)) - } else { - valid() + when { + alreadyExistingAsset == null -> valid() + // we only allow to modify manually added tokens. Default tokens should remain unchanged + alreadyExistingAsset.source == Source.MANUAL -> assetAlreadyExists(alreadyExistingAsset.symbol, true).validationWarning() + else -> assetAlreadyExists(alreadyExistingAsset.symbol, false).validationError() } } catch (e: Exception) { validationError(addressMappingError(value)) @@ -37,18 +41,18 @@ class EvmAssetExistenceValidation( } } -fun ValidationSystemBuilder.evmAssetNotExist( - assetRepository: ChainAssetRepository, +fun ValidationSystemBuilder.evmAssetNotExists( + chainRegistry: ChainRegistry, chain: (P) -> Chain, address: (P) -> String, assetNotExistError: AssetNotExistError, addressMappingError: (P) -> E ) = validate( EvmAssetExistenceValidation( - assetRepository = assetRepository, + chainRegistry = chainRegistry, chain = chain, contractAddress = address, - assetNotExistError = assetNotExistError, + assetAlreadyExists = assetNotExistError, addressMappingError = addressMappingError ) ) diff --git a/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/repository/RealChainAssetRepository.kt b/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/repository/RealChainAssetRepository.kt index b1f370c6b2..7a3b59d868 100644 --- a/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/repository/RealChainAssetRepository.kt +++ b/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/repository/RealChainAssetRepository.kt @@ -24,12 +24,6 @@ class RealChainAssetRepository( chainAssetDao.insertAsset(localAsset) } - override suspend fun getAssetSymbol(id: FullChainAssetId): String? { - val existingAsset = chainAssetDao.getAsset(id.assetId, id.chainId) - - return existingAsset?.symbol - } - override suspend fun getEnabledAssets(): List { return chainAssetDao.getEnabledAssets().map { mapChainAssetLocalToAsset(it, gson) } } diff --git a/runtime/src/main/java/io/novafoundation/nova/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/io/novafoundation/nova/runtime/multiNetwork/ChainRegistry.kt index c4038a97b2..1a95f53a47 100644 --- a/runtime/src/main/java/io/novafoundation/nova/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/io/novafoundation/nova/runtime/multiNetwork/ChainRegistry.kt @@ -141,6 +141,12 @@ suspend fun ChainRegistry.chainWithAssetOrNull(chainId: String, assetId: Int): C return ChainWithAsset(chain, chainAsset) } +suspend fun ChainRegistry.assetOrNull(fullChainAssetId: FullChainAssetId): Chain.Asset? { + val chain = getChainOrNull(fullChainAssetId.chainId) ?: return null + + return chain.assetsById[fullChainAssetId.assetId] +} + suspend fun ChainRegistry.chainWithAsset(chainId: String, assetId: Int): ChainWithAsset { val chain = chainsById.first().getValue(chainId)