Updated on 2026-08-14
This commit is contained in:
commit
507169a2c3
492 changed files with 5734 additions and 12510 deletions
|
|
@ -18,10 +18,8 @@ import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
|||
import com.tangem.datasource.local.preferences.utils.storeObject
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.extensions.canHandleBlockchain
|
||||
import com.tangem.domain.common.extensions.canHandleToken
|
||||
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
|
|
@ -65,9 +63,12 @@ internal class DefaultCustomTokensRepository(
|
|||
contractAddress: String?,
|
||||
): Boolean {
|
||||
return withContext(dispatchers.io) {
|
||||
val storedCurrencies: UserTokensResponse = appPreferencesStore.getObjectSyncOrNull(
|
||||
val storedCurrencies = appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
||||
key = PreferencesKeys.getUserTokensKey(userWalletId.stringValue),
|
||||
) ?: error("User tokens not found")
|
||||
)
|
||||
requireNotNull(storedCurrencies) {
|
||||
"User tokens not found for user wallet [$userWalletId] while checking if currency is not added"
|
||||
}
|
||||
|
||||
storedCurrencies.tokens.none { token ->
|
||||
Blockchain.fromId(networkId.value).toNetworkId() == token.networkId &&
|
||||
|
|
@ -83,9 +84,12 @@ internal class DefaultCustomTokensRepository(
|
|||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): CryptoCurrency.Token? = withContext(dispatchers.io) {
|
||||
val userWallet = userWalletsStore.getSyncOrNull(userWalletId)
|
||||
?: error("User wallet not found")
|
||||
val network = getNetwork(networkId, derivationPath)
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"User wallet [$userWalletId] not found while finding token"
|
||||
}
|
||||
val network = requireNotNull(getNetwork(networkId, derivationPath, userWallet.scanResponse)) {
|
||||
"Network [$networkId] not found while finding token"
|
||||
}
|
||||
val tokenAddress = tokenAddressConverter.convertTokenAddress(
|
||||
networkId,
|
||||
contractAddress,
|
||||
|
|
@ -125,13 +129,22 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun createCoin(networkId: Network.ID, derivationPath: Network.DerivationPath): CryptoCurrency.Coin {
|
||||
val network = getNetwork(networkId, derivationPath)
|
||||
override suspend fun createCoin(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): CryptoCurrency.Coin {
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"User wallet [$userWalletId] not found while creating coin"
|
||||
}
|
||||
val network = requireNotNull(getNetwork(networkId, derivationPath, userWallet.scanResponse)) {
|
||||
"Network [$networkId] not found while creating coin"
|
||||
}
|
||||
|
||||
return cryptoCurrencyFactory.createCoin(network)
|
||||
}
|
||||
|
||||
override fun createToken(
|
||||
override suspend fun createToken(
|
||||
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
||||
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
||||
rawId: String?,
|
||||
|
|
@ -147,11 +160,17 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
|
||||
override suspend fun createCustomToken(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
formValues: AddCustomTokenForm.Validated.All,
|
||||
): CryptoCurrency.Token {
|
||||
val network = getNetwork(networkId, derivationPath)
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"User wallet [$userWalletId] not found while creating custom token"
|
||||
}
|
||||
val network = requireNotNull(getNetwork(networkId, derivationPath, userWallet.scanResponse)) {
|
||||
"Network [$networkId] not found while creating custom token"
|
||||
}
|
||||
val tokenAddress = tokenAddressConverter.convertTokenAddress(
|
||||
networkId,
|
||||
formValues.contractAddress,
|
||||
|
|
@ -171,7 +190,11 @@ internal class DefaultCustomTokensRepository(
|
|||
override suspend fun removeCurrency(userWalletId: UserWalletId, currency: ManagedCryptoCurrency.Custom) =
|
||||
withContext(dispatchers.io) {
|
||||
val cryptoCurrency = when (currency) {
|
||||
is ManagedCryptoCurrency.Custom.Coin -> createCoin(currency.network.id, currency.network.derivationPath)
|
||||
is ManagedCryptoCurrency.Custom.Coin -> createCoin(
|
||||
userWalletId = userWalletId,
|
||||
networkId = currency.network.id,
|
||||
derivationPath = currency.network.derivationPath,
|
||||
)
|
||||
is ManagedCryptoCurrency.Custom.Token -> cryptoCurrencyFactory.createToken(
|
||||
network = currency.network,
|
||||
rawId = currency.currencyId.rawCurrencyId,
|
||||
|
|
@ -181,15 +204,17 @@ internal class DefaultCustomTokensRepository(
|
|||
contractAddress = currency.contractAddress,
|
||||
)
|
||||
}
|
||||
|
||||
val savedCurrencies = requireNotNull(
|
||||
value = getSavedUserTokensResponseSync(key = userWalletId),
|
||||
lazyMessage = { "Saved tokens empty. Can not perform remove currency action" },
|
||||
val storedCurrencies = appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
||||
key = PreferencesKeys.getUserTokensKey(userWalletId.stringValue),
|
||||
)
|
||||
requireNotNull(storedCurrencies) {
|
||||
"User tokens not found for user wallet [$userWalletId] while removing currency"
|
||||
}
|
||||
|
||||
val token = userTokensResponseFactory.createResponseToken(cryptoCurrency)
|
||||
storeAndPushTokens(
|
||||
userWalletId = userWalletId,
|
||||
response = savedCurrencies.copy(tokens = savedCurrencies.tokens.filterNot { it == token }),
|
||||
response = storedCurrencies.copy(tokens = storedCurrencies.tokens.filterNot { it == token }),
|
||||
)
|
||||
when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> walletManagersFacade.remove(userWalletId, setOf(cryptoCurrency.network))
|
||||
|
|
@ -198,8 +223,9 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
|
||||
override suspend fun getSupportedNetworks(userWalletId: UserWalletId): List<Network> = withContext(dispatchers.io) {
|
||||
val userWallet = userWalletsStore.getSyncOrNull(userWalletId)
|
||||
?: error("User wallet not found")
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"User wallet [$userWalletId] not found while getting supported networks"
|
||||
}
|
||||
val scanResponse = userWallet.scanResponse
|
||||
|
||||
Blockchain.entries
|
||||
|
|
@ -208,9 +234,7 @@ internal class DefaultCustomTokensRepository(
|
|||
getNetwork(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = scanResponse.derivationStyleProvider,
|
||||
)?.copy(
|
||||
canHandleTokens = scanResponse.card.canHandleToken(blockchain, scanResponse.cardTypesResolver),
|
||||
scanResponse = scanResponse,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
@ -238,13 +262,7 @@ internal class DefaultCustomTokensRepository(
|
|||
|
||||
private suspend fun pushTokens(userWalletId: UserWalletId, response: UserTokensResponse) {
|
||||
safeApiCall({ tangemTechApi.saveUserTokens(userWalletId.stringValue, response).bind() }) {
|
||||
Timber.e(it, "Unable to save user tokens for: ${userWalletId.stringValue}")
|
||||
Timber.e(it, "Unable to push user tokens for: ${userWalletId.stringValue}")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getSavedUserTokensResponseSync(key: UserWalletId): UserTokensResponse? {
|
||||
return appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
||||
key = PreferencesKeys.getUserTokensKey(key.stringValue),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,10 +12,10 @@ import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
|
|||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.config.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.extensions.canHandleBlockchain
|
||||
|
|
@ -23,7 +23,6 @@ import com.tangem.domain.common.extensions.canHandleToken
|
|||
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||
import com.tangem.domain.common.extensions.supportedTokens
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.managetokens.model.*
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
|
|
@ -119,13 +118,13 @@ internal class DefaultManageTokensRepository(
|
|||
managedCryptoCurrencyFactory.createWithCustomTokens(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
} else {
|
||||
managedCryptoCurrencyFactory.create(
|
||||
coinsResponse = updatedCoinsResponse,
|
||||
tokensResponse = tokensResponse,
|
||||
derivationStyleProvider = userWallet?.scanResponse?.derivationStyleProvider,
|
||||
scanResponse = userWallet?.scanResponse,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +154,7 @@ internal class DefaultManageTokensRepository(
|
|||
testnetTokensConfig
|
||||
},
|
||||
tokensResponse = getSavedUserTokensResponseSync(userWallet.walletId),
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
|
||||
return BatchFetchResult.Success(
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import com.tangem.data.managetokens.DefaultCustomTokensRepository
|
|||
import com.tangem.data.managetokens.DefaultManageTokensRepository
|
||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.config.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
|
|
|
|||
|
|
@ -12,10 +12,14 @@ import com.tangem.data.common.currency.getNetwork
|
|||
import com.tangem.data.common.currency.getTokenId
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.extensions.canHandleToken
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
internal class ManagedCryptoCurrencyFactory {
|
||||
|
|
@ -23,20 +27,20 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
fun create(
|
||||
coinsResponse: CoinsResponse,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
derivationStyleProvider: DerivationStyleProvider?,
|
||||
scanResponse: ScanResponse?,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
return coinsResponse.coins.mapNotNull { coin ->
|
||||
createToken(coin, tokensResponse, coinsResponse.imageHost, derivationStyleProvider)
|
||||
createToken(coin, tokensResponse, coinsResponse.imageHost, scanResponse)
|
||||
}
|
||||
}
|
||||
|
||||
fun createWithCustomTokens(
|
||||
coinsResponse: CoinsResponse,
|
||||
tokensResponse: UserTokensResponse,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
scanResponse: ScanResponse,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val customTokens = createCustomTokens(tokensResponse, derivationStyleProvider)
|
||||
val tokens = create(coinsResponse, tokensResponse, derivationStyleProvider)
|
||||
val customTokens = createCustomTokens(tokensResponse, scanResponse)
|
||||
val tokens = create(coinsResponse, tokensResponse, scanResponse)
|
||||
|
||||
return customTokens + tokens
|
||||
}
|
||||
|
|
@ -44,10 +48,10 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
fun createTestnetWithCustomTokens(
|
||||
testnetTokensConfig: TestnetTokensConfig,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
scanResponse: ScanResponse,
|
||||
): List<ManagedCryptoCurrency> {
|
||||
val customTokens = tokensResponse
|
||||
?.let { createCustomTokens(it, derivationStyleProvider) }
|
||||
?.let { createCustomTokens(it, scanResponse) }
|
||||
?: emptyList()
|
||||
val testnetTokens = testnetTokensConfig.tokens.map { testnetToken ->
|
||||
ManagedCryptoCurrency.Token(
|
||||
|
|
@ -60,10 +64,10 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
networkId = network.id,
|
||||
contractAddress = network.address,
|
||||
decimals = network.decimalCount ?: return@mapNotNull null,
|
||||
derivationStyleProvider = derivationStyleProvider,
|
||||
scanResponse = scanResponse,
|
||||
)
|
||||
} ?: emptyList(),
|
||||
addedIn = findAddedInNetworks(testnetToken.id, tokensResponse, derivationStyleProvider),
|
||||
addedIn = findAddedInNetworks(testnetToken.id, tokensResponse, scanResponse),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -72,29 +76,29 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
|
||||
private fun createCustomTokens(
|
||||
tokensResponse: UserTokensResponse,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
scanResponse: ScanResponse,
|
||||
): List<ManagedCryptoCurrency> = tokensResponse.tokens
|
||||
.mapNotNull { token ->
|
||||
maybeCreateCustomToken(token, null, derivationStyleProvider)
|
||||
maybeCreateCustomToken(token, null, scanResponse)
|
||||
}
|
||||
|
||||
private fun maybeCreateCustomToken(
|
||||
token: UserTokensResponse.Token,
|
||||
imageHost: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
scanResponse: ScanResponse,
|
||||
): ManagedCryptoCurrency? {
|
||||
val blockchain = Blockchain.fromNetworkId(token.networkId)
|
||||
?.takeIf { it.isSupportedInApp() }
|
||||
?: return null
|
||||
|
||||
if (!checkIsCustomToken(token, blockchain, derivationStyleProvider)) {
|
||||
if (!checkIsCustomToken(token, blockchain, scanResponse.derivationStyleProvider)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val network = getNetwork(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
derivationStyleProvider = derivationStyleProvider,
|
||||
scanResponse = scanResponse,
|
||||
) ?: return null
|
||||
val contractAddress = token.contractAddress
|
||||
|
||||
|
|
@ -123,7 +127,7 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
coinResponse: CoinsResponse.Coin,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
imageHost: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider?,
|
||||
scanResponse: ScanResponse?,
|
||||
): ManagedCryptoCurrency? {
|
||||
if (coinResponse.networks.isEmpty() || !coinResponse.active) return null
|
||||
|
||||
|
|
@ -134,7 +138,7 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
networkId = network.networkId,
|
||||
contractAddress = network.contractAddress,
|
||||
decimals = network.decimalCount?.toInt(),
|
||||
derivationStyleProvider = derivationStyleProvider,
|
||||
scanResponse = scanResponse,
|
||||
)
|
||||
}
|
||||
.ifEmpty { return null }
|
||||
|
|
@ -153,14 +157,21 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
networkId: String,
|
||||
contractAddress: String?,
|
||||
decimals: Int?,
|
||||
derivationStyleProvider: DerivationStyleProvider?,
|
||||
scanResponse: ScanResponse?,
|
||||
extraDerivationPath: String? = null,
|
||||
): SourceNetwork? {
|
||||
val blockchain = Blockchain.fromNetworkId(networkId)
|
||||
?.takeIf { it.isSupportedInApp() }
|
||||
?: return null
|
||||
|
||||
val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null
|
||||
val network = getNetwork(
|
||||
blockchain,
|
||||
extraDerivationPath,
|
||||
scanResponse?.derivationStyleProvider,
|
||||
canHandleTokens = scanResponse?.let {
|
||||
it.card.canHandleToken(blockchain, it.cardTypesResolver)
|
||||
} ?: true,
|
||||
) ?: return null
|
||||
|
||||
return when {
|
||||
contractAddress.isNullOrBlank() -> SourceNetwork.Main(
|
||||
|
|
@ -180,7 +191,7 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
private fun findAddedInNetworks(
|
||||
currencyId: String,
|
||||
tokensResponse: UserTokensResponse?,
|
||||
derivationStyleProvider: DerivationStyleProvider?,
|
||||
scanResponse: ScanResponse?,
|
||||
): Set<Network> {
|
||||
if (tokensResponse == null) return emptySet()
|
||||
|
||||
|
|
@ -191,9 +202,12 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
|
||||
if (blockchain != null && blockchain.isSupportedInApp()) {
|
||||
getNetwork(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
derivationStyleProvider = derivationStyleProvider,
|
||||
blockchain,
|
||||
token.derivationPath,
|
||||
scanResponse?.derivationStyleProvider,
|
||||
canHandleTokens = scanResponse?.let {
|
||||
it.card.canHandleToken(blockchain, it.cardTypesResolver)
|
||||
} ?: true,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue