Updated on 2026-08-14
This commit is contained in:
commit
0cf428b94a
706 changed files with 12779 additions and 5939 deletions
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.datasource.api.common.visa
|
||||
|
||||
interface TangemVisaAuthProvider {
|
||||
|
||||
suspend fun getAuthHeader(cardId: String): String
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.datasource.api.express.models.response
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.joda.time.DateTime
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ExchangeStatusResponse(
|
||||
|
|
@ -26,6 +27,12 @@ data class ExchangeStatusResponse(
|
|||
|
||||
@Json(name = "refundContractAddress")
|
||||
val refundContractAddress: String? = null,
|
||||
|
||||
@Json(name = "createdAt")
|
||||
val createdAt: DateTime? = null,
|
||||
|
||||
@Json(name = "averageDuration")
|
||||
val averageDuration: Int? = null,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ import com.tangem.datasource.api.utils.ReadTimeout
|
|||
import com.tangem.datasource.api.visa.models.request.ActivationByCardWalletRequest
|
||||
import com.tangem.datasource.api.visa.models.request.ActivationByCustomerWalletRequest
|
||||
import com.tangem.datasource.api.visa.models.request.SetPinCodeRequest
|
||||
import com.tangem.datasource.api.visa.models.response.CardActivationRemoteStateResponse
|
||||
import com.tangem.datasource.api.visa.models.response.CardWalletDataToSignResponse
|
||||
import com.tangem.datasource.api.visa.models.response.CustomerWalletDataToSignResponse
|
||||
import com.tangem.datasource.api.visa.models.response.*
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
|
|
@ -71,4 +69,19 @@ interface TangemVisaApi {
|
|||
@Header("Authorization") authHeader: String,
|
||||
@Body body: SetPinCodeRequest,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@GET("customer/info")
|
||||
suspend fun getCustomerInfo(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Query("card_id") cardId: String,
|
||||
): ApiResponse<VisaCustomerInfo>
|
||||
|
||||
@GET("product_instance/transactions")
|
||||
suspend fun getTxHistory(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Query("customer_id") customerId: String,
|
||||
@Query("product_instance_id") productInstanceId: String,
|
||||
@Query("limit") limit: Int,
|
||||
@Query("offset") offset: Int,
|
||||
): ApiResponse<VisaTxHistoryResponse>
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.datasource.api.visa
|
||||
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.visa.models.response.GenerateNonceResponse
|
||||
import com.tangem.datasource.api.visa.models.response.JWTResponse
|
||||
import retrofit2.http.Field
|
||||
|
|
@ -27,5 +28,5 @@ interface TangemVisaAuthApi {
|
|||
): JWTResponse
|
||||
|
||||
@POST("auth/refresh_token")
|
||||
suspend fun refreshAccessToken(@Field("refresh_token") refreshToken: String): JWTResponse
|
||||
suspend fun refreshAccessToken(@Field("refresh_token") refreshToken: String): ApiResponse<JWTResponse>
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.datasource.api.visa.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class VisaCustomerInfo(
|
||||
@Json(name = "payment_accounts") val paymentAccounts: List<PaymentAccount>,
|
||||
) {
|
||||
data class PaymentAccount(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||
@Json(name = "payment_account_address") val paymentAccountAddress: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package com.tangem.datasource.api.visa.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class VisaTxHistoryResponse(
|
||||
@Json(name = "card_wallet_address")
|
||||
val cardWalletAddress: String,
|
||||
@Json(name = "transactions")
|
||||
val transactions: List<Transaction>,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Transaction(
|
||||
@Json(name = "auth_code")
|
||||
val authCode: String?,
|
||||
@Json(name = "billing_amount")
|
||||
val billingAmount: BigDecimal,
|
||||
@Json(name = "billing_currency_code")
|
||||
val billingCurrencyCode: Int,
|
||||
@Json(name = "blockchain_amount")
|
||||
val blockchainAmount: BigDecimal,
|
||||
@Json(name = "blockchain_coin_name")
|
||||
val blockchainCoinName: String,
|
||||
@Json(name = "blockchain_fee")
|
||||
val blockchainFee: BigDecimal,
|
||||
// @Json(name = "local_dt")
|
||||
// val localDate: DateTime?,
|
||||
@Json(name = "merchant_category_code")
|
||||
val merchantCategoryCode: String?,
|
||||
@Json(name = "merchant_city")
|
||||
val merchantCity: String?,
|
||||
@Json(name = "merchant_country_code")
|
||||
val merchantCountryCode: String?,
|
||||
@Json(name = "merchant_name")
|
||||
val merchantName: String?,
|
||||
@Json(name = "requests")
|
||||
val requests: List<Request>,
|
||||
@Json(name = "rrn")
|
||||
val rrn: String?,
|
||||
@Json(name = "transaction_amount")
|
||||
val transactionAmount: BigDecimal,
|
||||
@Json(name = "transaction_currency_code")
|
||||
val transactionCurrencyCode: Int,
|
||||
@Json(name = "transaction_dt")
|
||||
val transactionDt: DateTime,
|
||||
@Json(name = "transaction_id")
|
||||
val transactionId: Long,
|
||||
@Json(name = "transaction_status")
|
||||
val transactionStatus: String,
|
||||
@Json(name = "transaction_type")
|
||||
val transactionType: String,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Request(
|
||||
@Json(name = "billing_amount")
|
||||
val billingAmount: BigDecimal,
|
||||
@Json(name = "billing_currency_code")
|
||||
val billingCurrencyCode: Int,
|
||||
@Json(name = "blockchain_amount")
|
||||
val blockchainAmount: BigDecimal,
|
||||
@Json(name = "blockchain_fee")
|
||||
val blockchainFee: BigDecimal,
|
||||
@Json(name = "error_code")
|
||||
val errorCode: Int,
|
||||
@Json(name = "request_dt")
|
||||
val requestDt: DateTime,
|
||||
@Json(name = "request_status")
|
||||
val requestStatus: String,
|
||||
@Json(name = "request_type")
|
||||
val requestType: String,
|
||||
@Json(name = "transaction_amount")
|
||||
val transactionAmount: BigDecimal,
|
||||
@Json(name = "transaction_currency_code")
|
||||
val transactionCurrencyCode: Int,
|
||||
@Json(name = "transaction_request_id")
|
||||
val transactionRequestId: Long,
|
||||
@Json(name = "tx_hash")
|
||||
val txHash: String?,
|
||||
@Json(name = "tx_status")
|
||||
val txStatus: String?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package com.tangem.datasource.di
|
|||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.blockchain.nft.models.NFTAsset
|
||||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.datasource.api.common.adapter.BigDecimalAdapter
|
||||
import com.tangem.datasource.api.common.adapter.DateTimeAdapter
|
||||
|
|
@ -45,6 +47,18 @@ class MoshiModule {
|
|||
.withSubtype(NetworkStatusDM.Verified::class.java, "amounts")
|
||||
.withSubtype(NetworkStatusDM.NoAccount::class.java, "amount_to_create_account"),
|
||||
)
|
||||
.add(
|
||||
PolymorphicJsonAdapterFactory.of(NFTCollection.Identifier::class.java, "bc")
|
||||
.withSubtype(NFTCollection.Identifier.EVM::class.java, "evm")
|
||||
.withSubtype(NFTCollection.Identifier.TON::class.java, "ton")
|
||||
.withDefaultValue(NFTCollection.Identifier.Unknown),
|
||||
)
|
||||
.add(
|
||||
PolymorphicJsonAdapterFactory.of(NFTAsset.Identifier::class.java, "bc")
|
||||
.withSubtype(NFTAsset.Identifier.EVM::class.java, "evm")
|
||||
.withSubtype(NFTAsset.Identifier.TON::class.java, "ton")
|
||||
.withDefaultValue(NFTAsset.Identifier.Unknown),
|
||||
)
|
||||
.addLast(KotlinJsonAdapterFactory())
|
||||
.addStakeKitEnumFallbackAdapters()
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ class EnvironmentConfigModel(
|
|||
@Json(name = "bittensorOnfinalityKey") val bittensorOnfinalityKey: String?,
|
||||
@Json(name = "koinosProApiKey") val koinosProApiKey: String?,
|
||||
@Json(name = "alephiumTangemApiKey") val alephiumTangemApiKey: String?,
|
||||
@Json(name = "moralisApiKey") val moralisApiKey: String?,
|
||||
@Json(name = "nftScanApiKey") val nftScanApiKey: String?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.datasource.local.nft
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import com.tangem.blockchain.nft.models.NFTAsset
|
||||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class DefaultNFTPersistenceStore(
|
||||
private val collectionsPersistenceStore: DataStore<List<NFTCollection>>,
|
||||
private val pricesPersistenceStore: DataStore<Map<NFTAsset.Identifier, NFTAsset.SalePrice>>,
|
||||
) : NFTPersistenceStore {
|
||||
|
||||
override fun getCollections(): Flow<List<NFTCollection>> = collectionsPersistenceStore.data
|
||||
|
||||
override suspend fun getCollectionsSync(): List<NFTCollection>? = collectionsPersistenceStore
|
||||
.data
|
||||
.firstOrNull()
|
||||
|
||||
override fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset?> =
|
||||
collectionsPersistenceStore.data
|
||||
.map { collection ->
|
||||
collection
|
||||
.firstOrNull { it.identifier == collectionId }
|
||||
?.getAsset(assetId)
|
||||
}
|
||||
|
||||
override fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTAsset.SalePrice?> = pricesPersistenceStore.data
|
||||
.map { it[assetId] }
|
||||
|
||||
override suspend fun getSalePricesSync(): Map<NFTAsset.Identifier, NFTAsset.SalePrice>? = pricesPersistenceStore
|
||||
.data
|
||||
.firstOrNull()
|
||||
|
||||
override suspend fun saveCollections(collections: List<NFTCollection>) {
|
||||
collectionsPersistenceStore.updateData {
|
||||
collections
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun saveSalePrice(assetId: NFTAsset.Identifier, salePrice: NFTAsset.SalePrice) {
|
||||
pricesPersistenceStore.updateData {
|
||||
it.toMutableMap().apply { this[assetId] = salePrice }
|
||||
}
|
||||
}
|
||||
|
||||
private fun NFTCollection.getAsset(assetId: NFTAsset.Identifier) = assets.firstOrNull { it.identifier == assetId }
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package com.tangem.datasource.local.nft
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.domain.nft.models.NFTCollections
|
||||
import com.tangem.domain.nft.models.NFTSalePrice
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class DefaultNFTRuntimeStore(
|
||||
private val network: Network,
|
||||
private val collectionsRuntimeStore: RuntimeSharedStore<NFTCollections>,
|
||||
private val pricesRuntimeStore: RuntimeSharedStore<Map<NFTAsset.Identifier, NFTSalePrice>>,
|
||||
) : NFTRuntimeStore {
|
||||
|
||||
override suspend fun initialize(collections: NFTCollections, prices: Map<NFTAsset.Identifier, NFTSalePrice>) {
|
||||
collectionsRuntimeStore.store(collections)
|
||||
pricesRuntimeStore.store(prices)
|
||||
}
|
||||
|
||||
override fun getCollections(): Flow<NFTCollections> = collectionsRuntimeStore
|
||||
.get()
|
||||
.combine(pricesRuntimeStore.get()) { collectionsData, prices ->
|
||||
collectionsData.mergeWithPrices(prices)
|
||||
}
|
||||
|
||||
override suspend fun getCollectionsSync(): NFTCollections {
|
||||
val collectionsData = collectionsRuntimeStore.getSyncOrNull()
|
||||
val prices = pricesRuntimeStore.getSyncOrNull()
|
||||
return collectionsData
|
||||
?.mergeWithPrices(prices.orEmpty())
|
||||
?: NFTCollections(
|
||||
network = network,
|
||||
content = NFTCollections.Content.Collections(
|
||||
collections = null,
|
||||
source = StatusSource.CACHE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset> =
|
||||
collectionsRuntimeStore
|
||||
.get()
|
||||
.combine(getSalePrice(assetId)) { collectionsData, price ->
|
||||
collectionsData
|
||||
.getCollection(collectionId)
|
||||
?.getAsset(assetId)
|
||||
?.mergeWithPrice(price)
|
||||
?: NFTAsset.Error(assetId)
|
||||
}
|
||||
|
||||
override fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTSalePrice> = pricesRuntimeStore
|
||||
.get()
|
||||
.map { it[assetId] ?: NFTSalePrice.Empty(assetId) }
|
||||
|
||||
override suspend fun saveCollections(collections: NFTCollections) {
|
||||
collectionsRuntimeStore.store(collections)
|
||||
}
|
||||
|
||||
override suspend fun saveSalePrice(salePrice: NFTSalePrice) {
|
||||
pricesRuntimeStore.update(emptyMap()) {
|
||||
it.plus(salePrice.assetId to salePrice)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NFTCollections.getCollection(collectionId: NFTCollection.Identifier): NFTCollection? =
|
||||
(content as? NFTCollections.Content.Collections)
|
||||
?.collections
|
||||
?.firstOrNull { it.id == collectionId }
|
||||
|
||||
private fun NFTCollection.getAsset(assetId: NFTAsset.Identifier): NFTAsset? =
|
||||
assets.firstOrNull { it.id == assetId }
|
||||
|
||||
private fun NFTCollections.mergeWithPrices(prices: Map<NFTAsset.Identifier, NFTSalePrice>): NFTCollections =
|
||||
when (val content = this.content) {
|
||||
is NFTCollections.Content.Collections -> {
|
||||
this.copy(
|
||||
content = content.mergeWithPrices(prices),
|
||||
)
|
||||
}
|
||||
is NFTCollections.Content.Error -> this
|
||||
}
|
||||
|
||||
private fun NFTCollections.Content.Collections.mergeWithPrices(prices: Map<NFTAsset.Identifier, NFTSalePrice>) =
|
||||
copy(
|
||||
collections = this.collections?.map { data ->
|
||||
data.copy(
|
||||
assets = data.assets.map { asset ->
|
||||
asset.mergeWithPrice(prices[asset.id] ?: NFTSalePrice.Empty(asset.id))
|
||||
},
|
||||
)
|
||||
},
|
||||
source = this.source,
|
||||
)
|
||||
|
||||
private fun NFTAsset.mergeWithPrice(price: NFTSalePrice): NFTAsset = when (this) {
|
||||
is NFTAsset.Error -> this
|
||||
is NFTAsset.Value -> copy(
|
||||
salePrice = price,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.datasource.local.nft
|
||||
|
||||
import com.tangem.blockchain.nft.models.NFTAsset
|
||||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface NFTPersistenceStore {
|
||||
fun getCollections(): Flow<List<NFTCollection>>
|
||||
|
||||
suspend fun getCollectionsSync(): List<NFTCollection>?
|
||||
|
||||
fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset?>
|
||||
|
||||
fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTAsset.SalePrice?>
|
||||
|
||||
suspend fun getSalePricesSync(): Map<NFTAsset.Identifier, NFTAsset.SalePrice>?
|
||||
|
||||
suspend fun saveCollections(collections: List<NFTCollection>)
|
||||
|
||||
suspend fun saveSalePrice(assetId: NFTAsset.Identifier, salePrice: NFTAsset.SalePrice)
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.tangem.datasource.local.nft
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStoreFactory
|
||||
import androidx.datastore.dataStoreFile
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.nft.models.NFTAsset
|
||||
import com.tangem.blockchain.nft.models.NFTCollection
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.utils.MoshiDataStoreSerializer
|
||||
import com.tangem.datasource.utils.listTypes
|
||||
import com.tangem.datasource.utils.mapWithCustomKeyTypes
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NFTPersistenceStoreFactory @Inject constructor(
|
||||
@NetworkMoshi private val moshi: Moshi,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
fun provide(network: Network): NFTPersistenceStore {
|
||||
val networkStringIdentifier = listOfNotNull(
|
||||
network.id.value,
|
||||
network.derivationPath.value,
|
||||
).joinToString("_") {
|
||||
// remove all non-alphanumeric characters
|
||||
it
|
||||
.toCharArray()
|
||||
.filter(Char::isLetterOrDigit)
|
||||
.joinToString("")
|
||||
.lowercase()
|
||||
}
|
||||
return DefaultNFTPersistenceStore(
|
||||
collectionsPersistenceStore = DataStoreFactory.create(
|
||||
serializer = MoshiDataStoreSerializer(
|
||||
moshi = moshi,
|
||||
types = listTypes<NFTCollection>(),
|
||||
defaultValue = emptyList(),
|
||||
),
|
||||
produceFile = {
|
||||
context.dataStoreFile(fileName = "nft_${networkStringIdentifier}_collections")
|
||||
},
|
||||
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
|
||||
),
|
||||
pricesPersistenceStore = DataStoreFactory.create(
|
||||
serializer = MoshiDataStoreSerializer(
|
||||
moshi = moshi,
|
||||
types = mapWithCustomKeyTypes<NFTAsset.Identifier, NFTAsset.SalePrice>(),
|
||||
defaultValue = emptyMap(),
|
||||
),
|
||||
produceFile = {
|
||||
context.dataStoreFile(fileName = "nft_${networkStringIdentifier}_prices")
|
||||
},
|
||||
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.datasource.local.nft
|
||||
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.domain.nft.models.NFTCollections
|
||||
import com.tangem.domain.nft.models.NFTSalePrice
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface NFTRuntimeStore {
|
||||
|
||||
suspend fun initialize(collections: NFTCollections, prices: Map<NFTAsset.Identifier, NFTSalePrice>)
|
||||
|
||||
fun getCollections(): Flow<NFTCollections>
|
||||
|
||||
suspend fun getCollectionsSync(): NFTCollections
|
||||
|
||||
fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset?>
|
||||
|
||||
fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTSalePrice?>
|
||||
|
||||
suspend fun saveCollections(collections: NFTCollections)
|
||||
|
||||
suspend fun saveSalePrice(salePrice: NFTSalePrice)
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.datasource.local.nft
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NFTRuntimeStoreFactory @Inject constructor() {
|
||||
|
||||
fun provide(network: Network): NFTRuntimeStore = DefaultNFTRuntimeStore(
|
||||
network = network,
|
||||
collectionsRuntimeStore = RuntimeSharedStore(),
|
||||
pricesRuntimeStore = RuntimeSharedStore(),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.datasource.local.nft.converter
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTSalePrice
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.nft.models.NFTAsset as SdkNFTAsset
|
||||
|
||||
class NFTSdkAssetConverter(
|
||||
private val nftSdkAssetIdentifierConverter: NFTSdkAssetIdentifierConverter,
|
||||
private val nftSdkCollectionIdentifierConverter: NFTSdkCollectionIdentifierConverter,
|
||||
) : Converter<Pair<Network, SdkNFTAsset>, NFTAsset> {
|
||||
override fun convert(value: Pair<Network, SdkNFTAsset>): NFTAsset {
|
||||
val (network, asset) = value
|
||||
val assetId = nftSdkAssetIdentifierConverter.convert(asset.identifier)
|
||||
val collectionId = nftSdkCollectionIdentifierConverter.convert(asset.collectionIdentifier)
|
||||
return NFTAsset.Value(
|
||||
id = assetId,
|
||||
collectionId = collectionId,
|
||||
network = network,
|
||||
contractType = asset.contractType,
|
||||
owner = asset.owner,
|
||||
name = asset.name,
|
||||
description = asset.description,
|
||||
salePrice = asset.salePrice?.let {
|
||||
NFTSalePrice.Value(
|
||||
assetId = assetId,
|
||||
value = it.value,
|
||||
symbol = it.symbol,
|
||||
source = StatusSource.CACHE,
|
||||
)
|
||||
} ?: NFTSalePrice.Empty(assetId = assetId),
|
||||
rarity = asset.rarity?.let {
|
||||
NFTAsset.Value.Rarity(
|
||||
rank = it.rank,
|
||||
label = it.label,
|
||||
)
|
||||
},
|
||||
media = asset.media?.let {
|
||||
NFTAsset.Value.Media(
|
||||
url = it.url,
|
||||
mimetype = it.mimetype,
|
||||
)
|
||||
},
|
||||
traits = asset.traits.map {
|
||||
NFTAsset.Value.Trait(
|
||||
name = it.name,
|
||||
value = it.value,
|
||||
)
|
||||
},
|
||||
source = StatusSource.CACHE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.datasource.local.nft.converter
|
||||
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
import com.tangem.blockchain.nft.models.NFTAsset as SdkNFTAsset
|
||||
|
||||
object NFTSdkAssetIdentifierConverter : TwoWayConverter<SdkNFTAsset.Identifier, NFTAsset.Identifier> {
|
||||
override fun convert(value: SdkNFTAsset.Identifier): NFTAsset.Identifier = when (value) {
|
||||
is SdkNFTAsset.Identifier.EVM -> NFTAsset.Identifier.EVM(
|
||||
tokenId = value.tokenId,
|
||||
tokenAddress = value.tokenAddress,
|
||||
)
|
||||
is SdkNFTAsset.Identifier.TON -> NFTAsset.Identifier.TON(
|
||||
tokenAddress = value.tokenAddress,
|
||||
)
|
||||
is SdkNFTAsset.Identifier.Unknown -> NFTAsset.Identifier.Unknown
|
||||
}
|
||||
|
||||
override fun convertBack(value: NFTAsset.Identifier): SdkNFTAsset.Identifier = when (value) {
|
||||
is NFTAsset.Identifier.EVM -> SdkNFTAsset.Identifier.EVM(
|
||||
tokenId = value.tokenId,
|
||||
tokenAddress = value.tokenAddress,
|
||||
)
|
||||
is NFTAsset.Identifier.TON -> SdkNFTAsset.Identifier.TON(
|
||||
tokenAddress = value.tokenAddress,
|
||||
)
|
||||
is NFTAsset.Identifier.Unknown -> SdkNFTAsset.Identifier.Unknown
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.datasource.local.nft.converter
|
||||
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.nft.models.NFTCollection as SdkNFTCollection
|
||||
|
||||
class NFTSdkCollectionConverter(
|
||||
private val nftSdkCollectionIdentifierConverter: NFTSdkCollectionIdentifierConverter,
|
||||
private val nftSdkAssetConverter: NFTSdkAssetConverter,
|
||||
) : Converter<Pair<Network, SdkNFTCollection>, NFTCollection> {
|
||||
override fun convert(value: Pair<Network, SdkNFTCollection>): NFTCollection {
|
||||
val (network, collection) = value
|
||||
val collectionId = nftSdkCollectionIdentifierConverter.convert(collection.identifier)
|
||||
return NFTCollection(
|
||||
id = collectionId,
|
||||
network = network,
|
||||
name = collection.name,
|
||||
description = collection.description,
|
||||
logoUrl = collection.logoUrl,
|
||||
count = collection.count,
|
||||
assets = collection.assets
|
||||
.map { asset ->
|
||||
nftSdkAssetConverter.convert(network to asset)
|
||||
}
|
||||
.filter {
|
||||
it.id !is NFTAsset.Identifier.Unknown
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.datasource.local.nft.converter
|
||||
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
import com.tangem.blockchain.nft.models.NFTCollection as SdkNFTCollection
|
||||
|
||||
object NFTSdkCollectionIdentifierConverter : TwoWayConverter<SdkNFTCollection.Identifier, NFTCollection.Identifier> {
|
||||
override fun convert(value: SdkNFTCollection.Identifier): NFTCollection.Identifier = when (value) {
|
||||
is SdkNFTCollection.Identifier.EVM -> NFTCollection.Identifier.EVM(
|
||||
tokenAddress = value.tokenAddress,
|
||||
)
|
||||
is SdkNFTCollection.Identifier.TON -> NFTCollection.Identifier.TON(
|
||||
contractAddress = value.contractAddress,
|
||||
)
|
||||
is SdkNFTCollection.Identifier.Unknown -> NFTCollection.Identifier.Unknown
|
||||
}
|
||||
|
||||
override fun convertBack(value: NFTCollection.Identifier): SdkNFTCollection.Identifier = when (value) {
|
||||
is NFTCollection.Identifier.EVM -> SdkNFTCollection.Identifier.EVM(
|
||||
tokenAddress = value.tokenAddress,
|
||||
)
|
||||
is NFTCollection.Identifier.TON -> SdkNFTCollection.Identifier.TON(
|
||||
contractAddress = value.contractAddress,
|
||||
)
|
||||
is NFTCollection.Identifier.Unknown -> SdkNFTCollection.Identifier.Unknown
|
||||
}
|
||||
}
|
||||
|
|
@ -121,6 +121,8 @@ object PreferencesKeys {
|
|||
|
||||
val SEED_FIRST_NOTIFICATION_SHOW_TIME by lazy { longPreferencesKey("seedFirstNotificationTime") }
|
||||
|
||||
val WALLETS_NFT_ENABLED_STATES_KEY by lazy { stringPreferencesKey(name = "walletsNftEnabledStates") }
|
||||
|
||||
fun getShouldShowStoriesKey(storyId: String) = booleanPreferencesKey("shouldShowStories_$storyId")
|
||||
|
||||
// region Permission
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ inline fun <reified T> mapWithStringKeyTypes(): ParameterizedType {
|
|||
return Types.newParameterizedType(Map::class.java, String::class.java, T::class.java)
|
||||
}
|
||||
|
||||
inline fun <reified K, reified T> mapWithCustomKeyTypes(): ParameterizedType {
|
||||
return Types.newParameterizedType(Map::class.java, K::class.java, T::class.java)
|
||||
}
|
||||
|
||||
inline fun <reified T> listTypes(): ParameterizedType {
|
||||
return Types.newParameterizedType(List::class.java, T::class.java)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue