Updated on 2026-08-14
This commit is contained in:
parent
f48154daa9
commit
320b3deffb
14 changed files with 199 additions and 22 deletions
|
|
@ -47,6 +47,7 @@ import com.tangem.domain.card.ScanCardUseCase
|
|||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.staking.SendUnsubmittedHashesUseCase
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
|
|
@ -88,6 +89,7 @@ import com.tangem.wallet.databinding.ActivityMainBinding
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
|
@ -149,6 +151,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
@Inject
|
||||
lateinit var settingsRepository: SettingsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sendUnsubmittedHashesUseCase: SendUnsubmittedHashesUseCase
|
||||
|
||||
@Inject
|
||||
lateinit var getPolkadotCheckHasResetUseCase: GetPolkadotCheckHasResetUseCase
|
||||
|
||||
|
|
@ -225,6 +230,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
observeStateUpdates()
|
||||
observePolkadotAccountHealthCheck()
|
||||
sendStakingUnsubmittedHashes()
|
||||
|
||||
if (intent != null) {
|
||||
deepLinksRegistry.launch(intent)
|
||||
|
|
@ -583,4 +589,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendStakingUnsubmittedHashes() {
|
||||
lifecycleScope.launch {
|
||||
sendUnsubmittedHashesUseCase.invoke()
|
||||
.onRight { Timber.d("Submitting hashes succeeded") }
|
||||
.onLeft { Timber.e(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,4 +61,22 @@ internal object StakingDomainModule {
|
|||
fun provideCreateEnterActionUseCase(stakingRepository: StakingRepository): InitializeStakingProcessUseCase {
|
||||
return InitializeStakingProcessUseCase(stakingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSubmitHashUseCase(stakingRepository: StakingRepository): SubmitHashUseCase {
|
||||
return SubmitHashUseCase(stakingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSaveUnsubmittedHashUseCase(stakingRepository: StakingRepository): SaveUnsubmittedHashUseCase {
|
||||
return SaveUnsubmittedHashUseCase(stakingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSendUnsubmittedHashesUseCase(stakingRepository: StakingRepository): SendUnsubmittedHashesUseCase {
|
||||
return SendUnsubmittedHashesUseCase(stakingRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -85,6 +85,8 @@ object PreferencesKeys {
|
|||
|
||||
val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") }
|
||||
|
||||
val UNSUBMITTED_TRANSACTIONS_KEY by lazy { stringPreferencesKey(name = "unsubmittedTransactions") }
|
||||
|
||||
val IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY by lazy {
|
||||
booleanPreferencesKey(name = "isWalletSwapPromoOkxShown")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal class DefaultUserTokensStore(
|
||||
dataStore: StringKeyDataStore<UserTokensResponse>,
|
||||
) : UserTokensStore, StringKeyDataStoreDecorator<UserWalletId, UserTokensResponse>(dataStore) {
|
||||
|
||||
override fun provideStringKey(key: UserWalletId): String {
|
||||
return "user_tokens_${key.stringValue}"
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
|
|
@ -33,14 +32,17 @@ dependencies {
|
|||
// endregion
|
||||
|
||||
// region Others dependencies
|
||||
implementation(deps.androidx.datastore)
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.moshi)
|
||||
implementation(deps.moshi.kotlin)
|
||||
|
||||
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(deps.tangem.blockchain) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import com.tangem.data.staking.converters.transaction.StakingTransactionStatusCo
|
|||
import com.tangem.data.staking.converters.transaction.StakingTransactionTypeConverter
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.stakekit.models.request.Address
|
||||
import com.tangem.datasource.api.stakekit.models.request.ConstructTransactionRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.request.EnterActionRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.request.*
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
|
|
@ -33,14 +33,16 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.toFormattedString
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
internal class DefaultStakingRepository(
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val stakingBalanceStore: StakingBalanceStore,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
|
|
@ -372,6 +374,55 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun submitHash(transactionId: String, transactionHash: String) {
|
||||
withContext(dispatchers.io) {
|
||||
stakeKitApi.submitTransactionHash(
|
||||
transactionId = transactionId,
|
||||
body = SubmitTransactionHashRequestBody(
|
||||
hash = transactionHash,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata) {
|
||||
withContext(dispatchers.io) {
|
||||
appPreferencesStore.editData { preferences ->
|
||||
val savedTransactions = preferences.getObjectListOrDefault<UnsubmittedTransactionMetadata>(
|
||||
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
|
||||
default = emptyList(),
|
||||
)
|
||||
|
||||
preferences.setObjectList(
|
||||
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
|
||||
value = savedTransactions + unsubmittedTransactionMetadata,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendUnsubmittedHashes() {
|
||||
withContext(NonCancellable) {
|
||||
val savedTransactions = appPreferencesStore.getObjectListSync<UnsubmittedTransactionMetadata>(
|
||||
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
|
||||
)
|
||||
|
||||
savedTransactions.forEach {
|
||||
stakeKitApi.submitTransactionHash(
|
||||
transactionId = it.transactionId,
|
||||
body = SubmitTransactionHashRequestBody(hash = it.transactionHash),
|
||||
)
|
||||
}
|
||||
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
mutablePreferences.setObjectList<UnsubmittedTransactionMetadata>(
|
||||
key = PreferencesKeys.UNSUBMITTED_TRANSACTIONS_KEY,
|
||||
value = emptyList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPrefetchedYield(yields: List<Yield>, currencyId: String, symbol: String): Yield? {
|
||||
return yields.find { it.token.coinGeckoId == currencyId && it.token.symbol == symbol }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.data.staking.di
|
|||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.staking.DefaultStakingRepository
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
|
@ -22,6 +23,7 @@ internal object StakingDataModule {
|
|||
@Singleton
|
||||
fun provideStakingRepository(
|
||||
stakeKitApi: StakeKitApi,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
stakingTokenStore: StakingYieldsStore,
|
||||
stakingBalanceStore: StakingBalanceStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -30,6 +32,7 @@ internal object StakingDataModule {
|
|||
): StakingRepository {
|
||||
return DefaultStakingRepository(
|
||||
stakeKitApi = stakeKitApi,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
stakingYieldsStore = stakingTokenStore,
|
||||
stakingBalanceStore = stakingBalanceStore,
|
||||
dispatchers = dispatchers,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.UnsubmittedTransactionMetadata
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
||||
/**
|
||||
* Use case for saving hash that failed to submit during staking confirmation
|
||||
*/
|
||||
class SaveUnsubmittedHashUseCase(private val stakingRepository: StakingRepository) {
|
||||
|
||||
suspend operator fun invoke(transactionId: String, transactionHash: String): Either<Throwable, Unit> {
|
||||
return Either.catch {
|
||||
stakingRepository.storeUnsubmittedHash(
|
||||
unsubmittedTransactionMetadata = UnsubmittedTransactionMetadata(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
||||
/**
|
||||
* Use case for commiting hashes that failed to submit during staking confirmation
|
||||
*/
|
||||
class SendUnsubmittedHashesUseCase(private val stakingRepository: StakingRepository) {
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, Unit> {
|
||||
return Either.catch {
|
||||
stakingRepository.sendUnsubmittedHashes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
||||
/**
|
||||
* Use case for submitting transaction hash to stakekit
|
||||
*/
|
||||
class SubmitHashUseCase(private val stakingRepository: StakingRepository) {
|
||||
|
||||
suspend fun submitHash(transactionId: String, transactionHash: String): Either<Throwable, Unit> {
|
||||
return Either.catch {
|
||||
stakingRepository.submitHash(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.domain.staking.model
|
||||
|
||||
data class UnsubmittedTransactionMetadata(
|
||||
val transactionHash: String,
|
||||
val transactionId: String,
|
||||
)
|
||||
|
|
@ -69,4 +69,10 @@ interface StakingRepository {
|
|||
): EnterAction
|
||||
|
||||
suspend fun constructTransaction(transactionId: String): StakingTransaction
|
||||
|
||||
suspend fun submitHash(transactionId: String, transactionHash: String)
|
||||
|
||||
suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata)
|
||||
|
||||
suspend fun sendUnsubmittedHashes()
|
||||
}
|
||||
|
|
@ -200,4 +200,13 @@ class MockStakingRepository : StakingRepository {
|
|||
ledgerHwAppId = null,
|
||||
isMessage = false,
|
||||
)
|
||||
|
||||
override suspend fun submitHash(transactionId: String, transactionHash: String) {
|
||||
}
|
||||
|
||||
override suspend fun storeUnsubmittedHash(unsubmittedTransactionMetadata: UnsubmittedTransactionMetadata) {
|
||||
}
|
||||
|
||||
override suspend fun sendUnsubmittedHashes() {
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,8 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.staking.InitializeStakingProcessUseCase
|
||||
import com.tangem.domain.staking.SaveUnsubmittedHashUseCase
|
||||
import com.tangem.domain.staking.SubmitHashUseCase
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.domain.staking.model.transaction.StakingTransaction
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
|
||||
|
|
@ -58,6 +60,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val initializeStakingProcessUseCase: InitializeStakingProcessUseCase,
|
||||
private val sendTransactionUseCase: SendTransactionUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
|
||||
private val submitHashUseCase: SubmitHashUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
|
||||
|
||||
|
|
@ -286,12 +290,17 @@ internal class StakingViewModel @Inject constructor(
|
|||
)
|
||||
},
|
||||
ifRight = { txHash ->
|
||||
val gasEstimate = stakingTransaction?.gasEstimate ?: return@fold
|
||||
val transaction = stakingTransaction ?: return@fold
|
||||
|
||||
submitHash(transaction.id, txHash)
|
||||
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = txHash,
|
||||
networkId = cryptoCurrencyStatus.currency.network.id,
|
||||
).getOrElse { "" }
|
||||
|
||||
val gasEstimate = transaction.gasEstimate ?: error("No gas for transaction")
|
||||
|
||||
stateController.update(
|
||||
SetConfirmationStateCompletedTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
|
|
@ -304,6 +313,21 @@ internal class StakingViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun submitHash(transactionId: String, transactionHash: String) {
|
||||
submitHashUseCase.submitHash(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
.onLeft {
|
||||
saveUnsubmittedHashUseCase.invoke(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAssentState(): Boolean {
|
||||
return value.currentStep == StakingStep.Confirmation &&
|
||||
(value.confirmationState as? StakingStates.ConfirmationState.Data)?.innerState ==
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue