Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-26 13:14:53 +03:00
commit 43c376489a
316 changed files with 7586 additions and 3131 deletions

View file

@ -34,7 +34,7 @@ interface StakeKitApi {
@POST("yields/balances")
suspend fun getMultipleYieldBalances(
@Body body: List<YieldBalanceRequestBody>,
): ApiResponse<List<YieldBalanceWrapperDTO>>
): ApiResponse<Set<YieldBalanceWrapperDTO>>
@POST("yields/{integrationId}/balances")
suspend fun getSingleYieldBalance(

View file

@ -48,7 +48,7 @@ data class ActionRequestBodyArgs(
@Json(name = "ledgerWalletAPICompatible")
val ledgerWalletAPICompatible: Boolean? = null,
@Json(name = "tronResource")
val tronResource: String? = null,
val tronResource: TronResource? = null,
@Json(name = "signatureVerification")
val signatureVerification: SignatureVerification? = null,
@Json(name = "inputToken")
@ -60,4 +60,12 @@ data class SignatureVerification(
val message: String,
@Json(name = "signed")
val signed: String,
)
)
enum class TronResource {
@Json(name = "ENERGY")
ENERGY,
@Json(name = "BANDWIDTH")
BANDWIDTH,
}

View file

@ -0,0 +1,10 @@
package com.tangem.datasource.api.stakekit.models.response.model.transaction.tron
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class TronStakeKitTransaction(
@Json(name = "raw_data_hex")
val rawDataHex: String,
)

View file

@ -1,34 +0,0 @@
package com.tangem.datasource.di
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.token.AppPreferencesUserTokensStore
import com.tangem.datasource.local.token.UserTokensStore
import com.tangem.datasource.local.token.UserTokensStoreMigrationRunner
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object UserTokensStoreModule {
@Provides
@Singleton
fun provideUserTokensStore(
appPreferencesStore: AppPreferencesStore,
userTokensStoreMigrationRunner: UserTokensStoreMigrationRunner,
userWalletsStore: UserWalletsStore,
dispatchers: CoroutineDispatcherProvider,
): UserTokensStore {
return AppPreferencesUserTokensStore(
appPreferencesStore = appPreferencesStore,
userTokensStoreMigrationRunner = userTokensStoreMigrationRunner,
userWalletsStore = userWalletsStore,
dispatchers = dispatchers,
)
}
}

View file

@ -1,63 +0,0 @@
package com.tangem.datasource.local.token
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObject
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.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
/**
* Implementation of [UserTokensStore] that based on [appPreferencesStore]
*
* @property appPreferencesStore application preference store
*
[REDACTED_AUTHOR]
*/
internal class AppPreferencesUserTokensStore(
private val appPreferencesStore: AppPreferencesStore,
private val userTokensStoreMigrationRunner: UserTokensStoreMigrationRunner,
private val userWalletsStore: UserWalletsStore,
private val dispatchers: CoroutineDispatcherProvider,
) : UserTokensStore {
init {
runUserTokensMigrations()
}
override fun get(key: UserWalletId): Flow<UserTokensResponse> {
return appPreferencesStore
.getObject<UserTokensResponse>(PreferencesKeys.getUserTokensKey(userWalletId = key.stringValue))
.filterNotNull()
}
override suspend fun getSyncOrNull(key: UserWalletId): UserTokensResponse? {
return appPreferencesStore.getObjectSyncOrNull(
key = PreferencesKeys.getUserTokensKey(userWalletId = key.stringValue),
)
}
override suspend fun store(key: UserWalletId, value: UserTokensResponse) {
appPreferencesStore.storeObject(
key = PreferencesKeys.getUserTokensKey(userWalletId = key.stringValue),
value = value,
)
}
// TODO: delete in 5.15 (Mobile Sprint 161) [REDACTED_JIRA]
private fun runUserTokensMigrations() {
userWalletsStore.userWallets
.filter { it.isNotEmpty() }
.take(1)
.onEach { userWallets ->
userTokensStoreMigrationRunner.run(ids = userWallets.map { it.walletId.stringValue })
}
.flowOn(dispatchers.io)
.launchIn(CoroutineScope(dispatchers.io))
}
}

View file

@ -3,49 +3,53 @@ package com.tangem.datasource.local.token
import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
internal class DefaultStakingBalanceStore(
private val dataStore: StringKeyDataStore<List<YieldBalanceWrapperDTO>>,
private val dataStore: StringKeyDataStore<Set<YieldBalanceWrapperDTO>>,
) : StakingBalanceStore {
override fun get(): Flow<List<YieldBalanceWrapperDTO>> {
return dataStore.get(STAKING_BALANCE_KEY)
private val mutex = Mutex()
override fun get(userWalletId: UserWalletId): Flow<Set<YieldBalanceWrapperDTO>> {
return dataStore.get(userWalletId.stringValue)
}
override suspend fun getSyncOrNull(): List<YieldBalanceWrapperDTO>? {
return dataStore.getSyncOrNull(STAKING_BALANCE_KEY)
override suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalanceWrapperDTO>? {
return dataStore.getSyncOrNull(userWalletId.stringValue)
}
override suspend fun store(items: List<YieldBalanceWrapperDTO>) {
return dataStore.store(STAKING_BALANCE_KEY, items)
override suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>) {
mutex.withLock {
dataStore.store(userWalletId.stringValue, items)
}
}
override fun get(integrationId: String): Flow<List<BalanceDTO>> {
return dataStore.get(STAKING_BALANCE_KEY)
override fun get(userWalletId: UserWalletId, integrationId: String): Flow<List<BalanceDTO>> {
return dataStore.get(userWalletId.stringValue)
.map { balances ->
balances.filter { it.integrationId == integrationId }
.flatMap { it.balances }
}
}
override suspend fun getSyncOrNull(integrationId: String): List<BalanceDTO>? {
return dataStore.getSyncOrNull(STAKING_BALANCE_KEY)
override suspend fun getSyncOrNull(userWalletId: UserWalletId, integrationId: String): List<BalanceDTO>? {
return dataStore.getSyncOrNull(userWalletId.stringValue)
?.firstOrNull { it.integrationId == integrationId }?.balances
}
override suspend fun store(integrationId: String, item: YieldBalanceWrapperDTO) {
val balances = dataStore.getSyncOrNull(STAKING_BALANCE_KEY)
?.toMutableList()
?.addOrReplace(item) { item.integrationId == integrationId }
?: listOf(item)
override suspend fun store(userWalletId: UserWalletId, integrationId: String, item: YieldBalanceWrapperDTO) {
mutex.withLock {
val balances = dataStore.getSyncOrNull(userWalletId.stringValue)
?.addOrReplace(item) { it.integrationId == integrationId }
?: setOf(item)
return dataStore.store(STAKING_BALANCE_KEY, balances)
}
companion object {
private const val STAKING_BALANCE_KEY = "STAKING_BALANCE_KEY"
dataStore.store(userWalletId.stringValue, balances)
}
}
}

View file

@ -2,19 +2,20 @@ package com.tangem.datasource.local.token
import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
interface StakingBalanceStore {
fun get(): Flow<List<YieldBalanceWrapperDTO>>
fun get(userWalletId: UserWalletId): Flow<Set<YieldBalanceWrapperDTO>>
suspend fun getSyncOrNull(): List<YieldBalanceWrapperDTO>?
suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalanceWrapperDTO>?
suspend fun store(items: List<YieldBalanceWrapperDTO>)
suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>)
fun get(integrationId: String): Flow<List<BalanceDTO>>
fun get(userWalletId: UserWalletId, integrationId: String): Flow<List<BalanceDTO>>
suspend fun getSyncOrNull(integrationId: String): List<BalanceDTO>?
suspend fun getSyncOrNull(userWalletId: UserWalletId, integrationId: String): List<BalanceDTO>?
suspend fun store(integrationId: String, item: YieldBalanceWrapperDTO)
suspend fun store(userWalletId: UserWalletId, integrationId: String, item: YieldBalanceWrapperDTO)
}

View file

@ -1,46 +0,0 @@
package com.tangem.datasource.local.token
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
@Deprecated(
message = "Use AppPreferencesStore",
replaceWith = ReplaceWith(
expression = "AppPreferencesStore",
imports = arrayOf("com.tangem.datasource.local.preferences.AppPreferencesStore"),
),
level = DeprecationLevel.WARNING,
)
interface UserTokensStore {
@Deprecated(
message = "Use getObject",
replaceWith = ReplaceWith(
expression = "appPreferencesStore.getObject(userWalletId)",
imports = arrayOf("com.tangem.datasource.local.preferences.AppPreferencesStore"),
),
level = DeprecationLevel.WARNING,
)
fun get(key: UserWalletId): Flow<UserTokensResponse>
@Deprecated(
message = "Use getObjectSyncOrNull",
replaceWith = ReplaceWith(
expression = "appPreferencesStore.getObjectSyncOrNull(userWalletId)",
imports = arrayOf("com.tangem.datasource.local.preferences.AppPreferencesStore"),
),
level = DeprecationLevel.WARNING,
)
suspend fun getSyncOrNull(key: UserWalletId): UserTokensResponse?
@Deprecated(
message = "Use storeObject",
replaceWith = ReplaceWith(
expression = "appPreferencesStore.storeObject(userWalletId, response)",
imports = arrayOf("com.tangem.datasource.local.preferences.AppPreferencesStore"),
),
level = DeprecationLevel.WARNING,
)
suspend fun store(key: UserWalletId, value: UserTokensResponse)
}

View file

@ -1,56 +0,0 @@
package com.tangem.datasource.local.token
import androidx.datastore.core.DataMigration
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.files.FileReader
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.preferences.utils.storeObject
/**
* Migration of saving [UserTokensResponse] from file to [AppPreferencesStore]
*
* @param userWalletId user wallet id
* @param moshi moshi
* @property fileReader file reader
*
[REDACTED_AUTHOR]
*/
internal class UserTokensStoreMigration(
userWalletId: String,
moshi: Moshi,
private val fileReader: FileReader,
) : DataMigration<AppPreferencesStore> {
private val legacyFileName = "user_tokens_$userWalletId"
private val keyName = PreferencesKeys.getUserTokensKey(userWalletId = userWalletId)
@OptIn(ExperimentalStdlibApi::class)
private val adapter = moshi.adapter<UserTokensResponse>()
override suspend fun shouldMigrate(currentData: AppPreferencesStore): Boolean = true
override suspend fun migrate(currentData: AppPreferencesStore): AppPreferencesStore {
val currentKey = currentData.getObjectSyncOrNull<UserTokensResponse>(key = keyName)
if (currentKey != null) return currentData
val value = runCatching {
val json = fileReader.readFile(legacyFileName)
adapter.fromJson(json)
}.getOrNull()
if (value != null) {
currentData.storeObject(key = keyName, value = value)
}
return currentData
}
override suspend fun cleanUp() {
fileReader.removeFile(legacyFileName)
}
}

View file

@ -1,50 +0,0 @@
package com.tangem.datasource.local.token
import com.squareup.moshi.Moshi
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.files.FileReader
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
/**
* Runner that launch migrations of saving user tokens store
*
* @property appPreferencesStore application preference store
* @property fileReader file reader
* @property moshi moshi
* @property dispatchers dispatchers
*
[REDACTED_AUTHOR]
*/
@Singleton
class UserTokensStoreMigrationRunner @Inject constructor(
private val appPreferencesStore: AppPreferencesStore,
private val fileReader: FileReader,
@NetworkMoshi private val moshi: Moshi,
private val dispatchers: CoroutineDispatcherProvider,
) {
suspend fun run(ids: List<String>) {
ids.forEach { id ->
coroutineScope { run(id) }
}
}
private suspend fun run(id: String) {
withContext(dispatchers.io) {
val migration = UserTokensStoreMigration(
userWalletId = id,
moshi = moshi,
fileReader = fileReader,
)
migration.migrate(appPreferencesStore)
migration.cleanUp()
}
}
}