Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-14 15:21:59 +03:00
commit 285e72a250
404 changed files with 8122 additions and 3606 deletions

View file

@ -0,0 +1,60 @@
package com.tangem.datasource.api.express
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.express.models.request.AssetsRequestBody
import com.tangem.datasource.api.express.models.request.PairsRequestBody
import com.tangem.datasource.api.express.models.response.*
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Query
import java.math.BigDecimal
/**
* Interface of Tangem Express API (new swap mechanism)
*/
@Suppress("LongParameterList")
interface ExpressApi {
// TODO move first three params to retrofit interceptor
@POST("assets")
suspend fun getAssets(
@Header("api-key") apiKey: String,
@Header("user-id") userId: String,
@Header("session-id") sessionId: String,
@Body body: AssetsRequestBody,
): ApiResponse<List<Asset>>
@POST("pairs")
suspend fun getPairs(@Body body: PairsRequestBody): ApiResponse<List<SwapPair>>
@GET("providers")
suspend fun getProviders(): ApiResponse<List<ExchangeProvider>>
@GET("exchange-quote")
suspend fun getExchangeQuote(
@Query("fromContractAddress") fromContractAddress: String,
@Query("fromNetwork") fromNetwork: String,
@Query("toContractAddress") toContractAddress: String,
@Query("toNetwork") toNetwork: String,
@Query("fromAmount") fromAmount: BigDecimal,
@Query("providerId") providerId: Int,
@Query("rateType") rateType: RateType,
): ApiResponse<ExchangeQuoteResponse>
@GET("exchange-data")
suspend fun getExchangeData(
@Query("fromContractAddress") fromContractAddress: String,
@Query("fromNetwork") fromNetwork: String,
@Query("toContractAddress") toContractAddress: String,
@Query("toNetwork") toNetwork: String,
@Query("fromAmount") fromAmount: BigDecimal,
@Query("providerId") providerId: Int,
@Query("rateType") rateType: RateType,
@Query("toAddress") toAddress: String,
): ApiResponse<ExchangeDataResponse>
@GET("exchange-results")
suspend fun getExchangeResults(@Query("txId") txId: String): ApiResponse<ExchangeResultsResponse>
}

View file

@ -117,8 +117,8 @@ interface OneInchApi {
*/
@GET("quote")
suspend fun quote(
@Query("fromTokenAddress") fromTokenAddress: String,
@Query("toTokenAddress") toTokenAddress: String,
@Query("src") fromTokenAddress: String,
@Query("dst") toTokenAddress: String,
@Query("amount") amount: String,
@Query("protocols") protocols: String? = null,
@Query("fee") fee: String? = null,
@ -128,6 +128,7 @@ interface OneInchApi {
@Query("mainRouteParts") mainRouteParts: String? = null,
@Query("parts") parts: String? = null,
@Query("gasPrice") gasPrice: String? = null,
@Query("includeTokensInfo") includeTokensInfo: Boolean = true,
): Response<QuoteResponse>
/**
@ -178,18 +179,18 @@ interface OneInchApi {
*/
@GET("swap")
suspend fun swap(
@Query("fromTokenAddress") fromTokenAddress: String,
@Query("toTokenAddress") toTokenAddress: String,
@Query("src") fromTokenAddress: String,
@Query("dst") toTokenAddress: String,
@Query("amount") amount: String,
@Query("fromAddress") fromAddress: String,
@Query("from") fromAddress: String,
@Query("slippage") slippage: Int,
@Query("protocols") protocols: String? = null,
@Query("destReceiver") destinationAddress: String? = null,
@Query("referrerAddress") referrerAddress: String? = null,
@Query("receiver") destinationAddress: String? = null,
@Query("referrer") referrerAddress: String? = null,
@Query("fee") fee: String? = null,
@Query("disableEstimate") disableEstimate: Boolean? = null,
@Query("permit") permit: String? = null,
@Query("compatibilityMode") compatibilityMode: Boolean? = null,
@Query("compatibility") compatibilityMode: Boolean? = null,
@Query("burnChi") burnChi: Boolean? = null,
@Query("allowPartialFill") allowPartialFill: Boolean? = null,
@Query("parts") parts: String? = null,
@ -198,6 +199,7 @@ interface OneInchApi {
@Query("complexityLevel") complexityLevel: String? = null,
@Query("gasLimit") gasLimit: String? = null,
@Query("gasPrice") gasPrice: String? = null,
@Query("includeTokensInfo") includeTokensInfo: Boolean = true,
): Response<SwapResponse>
//endregion Swap
}

View file

@ -5,16 +5,10 @@ import com.squareup.moshi.Json
/**
* Quote response
*
* @property fromToken Source token info
* @property toToken Destination token info
* @property toTokenAmount Expected amount of destination token
* @property fromTokenAmount Amount of source token
* @property estimatedGas gas fee
*/
data class QuoteResponse(
@Json(name = "fromToken") val fromToken: TokenOneInchDto,
@Json(name = "toToken") val toToken: TokenOneInchDto,
@Json(name = "toTokenAmount") val toTokenAmount: String,
@Json(name = "fromTokenAmount") val fromTokenAmount: String,
@Json(name = "estimatedGas") val estimatedGas: Int,
@Json(name = "toAmount") val toTokenAmount: String,
)

View file

@ -5,7 +5,6 @@ import com.squareup.moshi.Json
data class SwapResponse(
@Json(name = "fromToken") val fromToken: TokenOneInchDto,
@Json(name = "toToken") val toToken: TokenOneInchDto,
@Json(name = "toTokenAmount") val toTokenAmount: String,
@Json(name = "fromTokenAmount") val fromTokenAmount: String,
@Json(name = "toAmount") val toTokenAmount: String,
@Json(name = "tx") val transaction: TransactionDto,
)

View file

@ -10,10 +10,8 @@ data class QuotesResponse(
data class Quote(
@Json(name = "price")
val price: BigDecimal,
val price: BigDecimal?,
@Json(name = "priceChange24h")
val priceChange: BigDecimal,
@Json(name = "lastUpdatedAt")
val lastUpdated: String,
val priceChange: BigDecimal?,
)
}

View file

@ -102,7 +102,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
appsFlyerDevKey = configValues.appsFlyer.appsFlyerDevKey,
amplitudeApiKey = configValues.amplitudeApiKey,
shopify = configValues.shopifyShop,
zendesk = configValues.zendesk,
sprinklr = configValues.sprinklr,
swapReferrerAccount = configValues.swapReferrerAccount,
walletConnectProjectId = configValues.walletConnectProjectId,
tangemComAuthorization = configValues.tangemComAuthorization,

View file

@ -5,24 +5,9 @@ import com.squareup.moshi.JsonClass
sealed interface ChatConfig
@JsonClass(generateAdapter = true)
data class ZendeskConfig(
@Json(name = "zendeskApiKey")
val apiKey: String,
@Json(name = "zendeskAppId")
val appId: String,
@Json(name = "zendeskClientId")
val clientId: String,
@Json(name = "zendeskAccountKey")
val accountKey: String,
@Json(name = "zendeskUrl")
val url: String,
) : ChatConfig
@JsonClass(generateAdapter = true)
data class SprinklrConfig(
@Json(name = "appID")
val appId: String,
@Json(name = "baseURL")
val baseUrl: String,
@Json(name = "appID") val appId: String,
@Json(name = "apiKey") val apiKey: String,
@Json(name = "environment") val environment: String,
) : ChatConfig

View file

@ -15,7 +15,7 @@ data class Config(
@Deprecated("Not relevant since version 3.23")
val isCreatingTwinCardsAllowed: Boolean = false,
val shopify: ShopifyShop? = null,
val zendesk: ZendeskConfig? = null,
val sprinklr: SprinklrConfig? = null,
val swapReferrerAccount: SwapReferrerAccount? = null,
val walletConnectProjectId: String = "",
val tangemComAuthorization: String? = null,

View file

@ -31,7 +31,7 @@ class ConfigValueModel(
val infuraProjectId: String?,
val appsFlyer: AppsFlyer,
val shopifyShop: ShopifyShop?,
val zendesk: ZendeskConfig?,
val sprinklr: SprinklrConfig?,
val tronGridApiKey: String,
val amplitudeApiKey: String,
val swapReferrerAccount: SwapReferrerAccount?,

View file

@ -0,0 +1,31 @@
package com.tangem.datasource.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.tangem.datasource.local.*
import com.tangem.datasource.local.preferences.*
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 javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object AppPreferencesStoreModule {
@Provides
@Singleton
fun provideAppPreferencesStore(
@ApplicationContext appContext: Context,
dispatchers: CoroutineDispatcherProvider,
@NetworkMoshi moshi: Moshi,
): AppPreferencesStore {
return AppPreferencesStore(
preferencesDataStore = PreferencesDataStore.getInstance(context = appContext, dispatcher = dispatchers.io),
moshi = moshi,
)
}
}

View file

@ -61,7 +61,7 @@ class OneInchApisModule {
}
companion object {
private const val ONE_INCH_BASE_URL = "https://api-tangem.1inch.io/v5.0/"
private const val ONE_INCH_BASE_URL = "https://api-tangem.1inch.io/v5.2/"
private const val ONE_INCH_ETH_PATH = "1/"
private const val ONE_INCH_BSC_PATH = "56/"
private const val ONE_INCH_POLYGON_PATH = "137/"

View file

@ -2,6 +2,7 @@ package com.tangem.datasource.files
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import okio.use
import javax.inject.Inject
class AndroidFileReader @Inject constructor(@ApplicationContext private val context: Context) : FileReader {
@ -16,7 +17,10 @@ class AndroidFileReader @Inject constructor(@ApplicationContext private val cont
override fun rewriteFile(content: String, fileName: String) {
context.openFileOutput(fileName, Context.MODE_PRIVATE).use { stream ->
stream.write(content.toByteArray(), 0, content.length)
stream.writer().use { writer ->
// warning: don't write byteArray as json, its break cyrillic encoding
writer.write(content)
}
}
}

View file

@ -13,6 +13,7 @@ internal class BalanceStateHidingSettingsStore(
return getSyncOrNull() ?: BalanceHidingSettings(
isHidingEnabledInSettings = false,
isBalanceHidden = false,
isBalanceHidingNotificationEnabled = true,
)
}
}

View file

@ -5,20 +5,26 @@ import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
internal class DefaultNetworksStatusesStore(
dataStore: StringKeyDataStore<Set<NetworkStatus>>,
) : NetworksStatusesStore, StringKeyDataStoreDecorator<UserWalletId, Set<NetworkStatus>>(dataStore) {
private val mutex = Mutex()
override fun provideStringKey(key: UserWalletId): String {
return key.stringValue
}
override suspend fun store(key: UserWalletId, value: NetworkStatus) {
val newValues = getSyncOrNull(key)
?.addOrReplace(value) { it.network == value.network }
?: setOf(value)
mutex.withLock {
val newValues = getSyncOrNull(key)
?.addOrReplace(value) { it.network == value.network }
?: setOf(value)
store(key, newValues)
store(key, newValues)
}
}
}

View file

@ -0,0 +1,45 @@
package com.tangem.datasource.local.preferences
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import com.squareup.moshi.Moshi
/**
* Application preferences store.
* AppPreferencesStore is wrapper around DataStore<Preferences> that supports json serialization and deserialization.
*
* @property moshi Moshi instance. Property has 'public' modifier because it is used
* by Public-API inline function. Don't use it directly.
* @property preferencesDataStore DataStore<Preferences> instance
*
[REDACTED_AUTHOR]
*/
class AppPreferencesStore(
val moshi: Moshi,
private val preferencesDataStore: DataStore<Preferences>,
) : DataStore<Preferences> by preferencesDataStore {
/**
* Edit data according with transaction [transform].
*
* @param transform transaction. It has receiver [AppPreferencesStore] that allows to use [getObject], [setObject]
* functions when creating transaction.
*/
suspend fun editData(transform: suspend AppPreferencesStore.(MutablePreferences) -> Unit): Preferences {
return edit { transform(it) }
}
/** Get nullable data [T] by string [key] from [MutablePreferences] */
inline fun <reified T> MutablePreferences.getObject(key: Preferences.Key<String>): T? {
val adapter = moshi.adapter(T::class.java)
return this[key]?.let(adapter::fromJson)
}
/** Set data [T] by string [key] to [MutablePreferences] */
inline fun <reified T> MutablePreferences.setObject(key: Preferences.Key<String>, value: T) {
val adapter = moshi.adapter(T::class.java)
this[key] = adapter.toJson(value)
}
}

View file

@ -0,0 +1,54 @@
package com.tangem.datasource.local.preferences
import android.content.Context
import androidx.datastore.core.DataMigration
import androidx.datastore.core.DataStore
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.preferencesDataStoreFile
import com.tangem.datasource.local.preferences.PreferencesDataStore.INSTANCE
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
/**
* Application preferences data store 'DataStore<Preferences>'.
* Implements the singleton pattern [INSTANCE] under the hood.
*
[REDACTED_AUTHOR]
*/
internal object PreferencesDataStore {
private const val PREFERENCES_FILE_NAME = "TAP_PREFS"
private var INSTANCE: DataStore<Preferences>? = null
fun getInstance(context: Context, dispatcher: CoroutineContext): DataStore<Preferences> {
return INSTANCE ?: create(context, dispatcher).also { INSTANCE = it }
}
private fun create(context: Context, dispatcher: CoroutineContext): DataStore<Preferences> {
return PreferenceDataStoreFactory.create(
corruptionHandler = createCorruptionHandler(),
migrations = createMigrations(),
scope = CoroutineScope(context = dispatcher + SupervisorJob()),
produceFile = { context.preferencesDataStoreFile(name = PREFERENCES_FILE_NAME) },
)
}
private fun createCorruptionHandler(): ReplaceFileCorruptionHandler<Preferences> {
return ReplaceFileCorruptionHandler(
produceNewData = {
Timber.w(it)
emptyPreferences()
},
)
}
private fun createMigrations(): List<DataMigration<Preferences>> {
return listOf()
}
}

View file

@ -0,0 +1,13 @@
package com.tangem.datasource.local.preferences
/**
* All preferences keys that DataStore<Preferences> is stored.
*
[REDACTED_AUTHOR]
*/
object PreferencesKeys
/** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore<Preferences> */
internal fun getTapPrefKeysToMigrate(): Set<String> {
return setOf()
}

View file

@ -0,0 +1,46 @@
package com.tangem.datasource.local.preferences.utils
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import com.tangem.datasource.local.preferences.AppPreferencesStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
/** Get flow of nullable data [T] by string [key] */
inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String>): Flow<T?> {
val adapter = moshi.adapter(T::class.java)
return data.map { it[key]?.let(adapter::fromJson) }
}
/** Get flow of data [T] by string [key]. If data is not found, it returns [default] */
inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String>, default: T): Flow<T> {
val adapter = moshi.adapter(T::class.java)
return data.map { it[key]?.let(adapter::fromJson) ?: default }
}
/** Get nullable data [T] by string [key] */
suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrNull(key: Preferences.Key<String>): T? {
val adapter = moshi.adapter(T::class.java)
return data.firstOrNull()
?.get(key)
?.let(adapter::fromJson)
}
/** Get data [T] by string [key]. If data is not found, it returns [default] */
suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrDefault(
key: Preferences.Key<String>,
default: T,
): T {
val adapter = moshi.adapter(T::class.java)
return data.firstOrNull()
?.get(key)
?.let(adapter::fromJson)
?: default
}
/** Store data [value] by string [key] */
suspend inline fun <reified T> AppPreferencesStore.storeObject(key: Preferences.Key<String>, value: T) {
val adapter = moshi.adapter(T::class.java)
edit { it[key] = adapter.toJson(value) }
}

View file

@ -0,0 +1,33 @@
package com.tangem.datasource.local.preferences.utils
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
/** Get flow of nullable data [T] by [key] */
fun <T> DataStore<Preferences>.get(key: Preferences.Key<T>): Flow<T?> {
return data.map { it[key] }
}
/** Get flow of data [T] by [key]. If data is not found, it returns [default] */
fun <T> DataStore<Preferences>.get(key: Preferences.Key<T>, default: T): Flow<T> {
return data.map { it[key] ?: default }
}
/** Get nullable data [T] by [key] */
suspend fun <T> DataStore<Preferences>.getSyncOrNull(key: Preferences.Key<T>): T? {
return data.firstOrNull()?.get(key)
}
/** Get data [T] by [key]. If data is not found, it returns [default] */
suspend fun <T> DataStore<Preferences>.getSyncOrDefault(key: Preferences.Key<T>, default: T): T {
return data.firstOrNull()?.get(key) ?: default
}
/** Store data [value] by [key] */
suspend fun <T> DataStore<Preferences>.store(key: Preferences.Key<T>, value: T) {
edit { it[key] = value }
}

View file

@ -0,0 +1,104 @@
package com.tangem.datasource.local.preferences.utils
import android.content.Context
import android.os.Build
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.datastore.core.DataMigration
import androidx.datastore.preferences.core.*
import java.io.File
import java.io.IOException
/**
* Migration of a specified key with name changing.
* Example, migrate the "key1" from "pref1" to the "key2" from "pref2".
*
* @property context context
* @property legacyPrefsName legacy SharedPreferences name
* @property legacyKeyName legacy SharedPreferences key name
* @property keyName new SharedPreferences key name
*/
internal class SharedPreferencesKeyMigration(
private val context: Context,
private val legacyPrefsName: String,
private val legacyKeyName: String,
private val keyName: String,
) : DataMigration<Preferences> {
private val legacyPrefs = context.getSharedPreferences(legacyPrefsName, Context.MODE_PRIVATE)
override suspend fun cleanUp() {
val sharedPrefsEditor = legacyPrefs.edit()
sharedPrefsEditor.remove(legacyKeyName)
if (!sharedPrefsEditor.commit()) {
throw IOException("Unable to delete migrated keys from SharedPreferences.")
}
if (legacyPrefs.all.isEmpty()) {
deleteSharedPreferences(context = context, name = legacyPrefsName)
}
}
override suspend fun shouldMigrate(currentData: Preferences): Boolean = true
override suspend fun migrate(currentData: Preferences): Preferences {
val currentKeys = currentData.asMap().keys.map(Preferences.Key<*>::name)
// If migration is already happened, return
if (currentKeys.contains(keyName)) return currentData
val value = legacyPrefs.all[legacyKeyName]
if (value != null) {
val mutablePreferences = currentData.toMutablePreferences()
when (value) {
is Boolean -> mutablePreferences[booleanPreferencesKey(keyName)] = value
is Float -> mutablePreferences[floatPreferencesKey(keyName)] = value
is Int -> mutablePreferences[intPreferencesKey(keyName)] = value
is Long -> mutablePreferences[longPreferencesKey(keyName)] = value
is String -> mutablePreferences[stringPreferencesKey(keyName)] = value
is Set<*> -> {
@Suppress("UNCHECKED_CAST")
mutablePreferences[stringSetPreferencesKey(keyName)] = value as Set<String>
}
}
return mutablePreferences.toPreferences()
}
return currentData
}
private fun deleteSharedPreferences(context: Context, name: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (!Api24Impl.deleteSharedPreferences(context, name)) {
throw IOException("Unable to delete SharedPreferences: $name")
}
} else {
val prefsFile = getSharedPrefsFile(context, name)
val prefsBackup = getSharedPrefsBackup(prefsFile)
prefsFile.delete()
prefsBackup.delete()
}
}
@RequiresApi(Build.VERSION_CODES.N)
private object Api24Impl {
@JvmStatic
@DoNotInline
fun deleteSharedPreferences(context: Context, name: String): Boolean {
return context.deleteSharedPreferences(name)
}
}
private fun getSharedPrefsFile(context: Context, name: String): File {
val prefsDir = File(context.applicationInfo.dataDir, "shared_prefs")
return File(prefsDir, "$name.xml")
}
private fun getSharedPrefsBackup(prefsFile: File) = File(prefsFile.path + ".bak")
}

View file

@ -4,9 +4,8 @@ import com.tangem.datasource.api.tangemTech.models.QuotesResponse
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import com.tangem.datasource.local.quote.model.StoredQuote
import com.tangem.domain.tokens.model.CryptoCurrency
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.combine
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.flow.*
internal class DefaultQuotesStore(
private val dataStore: StringKeyDataStore<StoredQuote>,
@ -22,7 +21,12 @@ internal class DefaultQuotesStore(
send(emptySet())
}
combine(flows) { quotes -> quotes.toSet() }.collect(::send)
merge(*flows.toTypedArray())
.scan<StoredQuote, Set<StoredQuote>>(emptySet()) { acc, quote ->
acc.addOrReplace(quote) { it.rawCurrencyId == quote.rawCurrencyId }
}
.filter(Set<StoredQuote>::isNotEmpty)
.collect(::send)
}
}

View file

@ -10,6 +10,6 @@ internal class DefaultUserTokensStore(
) : UserTokensStore, StringKeyDataStoreDecorator<UserWalletId, UserTokensResponse>(dataStore) {
override fun provideStringKey(key: UserWalletId): String {
return key.stringValue
return "user_tokens_${key.stringValue}"
}
}

View file

@ -6,6 +6,7 @@ import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.extensions.addOrReplace
import com.tangem.utils.extensions.removeBy
import kotlinx.coroutines.flow.Flow
internal class DefaultWalletManagersStore(
@ -43,10 +44,18 @@ internal class DefaultWalletManagersStore(
val updatedWalletManagers = walletManagers
?.addOrReplace(walletManager) {
it.wallet.blockchain == walletManager.wallet.blockchain &&
it.wallet.publicKey == walletManager.wallet.publicKey
it.wallet.publicKey.derivationPath == walletManager.wallet.publicKey.derivationPath
}
?: listOf(walletManager)
store(userWalletId, updatedWalletManagers)
}
override suspend fun remove(userWalletId: UserWalletId, predicate: (WalletManager) -> Boolean) {
val walletManagers = getSyncOrNull(userWalletId)?.toMutableList() ?: return
walletManagers.removeBy(predicate)
store(userWalletId, walletManagers)
}
}

View file

@ -19,5 +19,7 @@ interface WalletManagersStore {
suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager)
suspend fun remove(userWalletId: UserWalletId, predicate: (WalletManager) -> Boolean)
suspend fun clear()
}