Updated on 2026-08-14
This commit is contained in:
parent
68f451d926
commit
8643eb4a90
10 changed files with 256 additions and 30 deletions
|
|
@ -5,9 +5,14 @@ import com.tangem.data.common.converter.ExpressProviderConverter
|
||||||
import com.tangem.data.txhistory.repository.converter.ExpressStatusMapper
|
import com.tangem.data.txhistory.repository.converter.ExpressStatusMapper
|
||||||
import com.tangem.data.txhistory.repository.converter.ExpressOnrampConverter
|
import com.tangem.data.txhistory.repository.converter.ExpressOnrampConverter
|
||||||
import com.tangem.data.txhistory.repository.converter.ExpressSwapConverter
|
import com.tangem.data.txhistory.repository.converter.ExpressSwapConverter
|
||||||
|
import com.tangem.data.txhistory.repository.factory.ExpressTransactionAssetFactory
|
||||||
|
import com.tangem.data.txhistory.repository.factory.toAssetId
|
||||||
import com.tangem.data.txhistory.repository.paging.TxHistoryPageBatchFetcher
|
import com.tangem.data.txhistory.repository.paging.TxHistoryPageBatchFetcher
|
||||||
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
|
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
|
||||||
import com.tangem.datasource.local.txhistory.db.dao.ExpressHistoryDao
|
import com.tangem.datasource.local.txhistory.db.dao.ExpressHistoryDao
|
||||||
|
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressExchangeEntity
|
||||||
|
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressOnrampEntity
|
||||||
|
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressProviderEntity
|
||||||
import com.tangem.domain.express.models.ExpressAsset
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.TxInfo
|
import com.tangem.domain.models.network.TxInfo
|
||||||
|
|
@ -36,6 +41,7 @@ internal class RefactoredTxHistoryRepository @Inject constructor(
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val walletManagersFacade: WalletManagersFacade,
|
||||||
private val txHistoryItemsStore: TxHistoryItemsStore,
|
private val txHistoryItemsStore: TxHistoryItemsStore,
|
||||||
private val expressHistoryDao: ExpressHistoryDao,
|
private val expressHistoryDao: ExpressHistoryDao,
|
||||||
|
private val expressTransactionAssetFactory: ExpressTransactionAssetFactory,
|
||||||
private val cacheRegistry: CacheRegistry,
|
private val cacheRegistry: CacheRegistry,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : TxHistoryRepositoryV2 {
|
) : TxHistoryRepositoryV2 {
|
||||||
|
|
@ -85,37 +91,67 @@ internal class RefactoredTxHistoryRepository @Inject constructor(
|
||||||
).distinctUntilChanged(),
|
).distinctUntilChanged(),
|
||||||
flow4 = expressHistoryDao.getProvidersById().distinctUntilChanged(),
|
flow4 = expressHistoryDao.getProvidersById().distinctUntilChanged(),
|
||||||
transform = { outgoingSwaps, incomingSwaps, onramps, providers ->
|
transform = { outgoingSwaps, incomingSwaps, onramps, providers ->
|
||||||
buildList<ExpressTx> {
|
buildExpressHistory(
|
||||||
fun String.expressProvider() = providers[this]?.let(expressProviderConverter::convert)
|
userWalletId = userWalletId,
|
||||||
outgoingSwaps.forEach { entity ->
|
outgoingSwaps = outgoingSwaps,
|
||||||
val input = ExpressSwapConverter.Input(
|
incomingSwaps = incomingSwaps,
|
||||||
entity = entity,
|
onramps = onramps,
|
||||||
provider = entity.providerId.expressProvider(),
|
providers = providers,
|
||||||
isOutgoing = true,
|
)
|
||||||
)
|
|
||||||
add(swapConverter.convert(input))
|
|
||||||
}
|
|
||||||
incomingSwaps.forEach { entity ->
|
|
||||||
val input = ExpressSwapConverter.Input(
|
|
||||||
entity = entity,
|
|
||||||
provider = entity.providerId.expressProvider(),
|
|
||||||
isOutgoing = false,
|
|
||||||
)
|
|
||||||
add(swapConverter.convert(input))
|
|
||||||
}
|
|
||||||
onramps.forEach { entity ->
|
|
||||||
val input = ExpressOnrampConverter.Input(entity, entity.providerId.expressProvider())
|
|
||||||
add(onrampConverter.convert(input))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// An exchange row may satisfy both swap queries only in degenerate cases;
|
|
||||||
// keep the outgoing interpretation (added first).
|
|
||||||
.distinctBy { it.txId }
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
emitAll(flow)
|
emitAll(flow)
|
||||||
}.flowOn(dispatchers.io)
|
}.flowOn(dispatchers.io)
|
||||||
|
|
||||||
|
private suspend fun buildExpressHistory(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
outgoingSwaps: List<ExpressExchangeEntity>,
|
||||||
|
incomingSwaps: List<ExpressExchangeEntity>,
|
||||||
|
onramps: List<ExpressOnrampEntity>,
|
||||||
|
providers: Map<String, ExpressProviderEntity>,
|
||||||
|
): List<ExpressTx> {
|
||||||
|
val currencies = expressTransactionAssetFactory.create(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
outgoingSwaps = outgoingSwaps,
|
||||||
|
incomingSwaps = incomingSwaps,
|
||||||
|
onramps = onramps,
|
||||||
|
)
|
||||||
|
fun String.expressProvider() = providers[this]?.let(expressProviderConverter::convert)
|
||||||
|
return buildList {
|
||||||
|
outgoingSwaps.forEach { entity ->
|
||||||
|
val input = ExpressSwapConverter.Input(
|
||||||
|
entity = entity,
|
||||||
|
provider = entity.providerId.expressProvider(),
|
||||||
|
isOutgoing = true,
|
||||||
|
fromCurrency = currencies[entity.from.toAssetId()],
|
||||||
|
toCurrency = currencies[entity.to.toAssetId()],
|
||||||
|
)
|
||||||
|
add(swapConverter.convert(input))
|
||||||
|
}
|
||||||
|
incomingSwaps.forEach { entity ->
|
||||||
|
val input = ExpressSwapConverter.Input(
|
||||||
|
entity = entity,
|
||||||
|
provider = entity.providerId.expressProvider(),
|
||||||
|
isOutgoing = false,
|
||||||
|
fromCurrency = currencies[entity.from.toAssetId()],
|
||||||
|
toCurrency = currencies[entity.to.toAssetId()],
|
||||||
|
)
|
||||||
|
add(swapConverter.convert(input))
|
||||||
|
}
|
||||||
|
onramps.forEach { entity ->
|
||||||
|
val input = ExpressOnrampConverter.Input(
|
||||||
|
entity = entity,
|
||||||
|
provider = entity.providerId.expressProvider(),
|
||||||
|
toCurrency = currencies[entity.to.toAssetId()],
|
||||||
|
)
|
||||||
|
add(onrampConverter.convert(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// An exchange row may satisfy both swap queries only in degenerate cases;
|
||||||
|
// keep the outgoing interpretation (added first).
|
||||||
|
.distinctBy { it.txId }
|
||||||
|
}
|
||||||
|
|
||||||
override fun getTxHistoryBatchFlow(batchSize: Int, context: TxHistoryListBatchingContext): TxHistoryListBatchFlow {
|
override fun getTxHistoryBatchFlow(batchSize: Int, context: TxHistoryListBatchingContext): TxHistoryListBatchFlow {
|
||||||
return BatchListSource(
|
return BatchListSource(
|
||||||
fetchDispatcher = dispatchers.io,
|
fetchDispatcher = dispatchers.io,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.express.models.ExpressOnrampStatus
|
||||||
import com.tangem.domain.express.models.ExpressProvider
|
import com.tangem.domain.express.models.ExpressProvider
|
||||||
import com.tangem.domain.express.models.ExpressTransactionAsset
|
import com.tangem.domain.express.models.ExpressTransactionAsset
|
||||||
import com.tangem.domain.express.models.OnrampTransaction
|
import com.tangem.domain.express.models.OnrampTransaction
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.Amount
|
import com.tangem.domain.tokens.model.Amount
|
||||||
import com.tangem.domain.tokens.model.AmountType
|
import com.tangem.domain.tokens.model.AmountType
|
||||||
import com.tangem.domain.txhistory.model.ExpressTx
|
import com.tangem.domain.txhistory.model.ExpressTx
|
||||||
|
|
@ -26,7 +27,7 @@ import java.math.BigDecimal
|
||||||
internal class ExpressSwapConverter : Converter<ExpressSwapConverter.Input, ExpressTx.Swap> {
|
internal class ExpressSwapConverter : Converter<ExpressSwapConverter.Input, ExpressTx.Swap> {
|
||||||
|
|
||||||
override fun convert(value: Input): ExpressTx.Swap = ExpressTx.Swap(
|
override fun convert(value: Input): ExpressTx.Swap = ExpressTx.Swap(
|
||||||
tx = convertExchangeTransaction(value.entity, value.provider),
|
tx = convertExchangeTransaction(value),
|
||||||
isOutgoing = value.isOutgoing,
|
isOutgoing = value.isOutgoing,
|
||||||
txInfo = null,
|
txInfo = null,
|
||||||
)
|
)
|
||||||
|
|
@ -35,6 +36,8 @@ internal class ExpressSwapConverter : Converter<ExpressSwapConverter.Input, Expr
|
||||||
val entity: ExpressExchangeEntity,
|
val entity: ExpressExchangeEntity,
|
||||||
val provider: ExpressProvider?,
|
val provider: ExpressProvider?,
|
||||||
val isOutgoing: Boolean,
|
val isOutgoing: Boolean,
|
||||||
|
val fromCurrency: CryptoCurrency? = null,
|
||||||
|
val toCurrency: CryptoCurrency? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,32 +62,40 @@ internal class ExpressOnrampConverter : Converter<ExpressOnrampConverter.Input,
|
||||||
id = ExpressAssetId(networkId = entity.to.network, contractAddress = entity.to.contractAddress),
|
id = ExpressAssetId(networkId = entity.to.network, contractAddress = entity.to.contractAddress),
|
||||||
amount = (entity.to.actualAmount ?: entity.to.amount).toBigDecimalOrZero(),
|
amount = (entity.to.actualAmount ?: entity.to.amount).toBigDecimalOrZero(),
|
||||||
decimals = entity.to.decimals,
|
decimals = entity.to.decimals,
|
||||||
|
cryptoCurrency = value.toCurrency,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
txInfo = null,
|
txInfo = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Input(val entity: ExpressOnrampEntity, val provider: ExpressProvider?)
|
data class Input(
|
||||||
|
val entity: ExpressOnrampEntity,
|
||||||
|
val provider: ExpressProvider?,
|
||||||
|
val toCurrency: CryptoCurrency? = null,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertExchangeTransaction(entity: ExpressExchangeEntity, provider: ExpressProvider?): ExchangeTransaction {
|
private fun convertExchangeTransaction(value: ExpressSwapConverter.Input): ExchangeTransaction {
|
||||||
|
val entity = value.entity
|
||||||
return ExchangeTransaction(
|
return ExchangeTransaction(
|
||||||
txId = entity.txId,
|
txId = entity.txId,
|
||||||
status = ExpressExchangeStatus.fromRaw(entity.status),
|
status = ExpressExchangeStatus.fromRaw(entity.status),
|
||||||
createdAtMillis = parseIsoMillis(entity.createdAt),
|
createdAtMillis = parseIsoMillis(entity.createdAt),
|
||||||
provider = provider,
|
provider = value.provider,
|
||||||
payinHash = entity.payinHash,
|
payinHash = entity.payinHash,
|
||||||
payoutHash = entity.payoutHash,
|
payoutHash = entity.payoutHash,
|
||||||
fromAsset = ExpressTransactionAsset(
|
fromAsset = ExpressTransactionAsset(
|
||||||
id = ExpressAssetId(networkId = entity.from.network, contractAddress = entity.from.contractAddress),
|
id = ExpressAssetId(networkId = entity.from.network, contractAddress = entity.from.contractAddress),
|
||||||
amount = entity.from.amount.toBigDecimalOrZero(),
|
amount = entity.from.amount.toBigDecimalOrZero(),
|
||||||
decimals = entity.from.decimals,
|
decimals = entity.from.decimals,
|
||||||
|
cryptoCurrency = value.fromCurrency,
|
||||||
),
|
),
|
||||||
toAsset = ExpressTransactionAsset(
|
toAsset = ExpressTransactionAsset(
|
||||||
id = ExpressAssetId(networkId = entity.to.network, contractAddress = entity.to.contractAddress),
|
id = ExpressAssetId(networkId = entity.to.network, contractAddress = entity.to.contractAddress),
|
||||||
amount = (entity.to.actualAmount ?: entity.to.amount).toBigDecimalOrZero(),
|
amount = (entity.to.actualAmount ?: entity.to.amount).toBigDecimalOrZero(),
|
||||||
decimals = entity.to.decimals,
|
decimals = entity.to.decimals,
|
||||||
|
cryptoCurrency = value.toCurrency,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
package com.tangem.data.txhistory.repository.factory
|
||||||
|
|
||||||
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
|
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||||
|
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressExchangeEntity
|
||||||
|
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressOnrampEntity
|
||||||
|
import com.tangem.domain.account.supplier.MultiAccountListSupplier
|
||||||
|
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||||
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a portfolio [CryptoCurrency] for every express asset (network id + contract address) referenced by a
|
||||||
|
* batch of exchange/onramp entities.
|
||||||
|
*
|
||||||
|
* Strategy: read every account of every wallet ONCE (via [MultiAccountListSupplier]) and match each express asset
|
||||||
|
* against the flattened portfolio currencies by network id + contract address. When nothing matches — notably
|
||||||
|
* tokens that are not present in any portfolio — a coin is built for the asset's network as a fallback (for now).
|
||||||
|
*/
|
||||||
|
internal class ExpressTransactionAssetFactory @Inject constructor(
|
||||||
|
private val multiAccountListSupplier: MultiAccountListSupplier,
|
||||||
|
private val userWalletsListRepository: UserWalletsListRepository,
|
||||||
|
excludedBlockchains: ExcludedBlockchains,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a `assetId -> resolved currency` map covering both legs of every swap and the to-leg of every onramp.
|
||||||
|
* Entries whose currency could not be resolved at all (no match and no fallback coin) are omitted.
|
||||||
|
*/
|
||||||
|
suspend fun create(
|
||||||
|
userWalletId: UserWalletId,
|
||||||
|
outgoingSwaps: List<ExpressExchangeEntity>,
|
||||||
|
incomingSwaps: List<ExpressExchangeEntity>,
|
||||||
|
onramps: List<ExpressOnrampEntity>,
|
||||||
|
): Map<ExpressAsset.ID, CryptoCurrency> {
|
||||||
|
val assetIds = buildSet {
|
||||||
|
(outgoingSwaps + incomingSwaps).forEach { entity ->
|
||||||
|
add(entity.from.toAssetId())
|
||||||
|
add(entity.to.toAssetId())
|
||||||
|
}
|
||||||
|
onramps.forEach { entity -> add(entity.to.toAssetId()) }
|
||||||
|
}
|
||||||
|
if (assetIds.isEmpty()) return emptyMap()
|
||||||
|
|
||||||
|
val portfolioCurrencies = multiAccountListSupplier.invoke()
|
||||||
|
.first()
|
||||||
|
.flatMap { accountList -> accountList.flattenCurrencies() }
|
||||||
|
|
||||||
|
val userWallet = userWalletsListRepository.userWalletsSync()
|
||||||
|
.firstOrNull { it.walletId == userWalletId }
|
||||||
|
|
||||||
|
return buildMap {
|
||||||
|
assetIds.forEach { id ->
|
||||||
|
val currency = portfolioCurrencies.findMatching(id) ?: createFallbackCoin(id, userWallet)
|
||||||
|
if (currency != null) put(id, currency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<CryptoCurrency>.findMatching(id: ExpressAsset.ID): CryptoCurrency? {
|
||||||
|
val isCoin = id.contractAddress == ExpressAsset.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||||
|
return firstOrNull { currency ->
|
||||||
|
currency.network.rawId == id.networkId &&
|
||||||
|
if (isCoin) {
|
||||||
|
currency is CryptoCurrency.Coin
|
||||||
|
} else {
|
||||||
|
currency is CryptoCurrency.Token &&
|
||||||
|
currency.contractAddress.equals(id.contractAddress, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO txHistory: tokens that are not in any portfolio cannot be resolved yet — fall back to a coin on the asset's
|
||||||
|
// network.
|
||||||
|
private fun createFallbackCoin(id: ExpressAsset.ID, userWallet: UserWallet?): CryptoCurrency.Coin? {
|
||||||
|
userWallet ?: return null
|
||||||
|
return cryptoCurrencyFactory.createCoin(
|
||||||
|
networkId = id.networkId,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
userWallet = userWallet,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ExpressExchangeEntity.AssetEmbedded.toAssetId(): ExpressAsset.ID =
|
||||||
|
ExpressAsset.ID(networkId = network, contractAddress = contractAddress)
|
||||||
|
|
||||||
|
internal fun ExpressOnrampEntity.AssetEmbedded.toAssetId(): ExpressAsset.ID =
|
||||||
|
ExpressAsset.ID(networkId = network, contractAddress = contractAddress)
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.tangem.data.walletmanager.utils
|
||||||
|
|
||||||
|
import com.tangem.domain.models.network.SdkAmount
|
||||||
|
import com.tangem.domain.models.network.SdkAmountType
|
||||||
|
import com.tangem.blockchain.common.Amount as BlockchainAmount
|
||||||
|
import com.tangem.blockchain.common.AmountType as BlockchainAmountType
|
||||||
|
|
||||||
|
/** Maps the blockchain SDK [BlockchainAmount] to the serializable domain [SdkAmount]. */
|
||||||
|
internal fun BlockchainAmount.toDomain(): SdkAmount = SdkAmount(
|
||||||
|
currencySymbol = currencySymbol,
|
||||||
|
value = value,
|
||||||
|
decimals = decimals,
|
||||||
|
type = type.toDomain(),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun BlockchainAmountType.toDomain(): SdkAmountType = when (this) {
|
||||||
|
BlockchainAmountType.Coin -> SdkAmountType.Coin
|
||||||
|
BlockchainAmountType.Reserve -> SdkAmountType.Reserve
|
||||||
|
is BlockchainAmountType.FeeResource -> SdkAmountType.FeeResource(name = name)
|
||||||
|
is BlockchainAmountType.Token -> SdkAmountType.Token(contractAddress = token.contractAddress, id = token.id)
|
||||||
|
is BlockchainAmountType.TokenYieldSupply -> SdkAmountType.Token(
|
||||||
|
contractAddress = token.contractAddress,
|
||||||
|
id = token.id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ internal class SdkTransactionHistoryItemConverter(
|
||||||
},
|
},
|
||||||
type = typeConverter.convert(value),
|
type = typeConverter.convert(value),
|
||||||
amount = requireNotNull(value.amount.value) { "Transaction amount value must not be null" },
|
amount = requireNotNull(value.amount.value) { "Transaction amount value must not be null" },
|
||||||
|
fee = value.fee.toDomain(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun SdkTransactionHistoryItem.SourceType.toDomain(): TxInfo.SourceType = when (this) {
|
private fun SdkTransactionHistoryItem.SourceType.toDomain(): TxInfo.SourceType = when (this) {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ internal class TransactionDataToTxHistoryItemConverter(
|
||||||
},
|
},
|
||||||
type = getTransactionType(value),
|
type = getTransactionType(value),
|
||||||
amount = amount,
|
amount = amount,
|
||||||
|
fee = value.fee?.amount?.toDomain(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,6 @@ plugins {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(deps.moshi.adapters)
|
implementation(deps.moshi.adapters)
|
||||||
implementation(deps.kotlin.serialization)
|
implementation(deps.kotlin.serialization)
|
||||||
|
implementation(projects.domain.models)
|
||||||
implementation(projects.domain.tokens.models)
|
implementation(projects.domain.tokens.models)
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.domain.express.models
|
package com.tangem.domain.express.models
|
||||||
|
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -8,9 +9,12 @@ import java.math.BigDecimal
|
||||||
* @property id The asset identifier (network id + contract address).
|
* @property id The asset identifier (network id + contract address).
|
||||||
* @property amount Human-readable amount (already scaled by [decimals]).
|
* @property amount Human-readable amount (already scaled by [decimals]).
|
||||||
* @property decimals The asset's decimals.
|
* @property decimals The asset's decimals.
|
||||||
|
* @property cryptoCurrency The portfolio [CryptoCurrency] this asset was resolved to (matched by network id +
|
||||||
|
* contract address across all accounts). `null` when no portfolio currency matched and no fallback could be built.
|
||||||
*/
|
*/
|
||||||
data class ExpressTransactionAsset(
|
data class ExpressTransactionAsset(
|
||||||
val id: ExpressAsset.ID,
|
val id: ExpressAsset.ID,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
val decimals: Int,
|
val decimals: Int,
|
||||||
|
val cryptoCurrency: CryptoCurrency? = null,
|
||||||
)
|
)
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.tangem.domain.models.network
|
||||||
|
|
||||||
|
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domain mirror of the blockchain SDK `Amount`, kept [Serializable] so it can be carried inside the serializable
|
||||||
|
* [TxInfo] graph (the SDK `Amount` is not serializable and pulls in blockchain-specific types).
|
||||||
|
*
|
||||||
|
* Holds a monetary value together with the metadata needed to display it. Compared to the SDK model it drops
|
||||||
|
* `maxValue` (irrelevant outside of "send" flows) and keeps only the currency identity on [SdkAmountType].
|
||||||
|
*
|
||||||
|
* @property currencySymbol display symbol of the currency (e.g. `ETH`, `USDT`)
|
||||||
|
* @property value amount value; `null` when the value is unknown
|
||||||
|
* @property decimals number of decimals of the currency
|
||||||
|
* @property type kind of currency the amount is denominated in
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class SdkAmount(
|
||||||
|
val currencySymbol: String,
|
||||||
|
val value: SerializedBigDecimal? = null,
|
||||||
|
val decimals: Int,
|
||||||
|
val type: SdkAmountType = SdkAmountType.Coin,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Kind of currency an [SdkAmount] is denominated in. Mirrors the SDK `AmountType`. */
|
||||||
|
@Serializable
|
||||||
|
sealed interface SdkAmountType {
|
||||||
|
|
||||||
|
/** Native coin of the blockchain. */
|
||||||
|
@Serializable
|
||||||
|
data object Coin : SdkAmountType
|
||||||
|
|
||||||
|
/** Native coin used as a reserve currency for fee calculation (e.g. Algorand). */
|
||||||
|
@Serializable
|
||||||
|
data object Reserve : SdkAmountType
|
||||||
|
|
||||||
|
/** A resource that can be spent to pay the fee (e.g. Mana on Koinos). */
|
||||||
|
@Serializable
|
||||||
|
data class FeeResource(val name: String? = null) : SdkAmountType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A token of the blockchain.
|
||||||
|
*
|
||||||
|
* @property contractAddress token contract address
|
||||||
|
* @property id backend currency id, when known
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Token(val contractAddress: String, val id: String? = null) : SdkAmountType
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable
|
||||||
* @property status transaction status
|
* @property status transaction status
|
||||||
* @property type transaction type
|
* @property type transaction type
|
||||||
* @property amount transaction amount
|
* @property amount transaction amount
|
||||||
|
* @property fee transaction fee
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TxInfo(
|
data class TxInfo(
|
||||||
|
|
@ -27,6 +28,7 @@ data class TxInfo(
|
||||||
val status: TransactionStatus,
|
val status: TransactionStatus,
|
||||||
val type: TransactionType,
|
val type: TransactionType,
|
||||||
val amount: SerializedBigDecimal,
|
val amount: SerializedBigDecimal,
|
||||||
|
val fee: SdkAmount? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/** Destination type*/
|
/** Destination type*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue