Updated on 2026-08-14
This commit is contained in:
parent
d3dbc2c3d0
commit
1a4405fd28
5 changed files with 382 additions and 16 deletions
|
|
@ -41,7 +41,9 @@ class UserTokensSaver(
|
||||||
return@withContext
|
return@withContext
|
||||||
}
|
}
|
||||||
|
|
||||||
val enrichedResponse = response.enrichIf(userWalletId = userWalletId, condition = useEnricher)
|
val enrichedResponse = response
|
||||||
|
.withoutDuplicates()
|
||||||
|
.enrichIf(userWalletId = userWalletId, condition = useEnricher)
|
||||||
|
|
||||||
push(userWallet = userWallet, response = enrichedResponse, onFailSend = onFailSend)
|
push(userWallet = userWallet, response = enrichedResponse, onFailSend = onFailSend)
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +87,46 @@ class UserTokensSaver(
|
||||||
apiResponse.bind()
|
apiResponse.bind()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError = { onFailSend() },
|
onError = { error ->
|
||||||
|
TangemLogger.e("Failed to push ${response.tokens.size} user tokens", error)
|
||||||
|
onFailSend()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops fully identical tokens from the list.
|
||||||
|
*
|
||||||
|
* The API rejects the whole list with `400 All tokens's elements must be unique` if it contains two equal
|
||||||
|
* elements, so a single duplicate discards the update of the entire wallet's token list. Duplicates are not
|
||||||
|
* expected here, hence the error log.
|
||||||
|
*
|
||||||
|
* Tokens are compared by every field of the request instead of [UserTokensResponse.Token.equals], which
|
||||||
|
* deliberately ignores most of them: dropping an element that differs in any way would silently change what the
|
||||||
|
* user has saved.
|
||||||
|
*/
|
||||||
|
private fun UserTokensResponse.withoutDuplicates(): UserTokensResponse {
|
||||||
|
val uniqueTokens = tokens.distinctBy { it.toRequestKey() }
|
||||||
|
|
||||||
|
if (uniqueTokens.size == tokens.size) return this
|
||||||
|
|
||||||
|
TangemLogger.e("Dropped ${tokens.size - uniqueTokens.size} duplicated tokens before pushing them")
|
||||||
|
|
||||||
|
return copy(tokens = uniqueTokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun UserTokensResponse.Token.toRequestKey(): List<Any?> {
|
||||||
|
return listOf(
|
||||||
|
id,
|
||||||
|
accountId,
|
||||||
|
networkId,
|
||||||
|
derivationPath,
|
||||||
|
name,
|
||||||
|
symbol,
|
||||||
|
decimals,
|
||||||
|
contractAddress,
|
||||||
|
addresses,
|
||||||
|
dynamicAddressesEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,4 +95,75 @@ class UserTokensSaverTest {
|
||||||
|
|
||||||
assert(onFailSendCalled) { "onFailSend callback should be called when API call fails" }
|
assert(onFailSendCalled) { "onFailSend callback should be called when API call fails" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN response with duplicated tokens WHEN push THEN duplicates are dropped before the api call`() = runTest {
|
||||||
|
// GIVEN
|
||||||
|
val userWalletId = UserWalletId("1234567890abcdef")
|
||||||
|
val userWallet = mockk<UserWallet.Cold> {
|
||||||
|
every { this@mockk.walletId } returns userWalletId
|
||||||
|
every { this@mockk.name } returns ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val token = createToken()
|
||||||
|
val response = createResponse(tokens = listOf(token, token))
|
||||||
|
val uniqueResponse = createResponse(tokens = listOf(token))
|
||||||
|
|
||||||
|
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(userWallet))
|
||||||
|
coEvery { enricher(userWalletId, uniqueResponse) } returns uniqueResponse
|
||||||
|
coEvery { tangemTechApi.saveTokens(any(), any()) } returns ApiResponse.Success(Unit)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
userTokensSaver.push(userWalletId = userWalletId, response = response)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
coVerify(exactly = 1) { tangemTechApi.saveTokens(userWalletId.stringValue, uniqueResponse) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN tokens differing in account id only WHEN push THEN both of them are pushed`() = runTest {
|
||||||
|
// GIVEN
|
||||||
|
val userWalletId = UserWalletId("1234567890abcdef")
|
||||||
|
val userWallet = mockk<UserWallet.Cold> {
|
||||||
|
every { this@mockk.walletId } returns userWalletId
|
||||||
|
every { this@mockk.name } returns ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val token = createToken()
|
||||||
|
val response = createResponse(tokens = listOf(token, token.copy(accountId = "other-account")))
|
||||||
|
|
||||||
|
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(userWallet))
|
||||||
|
coEvery { enricher(userWalletId, response) } returns response
|
||||||
|
coEvery { tangemTechApi.saveTokens(any(), any()) } returns ApiResponse.Success(Unit)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
userTokensSaver.push(userWalletId = userWalletId, response = response)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
coVerify(exactly = 1) { tangemTechApi.saveTokens(userWalletId.stringValue, response) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createResponse(tokens: List<UserTokensResponse.Token>): UserTokensResponse {
|
||||||
|
return UserTokensResponse(
|
||||||
|
version = 0,
|
||||||
|
group = UserTokensResponse.GroupType.NETWORK,
|
||||||
|
sort = UserTokensResponse.SortType.BALANCE,
|
||||||
|
tokens = tokens,
|
||||||
|
walletName = null,
|
||||||
|
walletType = WalletType.COLD,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createToken(): UserTokensResponse.Token {
|
||||||
|
return UserTokensResponse.Token(
|
||||||
|
id = "ethereum",
|
||||||
|
accountId = "account",
|
||||||
|
networkId = "ethereum",
|
||||||
|
derivationPath = "m/44'/60'/0'/0/1",
|
||||||
|
name = "Ethereum",
|
||||||
|
symbol = "ETH",
|
||||||
|
decimals = 18,
|
||||||
|
contractAddress = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,11 +20,13 @@ import com.tangem.domain.common.wallets.getSyncStrict
|
||||||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||||
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.wallets.config.curvesConfig
|
import com.tangem.domain.wallets.config.curvesConfig
|
||||||
|
import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
|
||||||
import com.tangem.lib.crypto.derivation.supportsDerivationPath
|
import com.tangem.lib.crypto.derivation.supportsDerivationPath
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
@ -139,11 +141,7 @@ internal class DefaultCustomTokensRepository(
|
||||||
): CryptoCurrency.Coin {
|
): CryptoCurrency.Coin {
|
||||||
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
||||||
val network = requireNotNull(
|
val network = requireNotNull(
|
||||||
networkFactory.create(
|
createNetwork(networkId = networkId, derivationPath = derivationPath, userWallet = userWallet),
|
||||||
networkId = networkId,
|
|
||||||
derivationPath = derivationPath,
|
|
||||||
userWallet = userWallet,
|
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
"Network [$networkId] not found while creating coin"
|
"Network [$networkId] not found while creating coin"
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +149,38 @@ internal class DefaultCustomTokensRepository(
|
||||||
return cryptoCurrencyFactory.createCoin(network)
|
return cryptoCurrencyFactory.createCoin(network)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a [Network] for the given [derivationPath].
|
||||||
|
*
|
||||||
|
* A derivation path typed in by the user always arrives as [Network.DerivationPath.Custom], even when it is the
|
||||||
|
|
||||||
|
* currency shares its network with the currencies already present in the account instead of getting a separate
|
||||||
|
* "custom" network for the very same address.
|
||||||
|
*/
|
||||||
|
private fun createNetwork(
|
||||||
|
networkId: Network.ID,
|
||||||
|
derivationPath: Network.DerivationPath,
|
||||||
|
userWallet: UserWallet,
|
||||||
|
): Network? {
|
||||||
|
val pathValue = derivationPath.value
|
||||||
|
val blockchain = networkId.toBlockchain()
|
||||||
|
|
||||||
|
val accountIndex = pathValue
|
||||||
|
?.let { AccountNodeRecognizer(blockchain).recognize(derivationPathValue = it) }
|
||||||
|
?.let { DerivationIndex(value = it.toInt()).getOrNull() }
|
||||||
|
|
||||||
|
return if (accountIndex == null) {
|
||||||
|
networkFactory.create(networkId = networkId, derivationPath = derivationPath, userWallet = userWallet)
|
||||||
|
} else {
|
||||||
|
networkFactory.create(
|
||||||
|
blockchain = blockchain,
|
||||||
|
extraDerivationPath = pathValue,
|
||||||
|
userWallet = userWallet,
|
||||||
|
accountIndex = accountIndex,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun createToken(
|
override suspend fun createToken(
|
||||||
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
||||||
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
||||||
|
|
@ -174,11 +204,7 @@ internal class DefaultCustomTokensRepository(
|
||||||
): CryptoCurrency.Token {
|
): CryptoCurrency.Token {
|
||||||
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
|
||||||
val network = requireNotNull(
|
val network = requireNotNull(
|
||||||
networkFactory.create(
|
createNetwork(networkId = networkId, derivationPath = derivationPath, userWallet = userWallet),
|
||||||
networkId = networkId,
|
|
||||||
derivationPath = derivationPath,
|
|
||||||
userWallet = userWallet,
|
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
"Network [$networkId] not found while creating custom token"
|
"Network [$networkId] not found while creating custom token"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,27 +381,35 @@ class ManageCryptoCurrenciesUseCase(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identity of a currency inside an account.
|
||||||
|
*
|
||||||
|
* The derivation path is compared by its raw value, not by the [Network.DerivationPath] subtype: the same path
|
||||||
|
* may be represented as [Network.DerivationPath.Card] for one currency and as [Network.DerivationPath.Custom]
|
||||||
|
* for another one, while both describe the very same address. Comparing the subtypes made such currencies look
|
||||||
|
|
||||||
|
*/
|
||||||
private data class TempID(
|
private data class TempID(
|
||||||
val networkId: String,
|
val networkId: String,
|
||||||
val derivationPath: Network.DerivationPath,
|
val derivationPath: String?,
|
||||||
val contractAddress: String?,
|
val contractAddress: String?,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
constructor(network: Network) : this(
|
constructor(network: Network) : this(
|
||||||
networkId = network.rawId,
|
networkId = network.rawId,
|
||||||
derivationPath = network.derivationPath,
|
derivationPath = network.derivationPath.value,
|
||||||
contractAddress = null,
|
contractAddress = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
constructor(currency: CryptoCurrency) : this(
|
constructor(currency: CryptoCurrency) : this(
|
||||||
networkId = currency.network.rawId,
|
networkId = currency.network.rawId,
|
||||||
derivationPath = currency.network.derivationPath,
|
derivationPath = currency.network.derivationPath.value,
|
||||||
contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress,
|
contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress,
|
||||||
)
|
)
|
||||||
|
|
||||||
constructor(status: CryptoCurrencyStatus) : this(
|
constructor(status: CryptoCurrencyStatus) : this(
|
||||||
networkId = status.currency.network.rawId,
|
networkId = status.currency.network.rawId,
|
||||||
derivationPath = status.currency.network.derivationPath,
|
derivationPath = status.currency.network.derivationPath.value,
|
||||||
contractAddress = (status.currency as? CryptoCurrency.Token)?.contractAddress,
|
contractAddress = (status.currency as? CryptoCurrency.Token)?.contractAddress,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,220 @@
|
||||||
|
package com.tangem.domain.account.status.usecase
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth
|
||||||
|
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||||
|
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||||
|
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||||
|
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||||
|
import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner
|
||||||
|
import com.tangem.domain.account.status.utils.createStatus
|
||||||
|
import com.tangem.domain.account.status.utils.createUngrouped
|
||||||
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
|
import com.tangem.domain.models.TotalFiatBalance
|
||||||
|
import com.tangem.domain.models.account.*
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.domain.models.network.Network
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||||
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||||
|
import com.tangem.test.core.TestAppCoroutineScope
|
||||||
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.mockk
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
internal class ManageCryptoCurrenciesUseCaseTest {
|
||||||
|
|
||||||
|
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier = mockk()
|
||||||
|
private val accountsCRUDRepository: AccountsCRUDRepository = mockk(relaxUnitFun = true)
|
||||||
|
private val currenciesRepository: CurrenciesRepository = mockk(relaxUnitFun = true)
|
||||||
|
private val derivationsRepository: DerivationsRepository = mockk(relaxUnitFun = true)
|
||||||
|
private val walletManagersFacade: WalletManagersFacade = mockk(relaxed = true)
|
||||||
|
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher = mockk(relaxed = true)
|
||||||
|
private val cryptoCurrencyMetadataCleaner: CryptoCurrencyMetadataCleaner = mockk(relaxed = true)
|
||||||
|
private val expressServiceFetcher: ExpressServiceFetcher = mockk(relaxed = true)
|
||||||
|
|
||||||
|
private val useCase = ManageCryptoCurrenciesUseCase(
|
||||||
|
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||||
|
accountsCRUDRepository = accountsCRUDRepository,
|
||||||
|
currenciesRepository = currenciesRepository,
|
||||||
|
derivationsRepository = derivationsRepository,
|
||||||
|
walletManagersFacade = walletManagersFacade,
|
||||||
|
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||||
|
cryptoCurrencyMetadataCleaner = cryptoCurrencyMetadataCleaner,
|
||||||
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
|
parallelUpdatingScope = TestAppCoroutineScope(),
|
||||||
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun resetMocks() {
|
||||||
|
clearMocks(
|
||||||
|
singleAccountStatusListSupplier,
|
||||||
|
accountsCRUDRepository,
|
||||||
|
currenciesRepository,
|
||||||
|
derivationsRepository,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN account coin has card derivation WHEN add token with the same custom derivation THEN coin is not duplicated`() =
|
||||||
|
runTest {
|
||||||
|
// Arrange
|
||||||
|
val coin = createCoin(derivationPath = Network.DerivationPath.Card(ACCOUNT_DERIVATION_PATH))
|
||||||
|
val account = createAccount(currencies = listOf(coin))
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
singleAccountStatusListSupplier.getSyncOrNull(SingleAccountStatusListProducer.Params(userWalletId))
|
||||||
|
} returns createAccountStatusList(account = account, currencies = listOf(coin))
|
||||||
|
|
||||||
|
// the derivation path typed in by the user always arrives as a custom one
|
||||||
|
val addedToken = createToken(derivationPath = Network.DerivationPath.Custom(ACCOUNT_DERIVATION_PATH))
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = useCase(accountId = account.accountId, add = addedToken, skipDerivationErrors = false)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Truth.assertThat(actual.isRight()).isTrue()
|
||||||
|
|
||||||
|
coVerify(exactly = 1) {
|
||||||
|
accountsCRUDRepository.saveAccount(account.copy(cryptoCurrencies = listOf(coin, addedToken)))
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) { currenciesRepository.createCoinCurrency(any()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN account without coin WHEN add token THEN coin is created`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val account = createAccount(currencies = emptyList())
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
singleAccountStatusListSupplier.getSyncOrNull(SingleAccountStatusListProducer.Params(userWalletId))
|
||||||
|
} returns createAccountStatusList(account = account, currencies = emptyList())
|
||||||
|
|
||||||
|
val addedToken = createToken(derivationPath = Network.DerivationPath.Custom(ACCOUNT_DERIVATION_PATH))
|
||||||
|
val createdCoin = createCoin(derivationPath = Network.DerivationPath.Custom(ACCOUNT_DERIVATION_PATH))
|
||||||
|
|
||||||
|
coEvery { currenciesRepository.createCoinCurrency(addedToken.network) } returns createdCoin
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = useCase(accountId = account.accountId, add = addedToken, skipDerivationErrors = false)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Truth.assertThat(actual.isRight()).isTrue()
|
||||||
|
|
||||||
|
coVerify(exactly = 1) {
|
||||||
|
accountsCRUDRepository.saveAccount(account.copy(cryptoCurrencies = listOf(createdCoin, addedToken)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAccount(currencies: List<CryptoCurrency>): Account.CryptoPortfolio {
|
||||||
|
return Account.CryptoPortfolio(
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = derivationIndex),
|
||||||
|
accountName = AccountName("Account 1").getOrNull()!!,
|
||||||
|
icon = CryptoPortfolioIcon.ofCustomAccount(
|
||||||
|
value = CryptoPortfolioIcon.Icon.Star,
|
||||||
|
color = CryptoPortfolioIcon.Color.Azure,
|
||||||
|
),
|
||||||
|
derivationIndex = derivationIndex,
|
||||||
|
cryptoCurrencies = currencies,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAccountStatusList(
|
||||||
|
account: Account.CryptoPortfolio,
|
||||||
|
currencies: List<CryptoCurrency>,
|
||||||
|
): com.tangem.domain.account.models.AccountStatusList {
|
||||||
|
val statuses = currencies.map { createStatus(currency = it, fiatAmount = BigDecimal.ONE) }
|
||||||
|
|
||||||
|
return com.tangem.domain.account.models.AccountStatusList(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountStatuses = listOf(
|
||||||
|
AccountStatus.CryptoPortfolio(
|
||||||
|
account = account,
|
||||||
|
tokenList = createUngrouped(statuses = statuses),
|
||||||
|
priceChangeLce = com.tangem.domain.core.utils.lceLoading(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
totalAccounts = 1,
|
||||||
|
totalArchivedAccounts = 0,
|
||||||
|
totalFiatBalance = TotalFiatBalance.Loading,
|
||||||
|
sortType = com.tangem.domain.models.TokensSortType.NONE,
|
||||||
|
groupType = com.tangem.domain.models.TokensGroupType.NONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNetwork(derivationPath: Network.DerivationPath): Network {
|
||||||
|
return Network(
|
||||||
|
id = Network.ID(value = NETWORK_ID, derivationPath = derivationPath),
|
||||||
|
name = "Ethereum",
|
||||||
|
isTestnet = false,
|
||||||
|
derivationPath = derivationPath,
|
||||||
|
currencySymbol = "ETH",
|
||||||
|
standardType = Network.StandardType.ERC20,
|
||||||
|
hasFiatFeeRate = true,
|
||||||
|
canHandleTokens = true,
|
||||||
|
transactionExtrasType = Network.TransactionExtrasType.NONE,
|
||||||
|
nameResolvingType = Network.NameResolvingType.NONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCoin(derivationPath: Network.DerivationPath): CryptoCurrency.Coin {
|
||||||
|
val network = createNetwork(derivationPath)
|
||||||
|
|
||||||
|
return CryptoCurrency.Coin(
|
||||||
|
id = CryptoCurrency.ID(
|
||||||
|
prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
|
||||||
|
body = CryptoCurrency.ID.Body.NetworkIdWithDerivationPath(
|
||||||
|
rawId = NETWORK_ID,
|
||||||
|
derivationPath = ACCOUNT_DERIVATION_PATH,
|
||||||
|
),
|
||||||
|
suffix = CryptoCurrency.ID.Suffix.RawID(rawId = NETWORK_ID),
|
||||||
|
),
|
||||||
|
network = network,
|
||||||
|
name = "Ethereum",
|
||||||
|
symbol = "ETH",
|
||||||
|
decimals = 18,
|
||||||
|
iconUrl = null,
|
||||||
|
isCustom = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createToken(derivationPath: Network.DerivationPath): CryptoCurrency.Token {
|
||||||
|
val network = createNetwork(derivationPath)
|
||||||
|
|
||||||
|
return CryptoCurrency.Token(
|
||||||
|
id = CryptoCurrency.ID(
|
||||||
|
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
|
||||||
|
body = CryptoCurrency.ID.Body.NetworkIdWithDerivationPath(
|
||||||
|
rawId = NETWORK_ID,
|
||||||
|
derivationPath = ACCOUNT_DERIVATION_PATH,
|
||||||
|
),
|
||||||
|
suffix = CryptoCurrency.ID.Suffix.ContractAddress(contractAddress = CONTRACT_ADDRESS),
|
||||||
|
),
|
||||||
|
network = network,
|
||||||
|
name = "Custom token",
|
||||||
|
symbol = "CST",
|
||||||
|
decimals = 6,
|
||||||
|
iconUrl = null,
|
||||||
|
contractAddress = CONTRACT_ADDRESS,
|
||||||
|
isCustom = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
val userWalletId = UserWalletId("011")
|
||||||
|
val derivationIndex = DerivationIndex(value = 1).getOrNull()!!
|
||||||
|
|
||||||
|
const val NETWORK_ID = "ethereum"
|
||||||
|
const val ACCOUNT_DERIVATION_PATH = "m/44'/60'/0'/0/1"
|
||||||
|
const val CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue