Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-22 17:20:18 +03:00
commit 7db00540c5
351 changed files with 6478 additions and 1691 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

@ -1,9 +1,11 @@
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.tangempay.repository.TangemPayTxHistoryRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -21,4 +23,8 @@ internal interface TangemPayDataModule {
@Binds
@Singleton
fun bindOnboardingRepository(repository: DefaultOnboardingRepository): OnboardingRepository
@Binds
@Singleton
fun bindTangemPayTxHistoryRepository(repository: DefaultTangemPayTxHistoryRepository): TangemPayTxHistoryRepository
}

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,11 @@ 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.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.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,19 +19,17 @@ 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 ->
tangemPayApi.getCustomerMe(authHeader)
}.map {
CustomerInfo(
productInstance = result.productInstance?.let { ProductInstance(id = it.id, status = it.status) },
kycStatus = result.kyc?.status,
productInstance = it.result?.productInstance?.let { ProductInstance(id = it.id, status = it.status) },
kycStatus = it.result?.kyc?.status,
)
}
}

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,7 +4,9 @@ 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
@ -44,18 +46,19 @@ 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 {
withContext(dispatchers.io) {
performRequest(requestBlock = requestBlock, refreshTokens = ::refreshAuthTokens).bind()
suspend fun <T : Any> request(requestBlock: suspend (header: String) -> ApiResponse<T>): Either<UniversalError, T> =
either {
withContext(dispatchers.io) {
performRequest(requestBlock = requestBlock, refreshTokens = ::refreshAuthTokens).bind()
}
}
}
private suspend fun <T : Any> performRequest(
requestBlock: suspend (header: String) -> T,
requestBlock: suspend (header: String) -> ApiResponse<T>,
refreshTokens: (suspend () -> Either<UniversalError, VisaAuthTokens>)? = null,
): Either<UniversalError, T> = either {
runCatching {
requestBlock("Bearer ${getAccessTokens().bind().accessToken}")
requestBlock("Bearer ${getAccessTokens().bind().accessToken}").getOrThrow()
}.getOrElse { error ->
when (error) {
is ApiResponseError.HttpException -> {

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

@ -30,6 +30,7 @@ internal class DefaultYieldSupplyTransactionRepository(
override suspend fun createEnterTransactions(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
maxNetworkFee: BigDecimal,
): List<TransactionData.Uncompiled> {
val cryptoCurrency = cryptoCurrencyStatus.currency
@ -62,6 +63,7 @@ internal class DefaultYieldSupplyTransactionRepository(
existingYieldContractAddress = existingYieldContractAddress,
calculatedYieldContractAddress = calculatedYieldContractAddress,
yieldTokenStatus = yieldTokenStatus,
maxNetworkFee = maxNetworkFee,
)
}
@ -93,12 +95,14 @@ internal class DefaultYieldSupplyTransactionRepository(
)
}
@Suppress("LongParameterList")
private fun buildEnterTransactions(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency.Token,
existingYieldContractAddress: String?,
calculatedYieldContractAddress: String,
yieldTokenStatus: YieldSupplyStatus?,
maxNetworkFee: BigDecimal,
): MutableList<TransactionData.Uncompiled> {
val enterTransactions = mutableListOf<TransactionData.Uncompiled>()
@ -108,6 +112,7 @@ internal class DefaultYieldSupplyTransactionRepository(
createDeployTransaction(
walletManager = walletManager,
cryptoCurrency = cryptoCurrency,
maxNetworkFee = maxNetworkFee,
),
)
}
@ -118,6 +123,7 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
yieldSupplyStatus = yieldTokenStatus,
yieldContractAddress = calculatedYieldContractAddress,
maxNetworkFee = maxNetworkFee,
),
)
!yieldTokenStatus.isActive -> enterTransactions.add(
@ -126,6 +132,7 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency = cryptoCurrency,
yieldSupplyStatus = yieldTokenStatus,
yieldContractAddress = calculatedYieldContractAddress,
maxNetworkFee = maxNetworkFee,
),
)
else -> Unit
@ -202,6 +209,7 @@ internal class DefaultYieldSupplyTransactionRepository(
isActive = sdkSupplyStatus?.isActive == true,
isInitialized = sdkSupplyStatus?.isInitialized == true,
isAllowedToSpend = isAllowedToSpend,
// maxNetworkFee = sdkSupplyStatus?.maxNetworkFee,
)
}.onFailure(Timber::e).getOrNull()
}
@ -209,11 +217,12 @@ internal class DefaultYieldSupplyTransactionRepository(
private fun createDeployTransaction(
walletManager: WalletManager,
cryptoCurrency: CryptoCurrency.Token,
maxNetworkFee: BigDecimal,
): TransactionData.Uncompiled {
val callData = YieldSupplyContractCallDataProviderFactory.getDeployCallData(
tokenContractAddress = cryptoCurrency.contractAddress,
walletAddress = walletManager.wallet.address,
maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = maxNetworkFee.convertToSdkAmount(cryptoCurrency),
)
val factoryContractAddress = walletManager.getYieldSupplyContractAddresses()?.factoryContractAddress
@ -234,10 +243,11 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency: CryptoCurrency.Token,
yieldContractAddress: String,
yieldSupplyStatus: YieldSupplyStatus,
maxNetworkFee: BigDecimal,
): TransactionData.Uncompiled {
val callData = YieldSupplyContractCallDataProviderFactory.getInitTokenCallData(
tokenContractAddress = cryptoCurrency.contractAddress,
maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = maxNetworkFee.convertToSdkAmount(cryptoCurrency),
)
return createTransaction(
@ -255,10 +265,11 @@ internal class DefaultYieldSupplyTransactionRepository(
cryptoCurrency: CryptoCurrency.Token,
yieldContractAddress: String,
yieldSupplyStatus: YieldSupplyStatus,
maxNetworkFee: BigDecimal,
): TransactionData.Uncompiled {
val callData = YieldSupplyContractCallDataProviderFactory.getReactivateTokenCallData(
tokenContractAddress = cryptoCurrency.contractAddress,
maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency),
maxNetworkFee = maxNetworkFee.convertToSdkAmount(cryptoCurrency),
)
return createTransaction(
@ -364,8 +375,4 @@ internal class DefaultYieldSupplyTransactionRepository(
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()
@ -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()
@ -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()
@ -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()
@ -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()