Updated on 2026-08-14

This commit is contained in:
Tangem 2024-03-26 12:02:58 +03:00
parent 328271bc26
commit 8b6944e3e4
31 changed files with 1464 additions and 818 deletions

View file

@ -29,4 +29,6 @@ fun String.toQrCode(sizePx: Int = 256, paddingPx: Int = 0): Bitmap {
}
}
return bmp
}
}
fun String.capitalize(): String = replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }

View file

@ -59,11 +59,7 @@ object DateTimeFormatters {
DateTimeFormat.forPattern("dd.MM.yyyy HH:mm")
}
fun formatTime(formatter: DateTimeFormatter = timeFormatter, time: DateTime): String {
return formatter.print(time)
}
fun formatDate(formatter: DateTimeFormatter = dateFormatter, date: DateTime): String {
fun formatDate(date: DateTime, formatter: DateTimeFormatter = dateFormatter): String {
return formatter.print(date)
}
}

View file

@ -29,5 +29,5 @@ fun Long.toDateFormat(formatter: DateTimeFormatter = DateTimeFormatters.dateForm
* Returns formatted time according to [formatter].
*/
fun Long.toTimeFormat(formatter: DateTimeFormatter = DateTimeFormatters.timeFormatter): String {
return DateTimeFormatters.formatTime(formatter = formatter, time = DateTime(this, DateTimeZone.getDefault()))
return DateTimeFormatters.formatDate(date = DateTime(this, DateTimeZone.getDefault()), formatter = formatter)
}

View file

@ -26,7 +26,7 @@ dependencies {
implementation(projects.domain.legacy)
/** Project - Libs */
implementation(projects.libs.visa)
debugImplementation(projects.libs.visa)
/** Libs - Other */
implementation(deps.kotlin.coroutines)

View file

@ -58,7 +58,7 @@ internal class DefaultVisaRepository(
override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency {
val address = makeAddress(userWalletId)
// val address = "0x143fe062a538176aa0bf162f13d390208f90898f" // for testing
// val address = "0x40d8194b7168723ece51fa34d16825c60ba03dfa" // for testing
fetchVisaCurrencyIfExpired(address, isRefresh)
@ -97,7 +97,7 @@ internal class DefaultVisaRepository(
): Flow<PagingData<VisaTxHistoryItem>> {
val userWallet = findVisaUserWallet(userWalletId)
val cardPubKey = getCardPubKey(userWallet).toHexString()
// val cardPubKey = "03DEF02B1FECC8BD3CFD52CE93235194479E1DE931EF0F55DC194967E7CCC3D12C" // for testing
// val cardPubKey = "02C2BBA0DA1E066EA968C1EB129499F6DEBC5FD82D70D61DCAF691CDB69AF5D8B9" // for testing
val pager = Pager(
config = PagingConfig(
pageSize = pageSize,
@ -125,7 +125,7 @@ internal class DefaultVisaRepository(
return withContext(dispatchers.io) {
val userWallet = findVisaUserWallet(userWalletId)
val cardPubKey = getCardPubKey(userWallet).toHexString()
// val cardPubKey = "03DEF02B1FECC8BD3CFD52CE93235194479E1DE931EF0F55DC194967E7CCC3D12C" // for testing
// val cardPubKey = "02C2BBA0DA1E066EA968C1EB129499F6DEBC5FD82D70D61DCAF691CDB69AF5D8B9" // for testing
val transaction = fetchedHistoryItems.value[cardPubKey]?.firstOrNull {
it.transactionId.toString() == txId
}

View file

@ -0,0 +1,53 @@
package com.tangem.data.visa.di
import com.squareup.moshi.Moshi
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.visa.BuildConfig
import com.tangem.data.visa.DefaultVisaRepository
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.visa.repository.VisaRepository
import com.tangem.lib.visa.VisaContractInfoProvider
import com.tangem.lib.visa.api.VisaApiBuilder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object ImplementedVisaDataModule {
@Provides
@Singleton
@ImplementedVisaRepository
fun provideVisaRepository(
@NetworkMoshi moshi: Moshi,
tangemTechApi: TangemTechApi,
cacheRegistry: CacheRegistry,
userWalletsStore: UserWalletsStore,
dispatchers: CoroutineDispatcherProvider,
): VisaRepository {
val contractInfoProvider = VisaContractInfoProvider.Builder(
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
dispatchers = dispatchers,
).build()
val visaApi = VisaApiBuilder(
useDevApi = true,
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
moshi = moshi,
).build()
return DefaultVisaRepository(
contractInfoProvider,
tangemTechApi,
visaApi,
cacheRegistry,
userWalletsStore,
dispatchers,
)
}
}

View file

@ -17,6 +17,7 @@ internal class VisaCurrencyFactory {
} else {
balancesAndLimits.newLimits
}
val remainingOtpLimit = getRemainingOtp(currentLimit, now)
return VisaCurrency(
symbol = VisaConfig.TOKEN_SYMBOL,
@ -35,8 +36,8 @@ internal class VisaCurrencyFactory {
)
},
limits = VisaCurrency.Limits(
remainingOtp = getRemainingOtp(currentLimit, now),
remainingNoOtp = getRemainingNoOtp(currentLimit, now),
remainingOtp = remainingOtpLimit,
remainingNoOtp = minOf(remainingOtpLimit, getRemainingNoOtp(currentLimit, now)),
singleTransaction = currentLimit.singleTransactionLimit,
expirationDate = getLimitsExpirationDate(currentLimit, now),
),

View file

@ -0,0 +1,28 @@
package com.tangem.data.visa
import androidx.paging.PagingData
import com.tangem.domain.visa.model.VisaCurrency
import com.tangem.domain.visa.model.VisaTxDetails
import com.tangem.domain.visa.model.VisaTxHistoryItem
import com.tangem.domain.visa.repository.VisaRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
internal class DummyVisaRepository : VisaRepository {
override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency {
TODO("Not implemented for this build type")
}
override suspend fun getTxHistory(
userWalletId: UserWalletId,
pageSize: Int,
isRefresh: Boolean,
): Flow<PagingData<VisaTxHistoryItem>> {
TODO("Not implemented for this build type")
}
override suspend fun getTxDetails(userWalletId: UserWalletId, txId: String): VisaTxDetails {
TODO("Not implemented for this build type")
}
}

View file

@ -0,0 +1,6 @@
package com.tangem.data.visa.di
import javax.inject.Qualifier
@Qualifier
internal annotation class ImplementedVisaRepository

View file

@ -0,0 +1,16 @@
package com.tangem.data.visa.di
import com.tangem.domain.visa.repository.VisaRepository
import dagger.BindsOptionalOf
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
internal interface ImplementedVisaRepositoryModule {
@BindsOptionalOf
@ImplementedVisaRepository
fun bindImplementedVisaRepository(): VisaRepository
}

View file

@ -1,21 +1,14 @@
package com.tangem.data.visa.di
import com.squareup.moshi.Moshi
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.visa.BuildConfig
import com.tangem.data.visa.DefaultVisaRepository
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.data.visa.DummyVisaRepository
import com.tangem.domain.visa.repository.VisaRepository
import com.tangem.lib.visa.VisaContractInfoProvider
import com.tangem.lib.visa.api.VisaApiBuilder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import java.util.Optional
import javax.inject.Singleton
import kotlin.jvm.optionals.getOrNull
@Module
@InstallIn(SingletonComponent::class)
@ -24,29 +17,8 @@ internal object VisaDataModule {
@Provides
@Singleton
fun provideVisaRepository(
@NetworkMoshi moshi: Moshi,
tangemTechApi: TangemTechApi,
cacheRegistry: CacheRegistry,
userWalletsStore: UserWalletsStore,
dispatchers: CoroutineDispatcherProvider,
@ImplementedVisaRepository implementedVisaRepository: Optional<VisaRepository>,
): VisaRepository {
val contractInfoProvider = VisaContractInfoProvider.Builder(
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
dispatchers = dispatchers,
).build()
val visaApi = VisaApiBuilder(
useDevApi = true,
isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED,
moshi = moshi,
).build()
return DefaultVisaRepository(
contractInfoProvider,
tangemTechApi,
visaApi,
cacheRegistry,
userWalletsStore,
dispatchers,
)
return implementedVisaRepository.getOrNull() ?: DummyVisaRepository()
}
}

View file

@ -19,7 +19,7 @@ internal data class BalancesAndLimitsBottomSheetConfig(
data class Limit(
val availableBy: String,
val inStore: String,
val total: String,
val other: String,
val singleTransaction: String,
val onInfoClick: () -> Unit,

View file

@ -48,19 +48,32 @@ internal sealed interface WalletAlertState {
override val isWarningConfirmButton: Boolean = true
}
object WrongCardIsScanned : Basic() {
data class VisaLimitsInfo(
val totalLimit: String,
val otherLimit: String,
) : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Limits are needed to control costs, improve security, manage risk. " +
"You can spend $totalLimit during the week for card payments in shops and " +
"$otherLimit for other transactions, e. g. subscriptions or debts.",
)
override val onConfirmClick: (() -> Unit)? = null
}
data object WrongCardIsScanned : Basic() {
override val title: TextReference = resourceReference(R.string.common_warning)
override val message: TextReference = resourceReference(R.string.error_wrong_wallet_tapped)
override val onConfirmClick: (() -> Unit)? = null
}
object RescanWallets : Basic() {
data object RescanWallets : Basic() {
override val title: TextReference = resourceReference(R.string.common_attention)
override val message: TextReference = resourceReference(R.string.key_invalidated_warning_description)
override val onConfirmClick: (() -> Unit)? = null
}
object VisaBalancesInfo : Basic() {
data object VisaBalancesInfo : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Available balance is actual funds available, considering pending transactions, " +
@ -68,14 +81,4 @@ internal sealed interface WalletAlertState {
)
override val onConfirmClick: (() -> Unit)? = null
}
object VisaLimitsInfo : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Limits are needed to control costs, improve security, manage risk. " +
"You can spend 1 000 USDT during the week for card payments in shops and " +
"100 USDT for other transactions, e. g. subscriptions or debts.",
)
override val onConfirmClick: (() -> Unit)? = null
}
}

View file

@ -21,6 +21,9 @@ internal class BalancesAndLimitsBottomSheetConverter(
decimals = value.decimals,
)
val otpLimit = value.limits.remainingOtp.let(::formatAmount)
val noOtpLimit = value.limits.remainingNoOtp.let(::formatAmount)
return BalancesAndLimitsBottomSheetConfig(
balance = BalancesAndLimitsBottomSheetConfig.Balance(
totalBalance = value.balances.total.let(::formatAmount),
@ -33,10 +36,10 @@ internal class BalancesAndLimitsBottomSheetConverter(
),
limit = BalancesAndLimitsBottomSheetConfig.Limit(
availableBy = DateTimeFormatters.formatDate(date = value.limits.expirationDate),
inStore = value.limits.remainingOtp.let(::formatAmount),
other = value.limits.remainingNoOtp.let(::formatAmount),
total = otpLimit,
other = noOtpLimit,
singleTransaction = value.limits.singleTransaction.let(::formatAmount),
onInfoClick = this::showLimitInfo,
onInfoClick = { showLimitInfo(otpLimit, noOtpLimit) },
),
)
}
@ -45,7 +48,7 @@ internal class BalancesAndLimitsBottomSheetConverter(
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo))
}
private fun showLimitInfo() {
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaLimitsInfo))
private fun showLimitInfo(totalLimit: String, otherLimit: String) {
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaLimitsInfo(totalLimit, otherLimit)))
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.core.ui.extensions.capitalize
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.domain.visa.model.VisaCurrency
@ -27,14 +28,14 @@ internal class VisaTxDetailsBottomSheetConverter(
private fun createTransaction(details: VisaTxDetails): VisaTxDetailsBottomSheetConfig.Transaction {
return VisaTxDetailsBottomSheetConfig.Transaction(
id = details.id,
type = details.type,
status = details.status,
type = details.type.capitalize(),
status = details.status.capitalize(),
blockchainAmount = formatNetworkAmount(details.blockchainAmount),
blockchainFee = formatNetworkAmount(details.blockchainFee),
transactionAmount = formatFiatAmount(details.transactionAmount, details.fiatCurrency),
transactionCurrencyCode = details.transactionCurrencyCode.toString(),
merchantName = details.merchantName ?: UNKNOWN,
merchantCity = details.merchantCity ?: UNKNOWN,
merchantName = details.merchantName?.capitalize() ?: UNKNOWN,
merchantCity = details.merchantCity?.capitalize() ?: UNKNOWN,
merchantCountryCode = details.merchantCountryCode ?: UNKNOWN,
merchantCategoryCode = details.merchantCategoryCode ?: UNKNOWN,
)
@ -46,16 +47,16 @@ internal class VisaTxDetailsBottomSheetConverter(
return VisaTxDetailsBottomSheetConfig.Request(
id = request.id,
type = request.requestType,
status = request.requestStatus,
type = request.requestType.capitalize(),
status = request.requestStatus.capitalize(),
blockchainAmount = formatNetworkAmount(request.blockchainAmount),
blockchainFee = formatNetworkAmount(request.blockchainFee),
transactionAmount = formatFiatAmount(request.transactionAmount, request.fiatCurrency),
currencyCode = request.billingCurrencyCode.toString(),
errorCode = request.errorCode,
date = DateTimeFormatters.formatDate(DateTimeFormatters.dateTimeFormatter, date = localDate),
date = DateTimeFormatters.formatDate(localDate, DateTimeFormatters.dateTimeFormatter),
txHash = request.txHash ?: UNKNOWN,
txStatus = request.txStatus ?: UNKNOWN,
txStatus = request.txStatus?.capitalize() ?: UNKNOWN,
onExploreClick = if (exploreUrl != null) {
{ clickIntents.onExploreClick(exploreUrl) }
} else {

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.extensions.capitalize
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.DateTimeFormatters
@ -18,8 +19,8 @@ internal class VisaTxHistoryItemStateConverter(
override fun convert(value: VisaTxHistoryItem): TransactionState {
val localDate = value.date.withZone(DateTimeZone.getDefault())
val time = DateTimeFormatters.formatTime(time = localDate)
val subtitle = "$time${value.status}"
val time = DateTimeFormatters.formatDate(localDate, DateTimeFormatters.timeFormatter)
val subtitle = "$time${value.status.capitalize()}"
return TransactionState.Content(
txHash = value.id,
@ -37,7 +38,7 @@ internal class VisaTxHistoryItemStateConverter(
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
iconRes = R.drawable.ic_arrow_up_24,
title = stringReference(value = value.merchantName ?: "Unknown merchant"),
title = stringReference(value = value.merchantName?.capitalize() ?: "Unknown merchant"),
subtitle = stringReference(subtitle),
timestamp = localDate.millis,
onClick = { clickIntents.onVisaTransactionClick(value.id) },

View file

@ -42,6 +42,7 @@ internal class VisaWalletSubscriber(
setLoadedCurrencyState(maybeCurrency)
val currency = maybeCurrency.getOrElse {
Timber.e(it, "Failed to load VISA currency")
setFailedTxHistoryState(it)
return@flow
}

View file

@ -4,7 +4,10 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -60,7 +63,6 @@ private fun BalancesAndLimitsBlock(state: BalancesAndLimitsBlockState, modifier:
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private inline fun ContentContainer(
enabled: Boolean,
@ -147,7 +149,7 @@ private fun AvailableLimit(availableBalance: String, limitDays: Int, modifier: M
color = TangemTheme.colors.text.primary1,
)
Text(
text = "available $limitDays-day limit",
text = "available for $limitDays day(s)",
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)

View file

@ -94,8 +94,8 @@ private fun LimitsBlock(limits: BalancesAndLimitsBottomSheetConfig.Limit, modifi
title = stringReference("Limits"),
content = {
BlockItem(
title = stringReference("In-store (otp)"),
value = limits.inStore,
title = stringReference("Total"),
value = limits.total,
)
BlockItem(
title = stringReference("Other (no-otp)"),
@ -193,7 +193,7 @@ private class BalancesAndLimitsBottomSheetParameterProvider :
),
limit = BalancesAndLimitsBottomSheetConfig.Limit(
availableBy = "Nov, 11 USDT",
inStore = "563.00 USDT",
total = "563.00 USDT",
other = "100.00 USDT",
singleTransaction = "100.00 USDT",
onInfoClick = {},

View file

@ -2,10 +2,7 @@ package com.tangem.lib.visa;
import org.web3j.abi.EventEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Event;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.*;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint8;
import org.web3j.crypto.Credentials;
@ -32,10 +29,10 @@ import io.reactivex.Flowable;
* <p>Auto generated code.
* <p><strong>Do not modify!</strong>
* <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
* or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the
* or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the
* <a href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
*
* <p>Generated with web3j version 1.5.0.
* <p>Generated with web3j version 1.5.2.
*/
@SuppressWarnings("rawtypes")
class ERC20 extends Contract {
@ -113,6 +110,16 @@ class ERC20 extends Contract {
return typedResponse;
}
@Deprecated
public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new ERC20(contractAddress, web3j, credentials, gasPrice, gasLimit);
}
@Deprecated
public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return new ERC20(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
public static List<TransferEventResponse> getTransferEvents(TransactionReceipt transactionReceipt) {
List<EventValuesWithLog> valueList = staticExtractEventParametersWithLog(TRANSFER_EVENT, transactionReceipt);
ArrayList<TransferEventResponse> responses = new ArrayList<TransferEventResponse>(valueList.size());
@ -127,16 +134,6 @@ class ERC20 extends Contract {
return responses;
}
@Deprecated
public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new ERC20(contractAddress, web3j, credentials, gasPrice, gasLimit);
}
@Deprecated
public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return new ERC20(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
public static TransferEventResponse getTransferEventFromLog(Log log) {
EventValuesWithLog eventValues = staticExtractEventParametersWithLog(TRANSFER_EVENT, log);
TransferEventResponse typedResponse = new TransferEventResponse();
@ -147,14 +144,6 @@ class ERC20 extends Contract {
return typedResponse;
}
public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
return new ERC20(contractAddress, web3j, credentials, contractGasProvider);
}
public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return new ERC20(contractAddress, web3j, transactionManager, contractGasProvider);
}
public Flowable<TransferEventResponse> transferEventFlowable(EthFilter filter) {
return web3j.ethLogFlowable(filter).map(log -> getTransferEventFromLog(log));
}
@ -165,6 +154,14 @@ class ERC20 extends Contract {
return transferEventFlowable(filter);
}
public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
return new ERC20(contractAddress, web3j, credentials, contractGasProvider);
}
public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return new ERC20(contractAddress, web3j, transactionManager, contractGasProvider);
}
public Flowable<ApprovalEventResponse> approvalEventFlowable(EthFilter filter) {
return web3j.ethLogFlowable(filter).map(log -> getApprovalEventFromLog(log));
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,7 +7,6 @@ import com.tangem.lib.visa.model.VisaBalancesAndLimits.Limits
import com.tangem.lib.visa.utils.toBigDecimal
import com.tangem.lib.visa.utils.toInstant
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import org.joda.time.Instant
import org.web3j.protocol.Web3j
import org.web3j.tx.TransactionManager
@ -18,39 +17,40 @@ internal class DefaultVisaContractInfoProvider(
private val transactionManager: TransactionManager,
private val gasProvider: ContractGasProvider,
private val bridgeProcessorAddress: String,
private val paymentAccountRegistryAddress: String,
private val dispatchers: CoroutineDispatcherProvider,
) : VisaContractInfoProvider {
override suspend fun getBalancesAndLimits(walletAddress: String): VisaBalancesAndLimits {
return withContext(dispatchers.io) {
val tangemBridgeProcessor = TangemBridgeProcessor.load(
/* contractAddress = */ bridgeProcessorAddress,
/* web3j = */ web3j,
/* transactionManager = */ transactionManager,
/* contractGasProvider = */ gasProvider,
)
parZip(
dispatchers.io,
{ loadPaymentAccount(walletAddress, tangemBridgeProcessor) },
{ loadPaymentTokenInfo(tangemBridgeProcessor) },
{ paymentAccount, paymentToken ->
fetchBalancesAndLimits(paymentAccount, paymentToken)
},
)
}
return parZip(
dispatchers.io,
{ loadPaymentAccount(walletAddress) },
{ loadPaymentTokenInfo() },
{ paymentAccount, paymentToken ->
fetchBalancesAndLimits(paymentAccount, paymentToken)
},
)
}
private fun loadPaymentAccount(
walletAddress: String,
tangemBridgeProcessor: TangemBridgeProcessor,
): TangemPaymentAccount {
val paymentAccountAddress = tangemBridgeProcessor.getPaymentAccount(walletAddress).send()
private fun loadPaymentAccount(walletAddress: String): TangemPaymentAccount {
val paymentAccountRegistry = TangemPaymentAccountRegistry.load(
/* contractAddress = */ paymentAccountRegistryAddress,
/* web3j = */ web3j,
/* transactionManager = */ transactionManager,
/* contractGasProvider = */ gasProvider,
)
val paymentAccountAddress = paymentAccountRegistry.paymentAccountByCard(walletAddress).send()
return TangemPaymentAccount.load(paymentAccountAddress, web3j, transactionManager, gasProvider)
}
private fun loadPaymentTokenInfo(tangemBridgeProcessor: TangemBridgeProcessor): PaymentTokenInfo {
private fun loadPaymentTokenInfo(): PaymentTokenInfo {
val tangemBridgeProcessor = TangemBridgeProcessor.load(
/* contractAddress = */ bridgeProcessorAddress,
/* web3j = */ web3j,
/* transactionManager = */ transactionManager,
/* contractGasProvider = */ gasProvider,
)
val paymentTokenContractAddress = tangemBridgeProcessor.paymentToken().send()
val paymentTokenContract = ERC20.load(paymentTokenContractAddress, web3j, transactionManager, gasProvider)
val paymentTokenDecimals = paymentTokenContract.decimals().send()

View file

@ -28,6 +28,7 @@ interface VisaContractInfoProvider {
private val dispatchers: CoroutineDispatcherProvider,
private val baseUrl: String = VisaConfig.BASE_RPC_URL,
private val bridgeProcessorAddress: String = VisaConfig.BRIDGE_PROCESSOR_CONTRACT_ADDRESS,
private val paymentAccountRegistryAddress: String = VisaConfig.PAYMENT_ACCOUNT_REGISTRY_ADDRESS,
private val chainId: Long = VisaConfig.CHAIN_ID,
private val networkTimeoutSeconds: Long = VisaConfig.NETWORK_TIMEOUT_SECONDS,
private val decimals: Int = VisaConfig.DECIMALS,
@ -45,6 +46,7 @@ interface VisaContractInfoProvider {
transactionManager = transactionManager,
gasProvider = gasProvider,
bridgeProcessorAddress = bridgeProcessorAddress,
paymentAccountRegistryAddress = paymentAccountRegistryAddress,
dispatchers = dispatchers,
)
}

View file

@ -2,8 +2,9 @@ package com.tangem.lib.visa.utils
internal object VisaConfig {
const val BASE_RPC_URL = "https://rpc-mumbai.maticvigil.com/"
const val BRIDGE_PROCESSOR_CONTRACT_ADDRESS = "0x62119697e78178512bfcc456ae6d1b7dee9fbaa6"
const val BASE_RPC_URL = "https://polygon-mumbai.g.alchemy.com/v2/_1qqjXgBC_IikaXChnna8KTcV2eMMIQG/"
const val BRIDGE_PROCESSOR_CONTRACT_ADDRESS = "0xe32ecbbc1ec17fa9c160569cd613ad568ca50279"
const val PAYMENT_ACCOUNT_REGISTRY_ADDRESS = "0x3f4ae01073d1a9d5a92315fe118e57d1cdec7c44"
const val CHAIN_ID = 80_001L
const val DECIMALS = 9
const val GAS_LIMIT = 500_000_000L