Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-24 12:36:51 +03:00
commit 808cd4a301
503 changed files with 12306 additions and 3036 deletions

View file

@ -29,6 +29,5 @@ interface ETagsStore {
enum class Key {
WalletAccounts,
UserTokens,
;
}
}

View file

@ -47,7 +47,6 @@ interface QuotesFetcher {
value = setOf(PRICE, PRICE_CHANGE_24H, PRICE_CHANGE_1W, PRICE_CHANGE_30D).combine(),
),
LAST_UPDATED_AT(value = "lastUpdatedAt"),
;
}
sealed interface Error {

View file

@ -2,11 +2,13 @@ package com.tangem.data.notifications
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.get
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.getSyncOrNull
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.notifications.repository.NotificationsRepository
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class DefaultNotificationsRepository @Inject constructor(
@ -17,6 +19,10 @@ class DefaultNotificationsRepository @Inject constructor(
return appPreferencesStore.getSyncOrDefault(PreferencesKeys.getShouldShowNotificationKey(key), true)
}
override fun getShouldShowNotification(key: String): Flow<Boolean> {
return appPreferencesStore.get(PreferencesKeys.getShouldShowNotificationKey(key), true)
}
override suspend fun setShouldShowNotifications(key: String, value: Boolean) {
appPreferencesStore.store(PreferencesKeys.getShouldShowNotificationKey(key), value)
}

View file

@ -1,6 +1,8 @@
package com.tangem.data.swap
import arrow.core.right
import arrow.core.none
import arrow.core.some
import arrow.core.toOption
import com.squareup.moshi.Moshi
import com.tangem.data.common.api.safeApiCall
import com.tangem.data.swap.converter.SwapDataConverter
@ -28,7 +30,7 @@ import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.swap.SwapRepositoryV2
import com.tangem.domain.swap.models.*
import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator
import com.tangem.domain.tokens.operations.CryptoCurrencyStatusFactory
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
@ -49,7 +51,6 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
private val dataSignatureVerifier: DataSignatureVerifier,
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
private val singleQuoteStatusFetcher: SingleQuoteStatusFetcher,
private val currencyStatusProxyCreator: CurrencyStatusProxyCreator,
@NetworkMoshi moshi: Moshi,
) : SwapRepositoryV2 {
@ -391,17 +392,19 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
)
}
return currencyStatusProxyCreator.createCurrencyStatus(
val quoteStatus = quote ?: singleQuoteStatusSupplier.getSyncOrNull(
params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId),
)
return CryptoCurrencyStatusFactory.create(
currency = cryptoCurrency,
maybeQuoteStatus = quote?.right() ?: singleQuoteStatusSupplier.getSyncOrNull(
params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId),
).right(),
maybeNetworkStatus = NetworkStatus(
network = cryptoCurrency.network,
value = NetworkStatus.MissedDerivation, // Caution!!! Do not change this status
).right(),
maybeYieldBalance = null,
).getOrNull()
).some(),
maybeQuoteStatus = quoteStatus.toOption(),
maybeYieldBalance = none(),
)
}
private fun parseTxDetails(txDetailsJson: String): TxDetails? {

View file

@ -17,7 +17,6 @@ import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.swap.SwapErrorResolver
import com.tangem.domain.swap.SwapRepositoryV2
import com.tangem.domain.swap.SwapTransactionRepository
import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -59,7 +58,6 @@ internal object SwapDataModule {
moshi = moshi,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleQuoteStatusFetcher = singleQuoteStatusFetcher,
currencyStatusProxyCreator = CurrencyStatusProxyCreator(),
)
}

View file

@ -9,6 +9,7 @@ import com.tangem.data.tokens.repository.DefaultCurrenciesRepository
import com.tangem.data.tokens.repository.DefaultCurrencyChecksRepository
import com.tangem.data.tokens.repository.DefaultPolkadotAccountHealthCheckRepository
import com.tangem.data.tokens.repository.DefaultTokenReceiveWarningsViewedRepository
import com.tangem.data.tokens.repository.DefaultYieldSupplyWarningsViewedRepository
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
import com.tangem.datasource.local.preferences.AppPreferencesStore
@ -19,6 +20,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
import com.tangem.domain.tokens.repository.TokenReceiveWarningsViewedRepository
import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
@ -96,4 +98,16 @@ internal object TokensDataModule {
tokenReceiveWarningActionStore = tokenReceiveWarningActionStore,
)
}
@Provides
@Singleton
fun provideDefaultYieldSupplyWarningsViewedRepository(
appPreferencesStore: AppPreferencesStore,
dispatchers: CoroutineDispatcherProvider,
): YieldSupplyWarningsViewedRepository {
return DefaultYieldSupplyWarningsViewedRepository(
appPreferencesStore = appPreferencesStore,
dispatchers = dispatchers,
)
}
}

View file

@ -0,0 +1,36 @@
package com.tangem.data.tokens.repository
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSet
import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.withContext
internal class DefaultYieldSupplyWarningsViewedRepository(
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : YieldSupplyWarningsViewedRepository {
override suspend fun getViewedWarnings(): Set<String> = withContext(dispatchers.io) {
appPreferencesStore.getObjectSet<String>(PreferencesKeys.YIELD_SUPPLY_WARNINGS_STATES_KEY).firstOrNull()
?: emptySet()
}
override suspend fun view(symbol: String) = withContext(dispatchers.io) {
appPreferencesStore.editData { mutablePreferences ->
val stored = mutablePreferences.getObjectSet<String>(
PreferencesKeys.YIELD_SUPPLY_WARNINGS_STATES_KEY,
) ?: mutableSetOf()
val updated = stored + symbol
mutablePreferences.setObjectSet<String>(
key = PreferencesKeys.YIELD_SUPPLY_WARNINGS_STATES_KEY,
value = updated,
)
}
return@withContext
}
}

View file

@ -1,11 +1,15 @@
package com.tangem.data.pay.di
import com.tangem.data.pay.repository.DefaultKycRepository
import com.tangem.data.pay.repository.DefaultTangemPayTxHistoryRepository
import com.tangem.data.pay.repository.DefaultOnboardingRepository
import com.tangem.domain.pay.repository.KycRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@ -21,4 +25,18 @@ internal interface TangemPayDataModule {
@Binds
@Singleton
fun bindOnboardingRepository(repository: DefaultOnboardingRepository): OnboardingRepository
@Binds
@Singleton
fun bindTangemPayTxHistoryRepository(repository: DefaultTangemPayTxHistoryRepository): TangemPayTxHistoryRepository
companion object {
@Provides
@Singleton
fun provideTangemPayMainScreenCustomerInfoUseCase(
repository: OnboardingRepository,
): TangemPayMainScreenCustomerInfoUseCase {
return TangemPayMainScreenCustomerInfoUseCase(repository = repository)
}
}
}

View file

@ -1,6 +1,5 @@
package com.tangem.data.pay.repository
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.domain.pay.KycStartInfo
import com.tangem.domain.pay.repository.KycRepository
@ -16,9 +15,9 @@ internal class DefaultKycRepository @Inject constructor(
override suspend fun getKycStartInfo() = withContext(dispatchers.io) {
requestHelper.request { authHeader ->
tangemPayApi.getKycAccess(authHeader = authHeader).getOrThrow().result
tangemPayApi.getKycAccess(authHeader = authHeader)
}.map {
KycStartInfo(token = it.token, locale = it.locale)
KycStartInfo(token = it.result.token, locale = it.result.locale)
}
}
}

View file

@ -3,13 +3,13 @@ package com.tangem.data.pay.repository
import arrow.core.Either
import arrow.core.raise.either
import com.tangem.core.error.UniversalError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.ProductInstance
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.visa.error.VisaApiError
import javax.inject.Inject
private const val VALID_STATUS = "valid"
@ -21,20 +21,38 @@ internal class DefaultOnboardingRepository @Inject constructor(
override suspend fun validateDeeplink(link: String): Either<UniversalError, Boolean> = either {
return requestHelper.request {
tangemPayApi.validateDeeplink(DeeplinkValidityRequest(link)).getOrThrow().result
?: raise(VisaApiError.UnknownWithoutCode)
}.map { result -> result.status == VALID_STATUS }
tangemPayApi.validateDeeplink(DeeplinkValidityRequest(link))
}.map { it.result?.status == VALID_STATUS }
}
override suspend fun getCustomerInfo(): Either<UniversalError, CustomerInfo> = either {
return requestHelper.request { authHeader ->
val response = tangemPayApi.getCustomerMe(authHeader).getOrThrow()
response.result ?: raise(VisaApiError.UnknownWithoutCode)
}.map { result ->
CustomerInfo(
productInstance = result.productInstance?.let { ProductInstance(id = it.id, status = it.status) },
kycStatus = result.kyc?.status,
tangemPayApi.getCustomerMe(authHeader)
}.map { getCustomerInfo(it.result) }
}
override suspend fun getMainScreenCustomerInfo(): Either<UniversalError, CustomerInfo> = either {
return requestHelper.requestWithPersistedToken { authHeader ->
tangemPayApi.getCustomerMe(authHeader)
}.map { getCustomerInfo(it.result) }
}
private fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo {
val card = response?.card
val balance = response?.balance
val cardInfo = if (card != null && balance != null) {
CardInfo(
lastFourDigits = card.cardNumberEnd,
balance = balance.availableBalance,
currencyCode = balance.currency,
)
} else {
null
}
return CustomerInfo(
productInstance = response?.productInstance?.let { ProductInstance(id = it.id, status = it.status) },
kycStatus = response?.kyc?.status,
cardInfo = cardInfo,
)
}
}

View file

@ -0,0 +1,95 @@
package com.tangem.data.pay.repository
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.visa.utils.TangemPayTxHistoryItemConverter
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListConfig
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.pagination.BatchFetchResult
import com.tangem.pagination.BatchListSource
import com.tangem.pagination.fetcher.BatchFetcher
import com.tangem.pagination.fetcher.CursorBatchFetcher
import com.tangem.pagination.toBatchFlow
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import javax.inject.Inject
private const val INITIAL_CURSOR = "initial_cursor_key"
internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
private val requestPerformer: TangemPayRequestPerformer,
private val visaApi: TangemPayApi,
private val cacheRegistry: CacheRegistry,
private val txHistoryItemsStore: TangemPayTxHistoryItemsStore,
private val dispatchers: CoroutineDispatcherProvider,
) : TangemPayTxHistoryRepository {
override fun getTxHistoryBatchFlow(
batchSize: Int,
context: TangemPayTxHistoryListBatchingContext,
): TangemPayTxHistoryListBatchFlow {
return BatchListSource(
fetchDispatcher = dispatchers.io,
context = context,
generateNewKey = { keys -> keys.lastOrNull()?.inc() ?: 0 },
batchFetcher = createFetcher(batchSize),
).toBatchFlow()
}
private fun createFetcher(
batchSize: Int,
): BatchFetcher<TangemPayTxHistoryListConfig, List<TangemPayTxHistoryItem>> {
return CursorBatchFetcher(
prefetchDistance = batchSize,
batchSize = batchSize,
subFetcher = { request, _, _ ->
val items = loadItems(config = request.params, cursor = request.cursor, limit = request.limit)
BatchFetchResult.Success(
data = items,
last = items.size < request.limit,
empty = items.isEmpty(),
)
},
cursorFromItem = { item -> item.id }, // last items id becomes next cursor
)
}
private suspend fun loadItems(
config: TangemPayTxHistoryListConfig,
cursor: String?,
limit: Int,
): List<TangemPayTxHistoryItem> {
cacheRegistry.invokeOnExpire(
key = getCacheKey(userWalletId = config.userWalletId, cursor = cursor),
skipCache = config.refresh,
block = { fetch(userWalletId = config.userWalletId, cursor = cursor, pageSize = limit) },
)
return txHistoryItemsStore.getSyncOrNull(
key = config.userWalletId,
cursor = cursor ?: INITIAL_CURSOR,
).orEmpty()
}
private fun getCacheKey(userWalletId: UserWalletId, cursor: String?): String {
return "tangem_pay_tx_history_${userWalletId}_${cursor ?: INITIAL_CURSOR}"
}
private suspend fun fetch(userWalletId: UserWalletId, cursor: String?, pageSize: Int) {
val response = requestPerformer.request { authHeader ->
visaApi.getTangemPayTxHistory(
authHeader = authHeader,
limit = pageSize,
cursor = cursor,
)
}.getOrNull()
response?.let {
val items = TangemPayTxHistoryItemConverter.convertList(response.result.transactions)
txHistoryItemsStore.store(key = userWalletId, cursor = cursor ?: INITIAL_CURSOR, value = items)
}
}
}

View file

@ -4,22 +4,26 @@ import arrow.core.Either
import arrow.core.raise.either
import com.squareup.moshi.Moshi
import com.tangem.core.error.UniversalError
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.model.VisaAuthTokens
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import javax.inject.Inject
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
@ -33,9 +37,9 @@ internal class TangemPayRequestPerformer @Inject constructor(
@NetworkMoshi moshi: Moshi,
private val dispatchers: CoroutineDispatcherProvider,
private val tangemPayStorage: TangemPayStorage,
private val userWalletsRepository: UserWalletsListRepository,
private val getCurrencyUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val authDataSource: TangemPayAuthDataSource,
private val getWalletsUseCase: GetWalletsUseCase,
) {
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
@ -44,24 +48,42 @@ internal class TangemPayRequestPerformer @Inject constructor(
private val refreshTokensMutex = Mutex()
private var refreshTokensJob: Deferred<Either<UniversalError, VisaAuthTokens>>? = null
suspend fun <T : Any> request(requestBlock: suspend (header: String) -> T): Either<UniversalError, T> = either {
suspend fun <T : Any> request(requestBlock: suspend (header: String) -> ApiResponse<T>): Either<UniversalError, T> =
either {
withContext(dispatchers.io) {
performRequest(
requestBlock = requestBlock,
getTokens = ::getAccessTokens,
refreshTokens = ::refreshAuthTokens,
).bind()
}
}
suspend fun <T : Any> requestWithPersistedToken(
requestBlock: suspend (header: String) -> ApiResponse<T>,
): Either<UniversalError, T> = either {
withContext(dispatchers.io) {
performRequest(requestBlock = requestBlock, refreshTokens = ::refreshAuthTokens).bind()
performRequest(
requestBlock = requestBlock,
getTokens = ::getAccessTokensIfSaved,
refreshTokens = ::refreshAuthTokens,
).bind()
}
}
private suspend fun <T : Any> performRequest(
requestBlock: suspend (header: String) -> T,
requestBlock: suspend (header: String) -> ApiResponse<T>,
getTokens: (suspend () -> Either<UniversalError, VisaAuthTokens>),
refreshTokens: (suspend () -> Either<UniversalError, VisaAuthTokens>)? = null,
): Either<UniversalError, T> = either {
runCatching {
requestBlock("Bearer ${getAccessTokens().bind().accessToken}")
requestBlock("Bearer ${getTokens().bind().accessToken}").getOrThrow()
}.getOrElse { error ->
when (error) {
is ApiResponseError.HttpException -> {
if (refreshTokens != null && error.code == ApiResponseError.HttpException.Code.UNAUTHORIZED) {
refreshOrJoin(refreshTokens).bind()
performRequest(requestBlock, refreshTokens = null).bind()
performRequest(requestBlock, refreshTokens = null, getTokens = getTokens).bind()
} else {
raise(mapHttpError(error))
}
@ -100,9 +122,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
}
private suspend fun getCustomerWalletAddress(): Either<UniversalError, String> = either {
customerWalletAddress
?: tangemPayStorage.getCustomerWalletAddress()
?: fetchAuthInputData().bind().address
customerWalletAddress ?: fetchAuthInputData().bind().address
}
private suspend fun getAccessTokens(): Either<UniversalError, VisaAuthTokens> = either {
@ -110,6 +130,11 @@ internal class TangemPayRequestPerformer @Inject constructor(
tangemPayStorage.getAuthTokens(address) ?: fetchTokens().bind()
}
private suspend fun getAccessTokensIfSaved(): Either<UniversalError, VisaAuthTokens> = either {
tangemPayStorage.getAuthTokens(getCustomerWalletAddress().bind())
?: raise(VisaApiError.UnknownWithoutCode)
}
private fun mapHttpError(throwable: ApiResponseError.HttpException): UniversalError {
val errorBody = throwable.errorBody ?: return VisaApiError.UnknownWithoutCode
return runCatching {
@ -122,14 +147,16 @@ internal class TangemPayRequestPerformer @Inject constructor(
}
private suspend fun fetchAuthInputData(): Either<UniversalError, AuthInputData> = either {
val wallet = userWalletsRepository.userWalletsSync().find { it is UserWallet.Cold } as? UserWallet.Cold
val userWallets = getWalletsUseCase()
.filter { it.isNotEmpty() }
.first()
val wallet = userWallets.find { it is UserWallet.Cold } as? UserWallet.Cold
?: raise(VisaApiError.UnknownWithoutCode)
val address = getCurrencyUseCase.invokeMultiWalletSync(wallet.walletId, CryptoCurrency.ID.fromValue(POL_VALUE))
.getOrNull()?.value?.networkAddress?.defaultAddress?.value ?: raise(VisaApiError.UnknownWithoutCode)
customerWalletAddress = address
tangemPayStorage.storeCustomerWalletAddress(address)
AuthInputData(address, wallet.cardId)
}

View file

@ -0,0 +1,51 @@
package com.tangem.data.visa.utils
import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.utils.converter.Converter
internal object TangemPayTxHistoryItemConverter :
Converter<TangemPayTxHistoryResponse.Transaction, TangemPayTxHistoryItem> {
@Suppress("CyclomaticComplexMethod")
override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem {
val spend = value.spend
val collateral = value.collateral
val payment = value.payment
val fee = value.fee
return TangemPayTxHistoryItem(
id = value.id,
date = when {
spend != null -> spend.postedAt
collateral != null -> collateral.postedAt
payment != null -> payment.postedAt
fee != null -> fee.postedAt
else -> null
},
amount = when {
spend != null -> spend.amount
collateral != null -> collateral.amount
payment != null -> payment.amount
fee != null -> fee.amount
else -> null
},
merchantName = when {
spend != null -> spend.merchantName
else -> null
},
status = when {
spend != null -> spend.status
payment != null -> payment.status
else -> null
},
currency = when {
spend != null -> spend.currency
collateral != null -> collateral.currency
payment != null -> payment.currency
fee != null -> fee.currency
else -> null
},
)
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.data.visa.utils
import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse
import com.tangem.domain.visa.model.VisaTxHistoryItem
import com.tangem.utils.converter.Converter
internal object VisaTxHistoryItemConverter : Converter<VisaTxHistoryResponse.Transaction, VisaTxHistoryItem> {
override fun convert(value: VisaTxHistoryResponse.Transaction): VisaTxHistoryItem {
return VisaTxHistoryItem(
id = value.transactionId.toString(),
date = value.transactionDt,
amount = value.blockchainAmount,
fiatAmount = value.transactionAmount,
merchantName = value.merchantName,
status = value.transactionStatus,
fiatCurrency = findCurrencyByNumericCode(value.transactionCurrencyCode),
)
}
}

View file

@ -1,19 +0,0 @@
package com.tangem.data.visa.utils
import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse
import com.tangem.domain.visa.model.VisaTxHistoryItem
internal class VisaTxHistoryItemFactory {
fun create(transaction: VisaTxHistoryResponse.Transaction): VisaTxHistoryItem {
return VisaTxHistoryItem(
id = transaction.transactionId.toString(),
date = transaction.transactionDt,
amount = transaction.blockchainAmount,
fiatAmount = transaction.transactionAmount,
merchantName = transaction.merchantName,
status = transaction.transactionStatus,
fiatCurrency = findCurrencyByNumericCode(transaction.transactionCurrencyCode),
)
}
}

View file

@ -20,8 +20,6 @@ internal class VisaTxHistoryPagingSource(
val requestTxHistory: suspend (offset: Int, pageSize: Int) -> VisaTxHistoryResponse,
) : PagingSource<Int, VisaTxHistoryItem>() {
private val itemsFactory = VisaTxHistoryItemFactory()
private val cardPublicKey = params.cardPublicKey
private val pageSize = params.pageSize
private val isRefresh = params.isRefresh
@ -82,7 +80,7 @@ internal class VisaTxHistoryPagingSource(
pagedItems.update {
it.toMutableMap().apply {
this[offset] = response.transactions.map(itemsFactory::create)
this[offset] = response.transactions.map(VisaTxHistoryItemConverter::convert)
}
}
}

View file

@ -250,7 +250,9 @@ internal class UpdateWalletManagerResultFactoryTest {
),
),
currenciesAmounts = setOf(
UpdateWalletManagerResult.CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO), // default for demo
UpdateWalletManagerResult.CryptoCurrencyAmount.Coin(
value = BigDecimal.ZERO,
), // default for demo
),
currentTransactions = emptySet(),
),
@ -272,7 +274,9 @@ internal class UpdateWalletManagerResultFactoryTest {
),
),
currenciesAmounts = setOf(
UpdateWalletManagerResult.CryptoCurrencyAmount.Coin(value = BigDecimal.ONE), // used demo amount
UpdateWalletManagerResult.CryptoCurrencyAmount.Coin(
value = BigDecimal.ONE,
), // used demo amount
),
currentTransactions = emptySet(),
),

View file

@ -415,6 +415,6 @@ internal class DefaultWalletsRepository(
else -> ActivatePromoCodeError.ActivationFailed
}
return@fold error.left()
},)
})
}
}

View file

@ -114,62 +114,64 @@ class DefaultWalletsRepositoryTest {
}
@Test
fun `GIVEN API returns wallets WHEN getWalletsInfo THEN should return converted wallets and update cache if requested`() = runTest {
// GIVEN
val applicationId = "test_app_id"
val wallet1Id = "1234567890abcdef"
val wallet2Id = "fedcba0987654321"
val walletResponses = listOf(
WalletResponse(
id = wallet1Id,
notifyStatus = true,
),
WalletResponse(
id = wallet2Id,
notifyStatus = false,
),
)
coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses)
coEvery { preferencesDataStore.updateData(any()) } returns mockk()
fun `GIVEN API returns wallets WHEN getWalletsInfo THEN should return converted wallets and update cache if requested`() =
runTest {
// GIVEN
val applicationId = "test_app_id"
val wallet1Id = "1234567890abcdef"
val wallet2Id = "fedcba0987654321"
val walletResponses = listOf(
WalletResponse(
id = wallet1Id,
notifyStatus = true,
),
WalletResponse(
id = wallet2Id,
notifyStatus = false,
),
)
coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses)
coEvery { preferencesDataStore.updateData(any()) } returns mockk()
// WHEN
val result = repository.getWalletsInfo(applicationId, updateCache = true)
// WHEN
val result = repository.getWalletsInfo(applicationId, updateCache = true)
// THEN
assertThat(result).hasSize(2)
assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id)
assertThat(result[0].isNotificationsEnabled).isTrue()
assertThat(result[1].walletId.stringValue).isEqualTo(wallet2Id)
assertThat(result[1].isNotificationsEnabled).isFalse()
// THEN
assertThat(result).hasSize(2)
assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id)
assertThat(result[0].isNotificationsEnabled).isTrue()
assertThat(result[1].walletId.stringValue).isEqualTo(wallet2Id)
assertThat(result[1].isNotificationsEnabled).isFalse()
coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) }
coVerify(exactly = 2) { preferencesDataStore.updateData(any()) }
}
coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) }
coVerify(exactly = 2) { preferencesDataStore.updateData(any()) }
}
@Test
fun `GIVEN API returns wallets WHEN getWalletsInfo with updateCache false THEN should return converted wallets without updating cache`() = runTest {
// GIVEN
val applicationId = "test_app_id"
val wallet1Id = "1234567890abcdef"
val walletResponses = listOf(
WalletResponse(
id = wallet1Id,
notifyStatus = true,
),
)
coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses)
fun `GIVEN API returns wallets WHEN getWalletsInfo with updateCache false THEN should return converted wallets without updating cache`() =
runTest {
// GIVEN
val applicationId = "test_app_id"
val wallet1Id = "1234567890abcdef"
val walletResponses = listOf(
WalletResponse(
id = wallet1Id,
notifyStatus = true,
),
)
coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses)
// WHEN
val result = repository.getWalletsInfo(applicationId, updateCache = false)
// WHEN
val result = repository.getWalletsInfo(applicationId, updateCache = false)
// THEN
assertThat(result).hasSize(1)
assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id)
assertThat(result[0].isNotificationsEnabled).isTrue()
// THEN
assertThat(result).hasSize(1)
assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id)
assertThat(result[0].isNotificationsEnabled).isTrue()
coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) }
coVerify(exactly = 0) { preferencesDataStore.updateData(any()) }
}
coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) }
coVerify(exactly = 0) { preferencesDataStore.updateData(any()) }
}
@Test
fun `GIVEN user wallets and application ID WHEN associateWallets THEN should convert and send to API`() = runTest {
@ -271,8 +273,8 @@ class DefaultWalletsRepositoryTest {
// GIVEN
coEvery { tangemTechApi.activatePromoCode(any()) } returns
ApiResponse.Error(
HttpException(code = HttpException.Code.NOT_FOUND, message = null, errorBody = null),
) as ApiResponse<PromocodeActivationResponse>
HttpException(code = HttpException.Code.NOT_FOUND, message = null, errorBody = null),
) as ApiResponse<PromocodeActivationResponse>
// WHEN
val result = repository.activatePromoCode(promoCode = "PROMO", bitcoinAddress = "addr")
@ -288,8 +290,8 @@ class DefaultWalletsRepositoryTest {
// GIVEN
coEvery { tangemTechApi.activatePromoCode(any()) } returns
ApiResponse.Error(
HttpException(code = HttpException.Code.CONFLICT, message = null, errorBody = null),
) as ApiResponse<PromocodeActivationResponse>
HttpException(code = HttpException.Code.CONFLICT, message = null, errorBody = null),
) as ApiResponse<PromocodeActivationResponse>
// WHEN
val result = repository.activatePromoCode(promoCode = "PROMO", bitcoinAddress = "addr")

View file

@ -12,7 +12,6 @@ import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
@ -30,6 +29,7 @@ internal class DefaultYieldSupplyTransactionRepository(
override suspend fun createEnterTransactions(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
maxNetworkFee: BigDecimal,
): List<TransactionData.Uncompiled> {
val cryptoCurrency = cryptoCurrencyStatus.currency
@ -51,27 +51,23 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
) ?: error("Calculated yield contract address is null")
val yieldTokenStatus = cryptoCurrencyStatus.value.yieldSupplyStatus ?: getYieldTokenStatus(
walletManager = walletManager,
cryptoCurrency = cryptoCurrency,
)
val maxNetworkFee = maxNetworkFee.convertToSdkAmount(cryptoCurrencyStatus)
return buildEnterTransactions(
walletManager = walletManager,
cryptoCurrency = cryptoCurrency,
cryptoCurrencyStatus = cryptoCurrencyStatus,
existingYieldContractAddress = existingYieldContractAddress,
calculatedYieldContractAddress = calculatedYieldContractAddress,
yieldTokenStatus = yieldTokenStatus,
maxNetworkFee = maxNetworkFee,
)
}
override suspend fun createExitTransaction(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
yieldSupplyStatus: YieldSupplyStatus,
cryptoCurrencyStatus: CryptoCurrencyStatus,
fee: Fee?,
): TransactionData.Uncompiled = withContext(dispatchers.io) {
require(cryptoCurrency is CryptoCurrency.Token)
val cryptoCurrency = cryptoCurrencyStatus.currency as CryptoCurrency.Token
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
@ -88,19 +84,24 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
callData = callData,
destinationAddress = walletManager.getYieldContract(),
yieldSupplyStatus = yieldSupplyStatus,
amount = BigDecimal.ZERO.convertToSdkAmount(cryptoCurrencyStatus),
fee = fee,
)
}
@Suppress("LongParameterList")
private fun buildEnterTransactions(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency.Token,
cryptoCurrencyStatus: CryptoCurrencyStatus,
existingYieldContractAddress: String?,
calculatedYieldContractAddress: String,
yieldTokenStatus: YieldSupplyStatus?,
maxNetworkFee: Amount,
): MutableList<TransactionData.Uncompiled> {
val enterTransactions = mutableListOf<TransactionData.Uncompiled>()
val cryptoCurrency = cryptoCurrencyStatus.currency as CryptoCurrency.Token
val yieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
val amount = BigDecimal.ZERO.convertToSdkAmount(cryptoCurrencyStatus)
when {
existingYieldContractAddress == null || existingYieldContractAddress == EthereumUtils.ZERO_ADDRESS -> {
@ -108,30 +109,34 @@ internal class DefaultYieldSupplyTransactionRepository(
createDeployTransaction(
walletManager = walletManager,
cryptoCurrency = cryptoCurrency,
amount = amount,
maxNetworkFee = maxNetworkFee,
),
)
}
yieldTokenStatus == null -> error("Yield token status is null")
!yieldTokenStatus.isInitialized -> enterTransactions.add(
yieldSupplyStatus == null -> error("Yield token status is null")
!yieldSupplyStatus.isInitialized -> enterTransactions.add(
createInitTokenTransaction(
walletManager = walletManager,
cryptoCurrency = cryptoCurrency,
yieldSupplyStatus = yieldTokenStatus,
yieldContractAddress = calculatedYieldContractAddress,
amount = amount,
maxNetworkFee = maxNetworkFee,
),
)
!yieldTokenStatus.isActive -> enterTransactions.add(
!yieldSupplyStatus.isActive -> enterTransactions.add(
createReactivateTokenTransaction(
walletManager = walletManager,
cryptoCurrency = cryptoCurrency,
yieldSupplyStatus = yieldTokenStatus,
yieldContractAddress = calculatedYieldContractAddress,
amount = amount,
maxNetworkFee = maxNetworkFee,
),
)
else -> Unit
}
if (yieldTokenStatus?.isAllowedToSpend == false) {
if (yieldSupplyStatus?.isAllowedToSpend != true) {
enterTransactions.add(
createTransaction(
walletManager = walletManager,
@ -141,7 +146,7 @@ internal class DefaultYieldSupplyTransactionRepository(
amount = null,
),
destinationAddress = cryptoCurrency.contractAddress,
yieldSupplyStatus = yieldTokenStatus,
amount = amount,
fee = null,
),
)
@ -151,7 +156,7 @@ internal class DefaultYieldSupplyTransactionRepository(
createEnterTransaction(
walletManager = walletManager,
cryptoCurrency = cryptoCurrency,
yieldSupplyStatus = yieldTokenStatus,
amount = amount,
yieldContractAddress = calculatedYieldContractAddress,
),
)
@ -171,11 +176,10 @@ internal class DefaultYieldSupplyTransactionRepository(
derivationPath = cryptoCurrency.network.derivationPath.value,
) ?: error("Wallet manager not found")
walletManager.calculateYieldContract()
}.onFailure(Timber::e)
.getOrNull()
}.onFailure(Timber::e).getOrNull()
}
private suspend fun getYieldContractAddress(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): String? =
override suspend fun getYieldContractAddress(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): String? =
withContext(dispatchers.io) {
require(cryptoCurrency is CryptoCurrency.Token)
runCatching {
@ -185,35 +189,19 @@ internal class DefaultYieldSupplyTransactionRepository(
derivationPath = cryptoCurrency.network.derivationPath.value,
) ?: error("Wallet manager not found")
walletManager.getYieldContract()
}.onFailure(Timber::e)
.getOrNull()
}.onFailure(Timber::e).getOrNull()
}
private suspend fun getYieldTokenStatus(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency,
): YieldSupplyStatus? = withContext(dispatchers.io) {
require(cryptoCurrency is CryptoCurrency.Token)
runCatching {
val sdkSupplyStatus = walletManager.getYieldSupplyStatus(cryptoCurrency.contractAddress)
val isAllowedToSpend = walletManager.isAllowedToSpend(cryptoCurrency.contractAddress)
YieldSupplyStatus(
isActive = sdkSupplyStatus?.isActive == true,
isInitialized = sdkSupplyStatus?.isInitialized == true,
isAllowedToSpend = isAllowedToSpend,
)
}.onFailure(Timber::e).getOrNull()
}
private fun createDeployTransaction(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency.Token,
amount: Amount,
maxNetworkFee: Amount,
): TransactionData.Uncompiled {
val callData = YieldSupplyContractCallDataProviderFactory.getDeployCallData(
tokenContractAddress = cryptoCurrency.contractAddress,
walletAddress = walletManager.wallet.address,
maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = maxNetworkFee,
)
val factoryContractAddress = walletManager.getYieldSupplyContractAddresses()?.factoryContractAddress
@ -224,7 +212,7 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
callData = callData,
destinationAddress = factoryContractAddress,
yieldSupplyStatus = null,
amount = amount,
fee = null,
)
}
@ -233,11 +221,12 @@ internal class DefaultYieldSupplyTransactionRepository(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency.Token,
yieldContractAddress: String,
yieldSupplyStatus: YieldSupplyStatus,
amount: Amount,
maxNetworkFee: Amount,
): TransactionData.Uncompiled {
val callData = YieldSupplyContractCallDataProviderFactory.getInitTokenCallData(
tokenContractAddress = cryptoCurrency.contractAddress,
maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = maxNetworkFee,
)
return createTransaction(
@ -245,7 +234,7 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
callData = callData,
destinationAddress = yieldContractAddress,
yieldSupplyStatus = yieldSupplyStatus,
amount = amount,
fee = null,
)
}
@ -254,11 +243,12 @@ internal class DefaultYieldSupplyTransactionRepository(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency.Token,
yieldContractAddress: String,
yieldSupplyStatus: YieldSupplyStatus,
amount: Amount,
maxNetworkFee: Amount,
): TransactionData.Uncompiled {
val callData = YieldSupplyContractCallDataProviderFactory.getReactivateTokenCallData(
tokenContractAddress = cryptoCurrency.contractAddress,
maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = maxNetworkFee,
)
return createTransaction(
@ -266,7 +256,7 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
callData = callData,
destinationAddress = yieldContractAddress,
yieldSupplyStatus = yieldSupplyStatus,
amount = amount,
fee = null,
)
}
@ -274,7 +264,7 @@ internal class DefaultYieldSupplyTransactionRepository(
private fun createEnterTransaction(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency.Token,
yieldSupplyStatus: YieldSupplyStatus?,
amount: Amount,
yieldContractAddress: String,
): TransactionData.Uncompiled {
val callData = YieldSupplyContractCallDataProviderFactory.getEnterCallData(
@ -286,7 +276,7 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
callData = callData,
destinationAddress = yieldContractAddress,
yieldSupplyStatus = yieldSupplyStatus,
amount = amount,
fee = null,
)
}
@ -297,7 +287,7 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency: CryptoCurrency,
callData: SmartContractCallData,
destinationAddress: String,
yieldSupplyStatus: YieldSupplyStatus?,
amount: Amount,
fee: Fee?,
): TransactionData.Uncompiled {
requireNotNull(cryptoCurrency as? CryptoCurrency.Token)
@ -308,8 +298,6 @@ internal class DefaultYieldSupplyTransactionRepository(
blockchain = blockchain,
)
val amount = getYieldSupplyAmount(cryptoCurrency, yieldSupplyStatus)
return if (fee != null) {
walletManager.createTransaction(
amount = amount,
@ -349,23 +337,4 @@ internal class DefaultYieldSupplyTransactionRepository(
else -> error("Data extras not supported for $blockchain")
}
}
private fun getYieldSupplyAmount(cryptoCurrency: CryptoCurrency.Token, yieldSupplyStatus: YieldSupplyStatus?) =
BigDecimal.ZERO.convertToSdkAmount(
cryptoCurrency = cryptoCurrency,
amountType = AmountType.TokenYieldSupply(
token = Token(
symbol = cryptoCurrency.symbol,
contractAddress = cryptoCurrency.contractAddress,
decimals = cryptoCurrency.decimals,
),
isActive = yieldSupplyStatus?.isActive ?: false,
isInitialized = yieldSupplyStatus?.isInitialized ?: false,
isAllowedToSpend = yieldSupplyStatus?.isAllowedToSpend ?: false,
),
)
private companion object {
val MAX_NETWORK_FEE: BigDecimal = BigDecimal.TEN // TODO for TESTNET only
}
}

View file

@ -16,7 +16,6 @@ import com.tangem.domain.models.yield.supply.YieldSupplyStatus
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import com.tangem.blockchain.yieldsupply.providers.YieldSupplyStatus as SDKYieldSupplyStatus
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
@ -26,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.math.BigDecimal
import com.tangem.blockchain.yieldsupply.providers.YieldSupplyStatus as SDKYieldSupplyStatus
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DefaultYieldSupplyTransactionRepositoryTest {
@ -74,7 +74,11 @@ class DefaultYieldSupplyTransactionRepositoryTest {
coEvery { walletManager.isAllowedToSpend(any()) } returns false
coEvery { walletManager.calculateYieldContract() } returns yieldContractAddress
val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus)
val result = repository.createEnterTransactions(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
maxNetworkFee = BigDecimal.TEN,
)
// Assert that 3 transactions are returned: deploy, approve, enter
Truth.assertThat(result).isNotNull()
@ -84,7 +88,7 @@ class DefaultYieldSupplyTransactionRepositoryTest {
val firstExpectedCallData = YieldSupplyContractCallDataProviderFactory.getDeployCallData(
walletAddress = walletManager.wallet.address,
tokenContractAddress = mockedContractAddress,
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrencyStatus),
)
val firstTransaction = result.first()
@ -117,13 +121,18 @@ class DefaultYieldSupplyTransactionRepositoryTest {
fun `createEnterTransactions returns init-approve-enter transactions`() = runTest {
coEvery { walletManager.getYieldContract() } returns yieldContractAddress
coEvery { walletManager.calculateYieldContract() } returns yieldContractAddress
coEvery { walletManager.isAllowedToSpend(any()) } returns false
coEvery { walletManager.getYieldSupplyStatus(any()) } returns SDKYieldSupplyStatus(
isActive = false,
isInitialized = false,
maxNetworkFee = BigDecimal.TEN,
)
val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus)
val result = repository.createEnterTransactions(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
maxNetworkFee = BigDecimal.TEN,
)
// Assert that 3 transactions are returned: init token, approve, enter
Truth.assertThat(result).isNotNull()
@ -132,7 +141,7 @@ class DefaultYieldSupplyTransactionRepositoryTest {
// Check transaction - init token
val firstExpectedCallData = YieldSupplyContractCallDataProviderFactory.getInitTokenCallData(
tokenContractAddress = mockedContractAddress,
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrencyStatus),
)
val firstTransaction = result.first()
@ -165,13 +174,18 @@ class DefaultYieldSupplyTransactionRepositoryTest {
fun `createEnterTransactions returns reactivate-approve-enter transactions`() = runTest {
coEvery { walletManager.getYieldContract() } returns yieldContractAddress
coEvery { walletManager.calculateYieldContract() } returns yieldContractAddress
coEvery { walletManager.isAllowedToSpend(any()) } returns false
coEvery { walletManager.getYieldSupplyStatus(any()) } returns SDKYieldSupplyStatus(
isActive = false,
isInitialized = true,
maxNetworkFee = BigDecimal.TEN,
)
val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus)
val result = repository.createEnterTransactions(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
maxNetworkFee = BigDecimal.TEN,
)
// Assert that 3 transactions are returned: reactivate token, approve, enter
Truth.assertThat(result).isNotNull()
@ -180,7 +194,7 @@ class DefaultYieldSupplyTransactionRepositoryTest {
// Check transaction - reactivate token
val firstExpectedCallData = YieldSupplyContractCallDataProviderFactory.getReactivateTokenCallData(
tokenContractAddress = mockedContractAddress,
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrencyStatus),
)
val firstTransaction = result.first()
@ -220,7 +234,11 @@ class DefaultYieldSupplyTransactionRepositoryTest {
)
coEvery { walletManager.isAllowedToSpend(any()) } returns true
val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus)
val result = repository.createEnterTransactions(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
maxNetworkFee = BigDecimal.TEN,
)
// Assert that 2 transactions are returned: approve, enter
Truth.assertThat(result).isNotNull()
@ -229,7 +247,7 @@ class DefaultYieldSupplyTransactionRepositoryTest {
// Check transaction - reactivate token
val firstExpectedCallData = YieldSupplyContractCallDataProviderFactory.getReactivateTokenCallData(
tokenContractAddress = mockedContractAddress,
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = BigDecimal.TEN.convertToSdkAmount(cryptoCurrencyStatus),
)
val firstTransaction = result.first()
@ -257,7 +275,11 @@ class DefaultYieldSupplyTransactionRepositoryTest {
)
coEvery { walletManager.isAllowedToSpend(any()) } returns true
val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus)
val result = repository.createEnterTransactions(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
maxNetworkFee = BigDecimal.TEN,
)
// Assert that transaction is returned: enter
Truth.assertThat(result).isNotNull()