Updated on 2026-08-14
This commit is contained in:
parent
434382eb33
commit
b54b85858f
17 changed files with 291 additions and 0 deletions
|
|
@ -40,6 +40,7 @@ dependencies {
|
|||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.nft.models)
|
||||
implementation(projects.domain.walletConnect.models)
|
||||
implementation(projects.domain.yieldSupply.models)
|
||||
|
||||
/** Tangem libraries */
|
||||
implementation(tangemDeps.blockchain)
|
||||
|
|
|
|||
|
|
@ -144,6 +144,14 @@ interface TangemTechApi {
|
|||
suspend fun activatePromoCode(@Body body: PromocodeActivationBody): ApiResponse<PromocodeActivationResponse>
|
||||
// endregion
|
||||
|
||||
// region yield
|
||||
@GET("v1/yield/markets")
|
||||
suspend fun getYieldMarkets(): ApiResponse<YieldMarketsResponse>
|
||||
|
||||
@GET("v1/yield/token/{tokenAddress}")
|
||||
suspend fun getYieldTokenStatus(@Path("tokenAddress") tokenAddress: String): ApiResponse<YieldTokenStatusResponse>
|
||||
// endregion
|
||||
|
||||
// region account
|
||||
@GET("/v1/wallets/{walletId}/accounts")
|
||||
suspend fun getWalletAccounts(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class YieldMarketsResponse(
|
||||
@Json(name = "markets") val marketDtos: List<MarketDto>,
|
||||
@Json(name = "lastUpdated") val lastUpdated: String,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MarketDto(
|
||||
@Json(name = "tokenAddress") val tokenAddress: String,
|
||||
@Json(name = "tokenSymbol") val tokenSymbol: String,
|
||||
@Json(name = "tokenName") val tokenName: String,
|
||||
@Json(name = "apy") val apy: BigDecimal,
|
||||
@Json(name = "totalSupplied") val totalSupplied: String,
|
||||
@Json(name = "totalBorrowed") val totalBorrowed: String,
|
||||
@Json(name = "liquidityRate") val liquidityRate: String,
|
||||
@Json(name = "borrowRate") val borrowRate: String,
|
||||
@Json(name = "utilizationRate") val utilizationRate: BigDecimal,
|
||||
@Json(name = "isActive") val isActive: Boolean,
|
||||
@Json(name = "ltv") val ltv: BigDecimal,
|
||||
@Json(name = "liquidationThreshold") val liquidationThreshold: BigDecimal,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class YieldTokenStatusResponse(
|
||||
@Json(name = "tokenAddress") val tokenAddress: String,
|
||||
@Json(name = "tokenSymbol") val tokenSymbol: String,
|
||||
@Json(name = "userBalance") val userBalance: String,
|
||||
@Json(name = "earnedYield") val earnedYield: String,
|
||||
@Json(name = "currentApy") val currentApy: BigDecimal,
|
||||
@Json(name = "totalDeposited") val totalDeposited: String,
|
||||
@Json(name = "moduleAddress") val moduleAddress: String,
|
||||
@Json(name = "status") val status: String,
|
||||
@Json(name = "lastUpdateAt") val lastUpdateAt: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStoreFactory
|
||||
import androidx.datastore.dataStoreFile
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.local.yieldsupply.DefaultYieldMarketsStore
|
||||
import com.tangem.datasource.local.yieldsupply.YieldMarketsStore
|
||||
import com.tangem.datasource.utils.MoshiDataStoreSerializer
|
||||
import com.tangem.datasource.utils.listTypes
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object YieldSupplyModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideYieldMarketsStore(
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
@ApplicationContext context: Context,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): YieldMarketsStore {
|
||||
return DefaultYieldMarketsStore(
|
||||
persistenceStore = DataStoreFactory.create(
|
||||
serializer = MoshiDataStoreSerializer(
|
||||
moshi = moshi,
|
||||
types = listTypes<YieldMarketToken>(),
|
||||
defaultValue = emptyList(),
|
||||
),
|
||||
produceFile = { context.dataStoreFile(fileName = "yield_markets_cache") },
|
||||
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.datasource.local.yieldsupply
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
||||
internal class DefaultYieldMarketsStore(
|
||||
private val persistenceStore: DataStore<List<YieldMarketToken>>,
|
||||
) : YieldMarketsStore {
|
||||
|
||||
override fun get(): Flow<List<YieldMarketToken>> = persistenceStore.data
|
||||
|
||||
override suspend fun getSyncOrNull(): List<YieldMarketToken>? {
|
||||
return persistenceStore.data.firstOrNull()
|
||||
}
|
||||
|
||||
override suspend fun store(items: List<YieldMarketToken>) {
|
||||
persistenceStore.updateData { _ -> items }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.datasource.local.yieldsupply
|
||||
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface YieldMarketsStore {
|
||||
|
||||
fun get(): Flow<List<YieldMarketToken>>
|
||||
|
||||
suspend fun getSyncOrNull(): List<YieldMarketToken>?
|
||||
|
||||
suspend fun store(items: List<YieldMarketToken>)
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ dependencies {
|
|||
|
||||
/** Domain */
|
||||
implementation(projects.domain.yieldSupply)
|
||||
implementation(projects.domain.yieldSupply.models)
|
||||
implementation(projects.domain.walletManager)
|
||||
implementation(projects.domain.legacy)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.data.yield.supply
|
||||
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.yieldsupply.YieldMarketsStore
|
||||
import com.tangem.data.yield.supply.converters.YieldMarketTokenConverter
|
||||
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DefaultYieldSupplyMarketRepository(
|
||||
private val techApi: TangemTechApi,
|
||||
private val store: YieldMarketsStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : YieldSupplyMarketRepository {
|
||||
|
||||
override suspend fun getCachedMarkets(): List<YieldMarketToken>? = withContext(dispatchers.io) {
|
||||
store.getSyncOrNull()
|
||||
}
|
||||
|
||||
override suspend fun updateMarkets(): List<YieldMarketToken> = withContext(dispatchers.io) {
|
||||
val response = techApi.getYieldMarkets().getOrThrow()
|
||||
val domain = response.marketDtos.map(YieldMarketTokenConverter::convert)
|
||||
store.store(domain)
|
||||
domain
|
||||
}
|
||||
|
||||
override fun getMarketsFlow(): Flow<List<YieldMarketToken>> = store.get()
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.data.yield.supply.converters
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.YieldMarketsResponse
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal object YieldMarketTokenConverter : Converter<YieldMarketsResponse.MarketDto, YieldMarketToken> {
|
||||
override fun convert(value: YieldMarketsResponse.MarketDto): YieldMarketToken {
|
||||
return YieldMarketToken(
|
||||
tokenAddress = value.tokenAddress,
|
||||
tokenSymbol = value.tokenSymbol,
|
||||
tokenName = value.tokenName,
|
||||
apy = value.apy,
|
||||
totalSupplied = value.totalSupplied,
|
||||
totalBorrowed = value.totalBorrowed,
|
||||
liquidityRate = value.liquidityRate,
|
||||
borrowRate = value.borrowRate,
|
||||
utilizationRate = value.utilizationRate,
|
||||
isActive = value.isActive,
|
||||
ltv = value.ltv,
|
||||
liquidationThreshold = value.liquidationThreshold,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
package com.tangem.data.yield.supply.di
|
||||
|
||||
import com.tangem.data.yield.supply.DefaultYieldSupplyMarketRepository
|
||||
import com.tangem.data.yield.supply.DefaultYieldSupplyTransactionRepository
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.yieldsupply.YieldMarketsStore
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
|
||||
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -25,4 +29,18 @@ internal object YieldSupplyDataModule {
|
|||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideYieldSupplyMarketRepository(
|
||||
techApi: TangemTechApi,
|
||||
store: YieldMarketsStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): YieldSupplyMarketRepository {
|
||||
return DefaultYieldSupplyMarketRepository(
|
||||
techApi = techApi,
|
||||
store = store,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ tasks.withType<Test>().configureEach {
|
|||
dependencies {
|
||||
/** Domain */
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.yieldSupply.models)
|
||||
implementation(projects.domain.transaction.models)
|
||||
implementation(projects.domain.transaction)
|
||||
implementation(projects.domain.legacy)
|
||||
|
|
|
|||
15
domain/yield-supply/models/build.gradle.kts
Normal file
15
domain/yield-supply/models/build.gradle.kts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
alias(deps.plugins.kotlin.jvm)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// region Domain modules
|
||||
api(projects.domain.models)
|
||||
// endregion
|
||||
|
||||
// region Other libraries
|
||||
implementation(deps.kotlin.serialization)
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.domain.yield.supply.models
|
||||
|
||||
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Domain model representing a token entry in the Yield Markets list.
|
||||
*/
|
||||
@Serializable
|
||||
data class YieldMarketToken(
|
||||
val tokenAddress: String,
|
||||
val tokenSymbol: String,
|
||||
val tokenName: String,
|
||||
val apy: SerializedBigDecimal,
|
||||
val totalSupplied: String,
|
||||
val totalBorrowed: String,
|
||||
val liquidityRate: String,
|
||||
val borrowRate: String,
|
||||
val utilizationRate: SerializedBigDecimal,
|
||||
val isActive: Boolean,
|
||||
val ltv: SerializedBigDecimal,
|
||||
val liquidationThreshold: SerializedBigDecimal,
|
||||
)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.domain.yield.supply.models
|
||||
|
||||
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Domain model representing a user's yield status for a specific token.
|
||||
*/
|
||||
@Serializable
|
||||
data class YieldTokenStatus(
|
||||
val tokenAddress: String,
|
||||
val tokenSymbol: String,
|
||||
val userBalance: String,
|
||||
val earnedYield: String,
|
||||
val currentApy: SerializedBigDecimal,
|
||||
val totalDeposited: String,
|
||||
val moduleAddress: String,
|
||||
val status: String,
|
||||
val lastUpdateAt: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.domain.yield.supply
|
||||
|
||||
import com.tangem.domain.yield.supply.models.YieldMarketToken
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface YieldSupplyMarketRepository {
|
||||
|
||||
/**
|
||||
* Get cached yield markets or null if nothing cached yet.
|
||||
*/
|
||||
suspend fun getCachedMarkets(): List<YieldMarketToken>?
|
||||
|
||||
/**
|
||||
* Update markets by fetching from network and cache the result. Returns latest markets.
|
||||
*/
|
||||
@Throws
|
||||
suspend fun updateMarkets(): List<YieldMarketToken>
|
||||
|
||||
/**
|
||||
* Observe runtime markets updates.
|
||||
*/
|
||||
fun getMarketsFlow(): Flow<List<YieldMarketToken>>
|
||||
}
|
||||
|
|
@ -354,6 +354,7 @@ include(":domain:swap:models")
|
|||
include(":domain:wallet-manager")
|
||||
include(":domain:wallet-manager:models")
|
||||
include(":domain:yield-supply")
|
||||
include(":domain:yield-supply:models")
|
||||
// endregion Domain modules
|
||||
|
||||
// region Data modules
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue