diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index dfcdf942bf..0887023b32 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -3,28 +3,37 @@ package com.tangem.tap.common.redux.global import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard -import com.tangem.common.extensions.ifNotNull import com.tangem.common.services.Result +import com.tangem.domain.common.CardDTO import com.tangem.domain.common.LogConfig +import com.tangem.domain.common.ProductType +import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState +import com.tangem.tap.domain.configurable.config.Config import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.network.exchangeServices.BuyExchangeService import com.tangem.tap.network.exchangeServices.CardExchangeRules import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager -import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoApi +import com.tangem.tap.network.exchangeServices.ExchangeService +import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoEnvironment import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService +import com.tangem.tap.network.exchangeServices.utorg.UtorgAuthProvider +import com.tangem.tap.network.exchangeServices.utorg.UtorgEnvironment +import com.tangem.tap.network.exchangeServices.utorg.UtorgExchangeService import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager import com.tangem.tap.userTokensRepository +import com.tangem.wallet.BuildConfig import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.DispatchFunction @@ -121,38 +130,23 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di } is GlobalAction.ExchangeManager.Init -> { val appStateSafe = appState() ?: return - val config = appStateSafe.globalState.configManager?.config - ifNotNull( - config?.mercuryoWidgetId, - config?.mercuryoSecret, - config?.moonPayApiKey, - config?.moonPayApiSecretKey, - ) { mercuryoWidgetId, mercuryoSecret, moonPayKey, moonPaySecretKey -> - scope.launch { - val buyService = MercuryoService( - apiVersion = MercuryoApi.API_VERSION, - mercuryoWidgetId = mercuryoWidgetId, - secret = mercuryoSecret, - logEnabled = LogConfig.network.mercuryoService, - ) - val sellService = MoonPayService( - apiKey = moonPayKey, - secretKey = moonPaySecretKey, - logEnabled = LogConfig.network.moonPayService, - ) - val cardProvider = { - store.state.globalState.scanResponse?.card - ?: store.state.globalState.onboardingState.onboardingManager?.scanResponse?.card - } + val config = appStateSafe.globalState.configManager?.config ?: return - val exchangeManager = CurrencyExchangeManager( - buyService = buyService, - sellService = sellService, - primaryRules = CardExchangeRules(cardProvider), - ) - store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager)) - store.dispatchOnMain(GlobalAction.ExchangeManager.Update) + scope.launch { + val scanResponseProvider: () -> ScanResponse? = { + store.state.globalState.scanResponse + ?: store.state.globalState.onboardingState.onboardingManager?.scanResponse } + val productTypeProvider: () -> ProductType? = { scanResponseProvider.invoke()?.productType } + val cardProvider: () -> CardDTO? = { scanResponseProvider.invoke()?.card } + + val exchangeManager = CurrencyExchangeManager( + buyService = makeBuyExchangeService(config, productTypeProvider), + sellService = makeSellExchangeService(config), + primaryRules = CardExchangeRules(cardProvider), + ) + store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager)) + store.dispatchOnMain(GlobalAction.ExchangeManager.Update) } } is GlobalAction.ExchangeManager.Init.Success -> {} @@ -207,4 +201,38 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di } } } +} + +private fun makeSellExchangeService(config: Config): ExchangeService { + return MoonPayService( + apiKey = config.moonPayApiKey, + secretKey = config.moonPayApiSecretKey, + logEnabled = LogConfig.network.moonPayService, + ) +} + +private fun makeBuyExchangeService(config: Config, productTypeProvider: () -> ProductType?): ExchangeService { + return BuyExchangeService( + productTypeProvider = productTypeProvider, + mercuryoService = makeMercuryoExchangeService(config), + utorgService = makeUtorgExchangeService(config), + ) +} + +private fun makeMercuryoExchangeService(config: Config): MercuryoService { + val mercuryoEnvironment = MercuryoEnvironment.prod(config.mercuryoWidgetId, config.mercuryoSecret) + return MercuryoService(mercuryoEnvironment) +} + +private fun makeUtorgExchangeService(config: Config): UtorgExchangeService { + val saltPayConfig = requireNotNull(config.saltPayConfig) + + val utorgAuthProvider = UtorgAuthProvider(saltPayConfig.kycProvider.sidValue) + val utorgEnvironment = if (BuildConfig.DEBUG) { + UtorgEnvironment.stage(utorgAuthProvider, LogConfig.network.utorgService) + // UtorgEnvironment.mock() + } else { + UtorgEnvironment.prod(utorgAuthProvider) + } + return UtorgExchangeService(utorgEnvironment) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 33b7f54788..e896251b09 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.wallet.redux.middlewares import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.common.AmountType +import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Token @@ -17,6 +18,7 @@ import com.tangem.tap.features.send.redux.PrepareSendScreen import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens import com.tangem.tap.scope @@ -40,10 +42,9 @@ class TradeCryptoMiddleware { action: WalletAction.TradeCryptoAction.Buy, ) { val selectedWalletData = store.state.walletState.selectedWalletData ?: return + val currency = chooseAppropriateCurrency(store.state.walletState) ?: return - val currency = selectedWalletData.currency Analytics.send(Token.ButtonBuy(AnalyticsParam.CurrencyType.Currency(currency))) - if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) { store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog()) return @@ -87,12 +88,12 @@ class TradeCryptoMiddleware { private fun proceedSellAction() { val selectedWalletData = store.state.walletState.selectedWalletData ?: return + val currency = chooseAppropriateCurrency(store.state.walletState) ?: return val appCurrency = store.state.globalState.appCurrency val addresses = selectedWalletData.walletAddresses?.list.orEmpty() if (addresses.isEmpty()) return - val currency = selectedWalletData.currency Analytics.send(Token.ButtonSell(AnalyticsParam.CurrencyType.Currency(currency))) store.state.globalState.exchangeManager.getUrl( @@ -107,6 +108,17 @@ class TradeCryptoMiddleware { } } + private fun chooseAppropriateCurrency(walletState: WalletState): Currency? { + return if (walletState.primaryTokenData == null) { + walletState.selectedWalletData?.currency + } else { + walletState.primaryTokenData?.currency as? Currency.Token + }.guard { + store.dispatchDebugErrorNotification("Can't select an appropriate currency for a Trade action") + return null + } + } + private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) { val selectedWalletData = store.state.walletState.selectedWalletData ?: return diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt index 53d576c61d..9ffd48f58e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt @@ -119,7 +119,8 @@ class BalanceWidget( tvErrorDescriptions.text = fragment.getString( R.string.no_account_generic, - data.amountToCreateAccount, data.currencySymbol, + data.amountToCreateAccount, + data.currencySymbol, ) } BalanceStatus.UnknownBlockchain -> with(binding.lBalanceError) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index e38d9d947b..ba1fd0b30f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -435,7 +435,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), lBalanceError.tvErrorDescriptions.text = getString( R.string.no_account_generic, - data.amountToCreateAccount, data.currencySymbol, + data.amountToCreateAccount, + data.currencySymbol, ) } } diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/BuyExchangeService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/BuyExchangeService.kt new file mode 100644 index 0000000000..d77245b45c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/BuyExchangeService.kt @@ -0,0 +1,62 @@ +package com.tangem.tap.network.exchangeServices + +import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.common.ProductType +import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService +import com.tangem.tap.network.exchangeServices.utorg.UtorgExchangeService +import com.tangem.tap.scope +import kotlinx.coroutines.launch + +/** +[REDACTED_AUTHOR] + * Temporary wrapper for the buy services. Service switches based on selected product type. + * Just now - UtorgService used only for the SaltPay cards + */ +class BuyExchangeService( + private val productTypeProvider: () -> ProductType?, + private val mercuryoService: MercuryoService, + private val utorgService: UtorgExchangeService, +) : ExchangeService, ExchangeUrlBuilder { + + init { + scope.launch { + mercuryoService.update() + utorgService.update() + } + } + + private val currentService: ExchangeService + get() = when (productTypeProvider.invoke()) { + ProductType.SaltPay -> utorgService + else -> mercuryoService + } + + override suspend fun update() { + currentService.update() + } + + override fun featureIsSwitchedOn(): Boolean = currentService.featureIsSwitchedOn() + + override fun isBuyAllowed(): Boolean = currentService.isBuyAllowed() + + override fun isSellAllowed(): Boolean = currentService.isSellAllowed() + + override fun availableForBuy(currency: Currency): Boolean = currentService.availableForBuy(currency) + + override fun availableForSell(currency: Currency): Boolean = currentService.availableForSell(currency) + + override fun getUrl( + action: CurrencyExchangeManager.Action, + blockchain: Blockchain, + cryptoCurrencyName: String, + fiatCurrencyName: String, + walletAddress: String, + ): String? { + return currentService.getUrl(action, blockchain, cryptoCurrencyName, fiatCurrencyName, walletAddress) + } + + override fun getSellCryptoReceiptUrl(action: CurrencyExchangeManager.Action, transactionId: String): String? { + return currentService.getSellCryptoReceiptUrl(action, transactionId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index 3f61d82801..656343a092 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -24,7 +24,7 @@ class CurrencyExchangeManager( private val buyService: ExchangeService, private val sellService: ExchangeService, private val primaryRules: ExchangeRules, -) : ExchangeService, ExchangeUrlBuilder { +) : ExchangeService { override fun featureIsSwitchedOn(): Boolean = primaryRules.featureIsSwitchedOn() @@ -59,7 +59,7 @@ class CurrencyExchangeManager( blockchain, cryptoCurrencyName, fiatCurrencyName, - walletAddress + walletAddress, ) } @@ -97,8 +97,11 @@ suspend fun CurrencyExchangeManager.buyErc20TestnetTokens( val amountToSend = Amount(walletManager.wallet.blockchain) val destinationAddress = token.contractAddress - val feeResult = walletManager.getFee(amountToSend, - destinationAddress) as? Result.Success ?: return + val feeResult = + walletManager.getFee( + amountToSend, + destinationAddress, + ) as? Result.Success ?: return val fee = feeResult.data[0] if ((walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO) < fee.value) { @@ -116,8 +119,8 @@ suspend fun CurrencyExchangeManager.buyErc20TestnetTokens( GlobalAction.UpdateWalletSignedHashes( walletSignedHashes = signResponse.totalSignedHashes, walletPublicKey = walletManager.wallet.publicKey.seedKey, - remainingSignatures = signResponse.remainingSignatures - ) + remainingSignatures = signResponse.remainingSignatures, + ), ) } walletManager.send(transaction, signer) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt index 8d6e8e2187..f38a8c64a1 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt @@ -7,11 +7,11 @@ import com.tangem.tap.features.wallet.models.Currency interface Exchanger { fun isBuyAllowed(): Boolean fun isSellAllowed(): Boolean - fun availableForBuy(currency: Currency):Boolean - fun availableForSell(currency: Currency):Boolean + fun availableForBuy(currency: Currency): Boolean + fun availableForSell(currency: Currency): Boolean } -interface ExchangeService: Feature, Exchanger { +interface ExchangeService : Feature, Exchanger, ExchangeUrlBuilder { suspend fun update() companion object { @@ -22,11 +22,23 @@ interface ExchangeService: Feature, Exchanger { override fun isSellAllowed(): Boolean = false override fun availableForBuy(currency: Currency): Boolean = false override fun availableForSell(currency: Currency): Boolean = false + override fun getUrl( + action: CurrencyExchangeManager.Action, + blockchain: Blockchain, + cryptoCurrencyName: String, + fiatCurrencyName: String, + walletAddress: String, + ): String? = null + + override fun getSellCryptoReceiptUrl( + action: CurrencyExchangeManager.Action, + transactionId: String, + ): String? = null } } } -interface ExchangeRules: Feature, Exchanger { +interface ExchangeRules : Feature, Exchanger { companion object { fun dummy(): ExchangeRules = object : ExchangeRules { diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt index 87565c3640..89308da1ce 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt @@ -11,11 +11,6 @@ interface MercuryoApi { suspend fun currencies( @Path("apiVersion") apiVersion: String, ): MercuryoCurrenciesResponse - - companion object { - const val BASE_URL = "https://api.mercuryo.io/" - const val API_VERSION = "v1.6" - } } data class MercuryoCurrenciesResponse( @@ -25,7 +20,7 @@ data class MercuryoCurrenciesResponse( data class Data( val fiat: List, val crypto: List, - val config: Config + val config: Config, ) data class Config( diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoEnvironment.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoEnvironment.kt new file mode 100644 index 0000000000..af3cda029a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoEnvironment.kt @@ -0,0 +1,36 @@ +package com.tangem.tap.network.exchangeServices.mercuryo + +import com.tangem.datasource.api.common.createRetrofitInstance + +/** +[REDACTED_AUTHOR] + */ +data class MercuryoEnvironment( + val baseUrl: String, + val apiVersion: String, + val widgetId: String, + val secret: String, + val mercuryoApi: MercuryoApi, +) { + companion object { + private const val BASE_URL = "https://api.mercuryo.io/" + private const val API_VERSION = "v1.6" + + fun prod( + widgetId: String, + secret: String, + apiVersion: String = API_VERSION, + ): MercuryoEnvironment { + return MercuryoEnvironment( + baseUrl = BASE_URL, + apiVersion = apiVersion, + widgetId = widgetId, + secret = secret, + mercuryoApi = createRetrofitInstance( + baseUrl = BASE_URL, + logEnabled = false, + ).create(MercuryoApi::class.java), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index 9dd2b9746f..64f2ae121a 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -6,7 +6,6 @@ import com.tangem.common.extensions.calculateSha512 import com.tangem.common.extensions.toHexString import com.tangem.common.services.Result import com.tangem.common.services.performRequest -import com.tangem.datasource.api.common.createRetrofitInstance import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager @@ -17,16 +16,10 @@ import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder [REDACTED_AUTHOR] */ class MercuryoService( - private val apiVersion: String, - private val mercuryoWidgetId: String, - private val secret: String, - private val logEnabled: Boolean, -) : ExchangeService, ExchangeUrlBuilder { + private val environment: MercuryoEnvironment, +) : ExchangeService { - private val api: MercuryoApi = createRetrofitInstance( - baseUrl = MercuryoApi.BASE_URL, - logEnabled = logEnabled, - ).create(MercuryoApi::class.java) + private val api: MercuryoApi = environment.mercuryoApi private val blockchainsAvailableToBuy = mutableListOf() private val tokensAvailableToBy = mutableMapOf>() @@ -34,7 +27,7 @@ class MercuryoService( override fun featureIsSwitchedOn(): Boolean = true override suspend fun update() { - when (val result = performRequest { api.currencies(apiVersion) }) { + when (val result = performRequest { api.currencies(environment.apiVersion) }) { is Result.Success -> { val response = result.data if (response.status == 200) { @@ -108,14 +101,14 @@ class MercuryoService( blockchain: Blockchain, cryptoCurrencyName: CryptoCurrencyName, fiatCurrencyName: String, - walletAddress: String + walletAddress: String, ): String { if (action == CurrencyExchangeManager.Action.Sell) throw UnsupportedOperationException() val builder = Uri.Builder() .scheme(ExchangeUrlBuilder.SCHEME) .authority("exchange.mercuryo.io") - .appendQueryParameter("widget_id", mercuryoWidgetId) + .appendQueryParameter("widget_id", environment.widgetId) .appendQueryParameter("type", action.name.lowercase()) .appendQueryParameter("currency", cryptoCurrencyName) .appendQueryParameter("address", walletAddress) @@ -128,13 +121,12 @@ class MercuryoService( } private fun signature(address: String): String { - return (address + secret).calculateSha512().toHexString().lowercase() + return (address + environment.secret).calculateSha512().toHexString().lowercase() } - override fun getSellCryptoReceiptUrl( action: CurrencyExchangeManager.Action, - transactionId: String + transactionId: String, ): String? = null private fun blockchainFromCurrencyName(currencyName: String): Blockchain? = when (currencyName) { diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt index 645d5182ed..0754f6c305 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt @@ -5,14 +5,13 @@ import android.util.Base64 import com.tangem.blockchain.common.Blockchain import com.tangem.common.services.Result import com.tangem.common.services.performRequest -import com.tangem.domain.common.extensions.withIOContext import com.tangem.datasource.api.common.createRetrofitInstance +import com.tangem.domain.common.extensions.withIOContext import com.tangem.tap.common.extensions.urlEncode import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.ExchangeService -import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder.Companion.SCHEME import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec @@ -21,7 +20,7 @@ class MoonPayService( private val apiKey: String, private val secretKey: String, private val logEnabled: Boolean, -) : ExchangeService, ExchangeUrlBuilder { +) : ExchangeService { private val api: MoonPayApi by lazy { createRetrofitInstance( @@ -103,7 +102,7 @@ class MoonPayService( blockchain: Blockchain, cryptoCurrencyName: CryptoCurrencyName, fatCurrency: String, - walletAddress: String + walletAddress: String, ): String? { if (action == CurrencyExchangeManager.Action.Buy) throw UnsupportedOperationException() @@ -130,7 +129,6 @@ class MoonPayService( .appendPath("transaction_receipt") .appendQueryParameter("transactionId", transactionId).build().toString() return url - } private fun createSignature(data: String): String { @@ -149,5 +147,5 @@ class MoonPayService( private data class MoonPayStatus( val availableForSell: List, val responseUserStatus: MoonPayUserStatus, - val responseCurrencies: List + val responseCurrencies: List, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/UtorgEnvironment.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/UtorgEnvironment.kt new file mode 100644 index 0000000000..b31e8aa8bf --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/UtorgEnvironment.kt @@ -0,0 +1,88 @@ +package com.tangem.tap.network.exchangeServices.utorg + +import android.net.Uri +import com.tangem.datasource.api.common.createRetrofitInstance +import com.tangem.tap.network.exchangeServices.utorg.api.UtorgApi +import com.tangem.tap.network.exchangeServices.utorg.mock.MockUtorgApi +import okhttp3.Interceptor +import okhttp3.Response +import java.util.* + +/** +[REDACTED_AUTHOR] + */ +data class UtorgEnvironment( + val baseUri: Uri, + val sidValue: String, + val apiVersion: String, + val utorgApi: UtorgApi, + val successUrl: String = "https://success.tangem.com", +) { + companion object { + private val PROD_BASE_URL = Uri.parse("https://app.utorg.pro") + private const val PROD_VERSION = "v1" + + private val STAGE_BASE_URL = Uri.parse("https://app-stage.utorg.pro") + private const val STAGE_VERSION = "v1" + + fun prod( + authProvider: UtorgAuthProvider, + apiVersion: String = PROD_VERSION, + ): UtorgEnvironment = UtorgEnvironment( + baseUri = PROD_BASE_URL, + sidValue = authProvider.sidValue, + apiVersion = apiVersion, + utorgApi = createApi(PROD_BASE_URL, authProvider, false), + ) + + fun stage( + authProvider: UtorgAuthProvider, + logEnabled: Boolean, + apiVersion: String = STAGE_VERSION, + ): UtorgEnvironment = UtorgEnvironment( + baseUri = STAGE_BASE_URL, + sidValue = authProvider.sidValue, + apiVersion = apiVersion, + utorgApi = createApi(STAGE_BASE_URL, authProvider, logEnabled), + ) + + fun mock(): UtorgEnvironment = UtorgEnvironment( + baseUri = Uri.EMPTY, + sidValue = "", + apiVersion = "", + utorgApi = MockUtorgApi(), + ) + + private fun createApi(baseUri: Uri, authProvider: UtorgAuthProvider, logEnabled: Boolean): UtorgApi { + return createRetrofitInstance( + baseUrl = "$baseUri/", + interceptors = listOf(UtorgAuthHeaderInterceptor(authProvider)), + logEnabled = logEnabled, + ).create(UtorgApi::class.java) + } + } +} + +data class UtorgAuthProvider( + val sidValue: String, + val headerSidKey: String = "X-AUTH-SID", + val headerNonceKey: String = "X-AUTH-NONCE", +) { + val clientNonce: String + get() = UUID.randomUUID().toString() +} + +private class UtorgAuthHeaderInterceptor( + private val authProvider: UtorgAuthProvider, +) : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + .newBuilder() + .addHeader(authProvider.headerSidKey, authProvider.sidValue) + .addHeader(authProvider.headerNonceKey, authProvider.clientNonce) + .build() + + return chain.proceed(request) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/UtorgExchangeService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/UtorgExchangeService.kt new file mode 100644 index 0000000000..21c8c48424 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/UtorgExchangeService.kt @@ -0,0 +1,117 @@ +package com.tangem.tap.network.exchangeServices.utorg + +import android.net.Uri +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.services.Result +import com.tangem.common.services.performRequest +import com.tangem.tap.common.redux.global.CryptoCurrencyName +import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager +import com.tangem.tap.network.exchangeServices.ExchangeService +import com.tangem.tap.network.exchangeServices.utorg.api.RequestSuccessUrl +import com.tangem.tap.network.exchangeServices.utorg.api.model.UtorgCurrencyData +import com.tangem.tap.network.exchangeServices.utorg.api.model.UtorgCurrencyType +import com.tangem.utils.converter.Converter + +/** +[REDACTED_AUTHOR] + */ +class UtorgExchangeService( + private val environment: UtorgEnvironment, + private val currencyConverter: Converter = ChainToBlockchainConverter(), +) : ExchangeService { + + private val api = environment.utorgApi + + private val utorgCurrencies = mutableListOf() + + override fun featureIsSwitchedOn(): Boolean = true + + override suspend fun update() { + when (val result = performRequest { api.getCurrency(environment.apiVersion) }) { + is Result.Success -> { + if (!result.data.isSuccess()) return + + val utorgCryptos = result.data.toSuccess().data.filter { + it.enabled && it.type == UtorgCurrencyType.CRYPTO + } + utorgCurrencies.clear() + utorgCurrencies.addAll(utorgCryptos) + + performRequest { + api.setSuccessUrl(environment.apiVersion, RequestSuccessUrl(environment.successUrl)) + } + } + is Result.Failure -> { + utorgCurrencies.clear() + } + } + } + + override fun isBuyAllowed(): Boolean = true + + override fun isSellAllowed(): Boolean = false + + override fun availableForBuy(currency: Currency): Boolean { + return true + if (!isBuyAllowed()) return false + + val foundUtorgCurrency = utorgCurrencies.firstOrNull { utorgCurrency -> + val utorgBlockchain = currencyConverter.convert(utorgCurrency) ?: return@firstOrNull false + + val isSameBlockchain = utorgBlockchain == currency.blockchain + val isSameSymbol = utorgCurrency.symbol.lowercase() == currency.currencySymbol.lowercase() + isSameBlockchain && isSameSymbol + } + + return foundUtorgCurrency != null + } + + override fun availableForSell(currency: Currency): Boolean = false + + override fun getUrl( + action: CurrencyExchangeManager.Action, + blockchain: Blockchain, + cryptoCurrencyName: CryptoCurrencyName, + fiatCurrencyName: String, + walletAddress: String, + ): String { + if (action == CurrencyExchangeManager.Action.Sell) throw UnsupportedOperationException() + + // https://app-stage.utorg.pro/direct/testSID/mvmeaWyWiuVYdZdySgofAt6CmKhJbRhaWA/?¤cy=BTC + val builder = Uri.Builder() + .scheme(environment.baseUri.scheme) + .authority(environment.baseUri.authority) + .appendPath("direct") + .appendPath(environment.sidValue) + .appendPath(walletAddress) + .appendQueryParameter("currency", cryptoCurrencyName) + // if we set the paymentCurrency, then the Utorg widget didn't work + // .appendQueryParameter("paymentCurrency", "USD") + + val url = builder.build().toString() + return url + } + + override fun getSellCryptoReceiptUrl(action: CurrencyExchangeManager.Action, transactionId: String): String? = null +} + +private class ChainToBlockchainConverter : Converter { + override fun convert(value: UtorgCurrencyData): Blockchain? { + return when (value.chain?.uppercase()) { + "ARBITRUM" -> Blockchain.Arbitrum + "AVALANCHE" -> Blockchain.Avalanche + "BINANCE_SMART_CHAIN" -> Blockchain.BSC + "BITCOIN" -> Blockchain.Bitcoin + "BNB" -> Blockchain.Binance + "ETHEREUM" -> Blockchain.Ethereum + "GNOSIS" -> Blockchain.Gnosis + "POLYGON" -> Blockchain.Polygon + "RIPPLE" -> Blockchain.XRP + "RSK" -> Blockchain.RSK + "SOLANA" -> Blockchain.Solana + else -> null + // "APTOS", "ATOM", "NEAR", "ZKSYNC", "VECHAIN", "VELAS", "VELAS_EVM" -> null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/api/UtorgApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/api/UtorgApi.kt new file mode 100644 index 0000000000..6244d39a5e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/api/UtorgApi.kt @@ -0,0 +1,67 @@ +package com.tangem.tap.network.exchangeServices.utorg.api + +import com.squareup.moshi.Json +import com.tangem.tap.network.exchangeServices.utorg.api.model.UtorgCurrencyResponse +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Path + +/** +[REDACTED_AUTHOR] + */ +interface UtorgApi { + + @POST("api/merchant/{apiVersion}/settings/currency") + suspend fun getCurrency( + @Path("apiVersion") apiVersion: String, + ): UtorgCurrencyResponse + + @POST("api/merchant/{apiVersion}/settings/successUrl") + suspend fun setSuccessUrl( + @Path("apiVersion") apiVersion: String, + @Body request: RequestSuccessUrl, + ) +} + +data class RequestSuccessUrl( + val url: String, +) + +interface UtorgResponse { + val success: Boolean + val timestamp: Long + val data: Data? + val error: UtorgErrorResponse? + + fun isSuccess(): Boolean = success && data != null && error == null + + @Throws(NullPointerException::class) + fun toSuccess(): UtorgSuccessResponse { + val internalTimestamp = this.timestamp + val internalData = this.data + + return object : UtorgSuccessResponse { + override val timestamp: Long = internalTimestamp + override val data: Data = internalData!! + } + } + + @Throws(NullPointerException::class) + fun toError(): UtorgErrorResponse = error!! +} + +interface UtorgSuccessResponse { + val timestamp: Long + val data: T +} + +data class UtorgErrorResponse( + @Json(name = "message") val message: String?, + @Json(name = "type") val type: UtorgErrorType, +) + +enum class UtorgErrorType { + UNAUTHORIZED, + UNKNOWN_ERROR, + BAD_REQUEST, +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/api/model/UtorgCurrencyResponse.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/api/model/UtorgCurrencyResponse.kt new file mode 100644 index 0000000000..5df98687d8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/api/model/UtorgCurrencyResponse.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.network.exchangeServices.utorg.api.model + +import com.squareup.moshi.Json +import com.tangem.tap.network.exchangeServices.utorg.api.UtorgErrorResponse +import com.tangem.tap.network.exchangeServices.utorg.api.UtorgResponse + +class UtorgCurrencyResponse( + @Json(name = "success") override val success: Boolean, + @Json(name = "timestamp") override val timestamp: Long, + @Json(name = "data") override val data: List?, + @Json(name = "error") override val error: UtorgErrorResponse?, +) : UtorgResponse> + +data class UtorgCurrencyData( + @Json(name = "currency") val currency: String, + @Json(name = "symbol") val symbol: String, + @Json(name = "enabled") val enabled: Boolean, + @Json(name = "type") val type: UtorgCurrencyType, + @Json(name = "caption") val caption: String?, + @Json(name = "chain") val chain: String?, +) + +enum class UtorgCurrencyType { + FIAT, CRYPTO +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/MockUtorgApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/MockUtorgApi.kt new file mode 100644 index 0000000000..e50b8b8833 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/MockUtorgApi.kt @@ -0,0 +1,58 @@ +package com.tangem.tap.network.exchangeServices.utorg.mock + +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.tap.network.exchangeServices.utorg.api.RequestSuccessUrl +import com.tangem.tap.network.exchangeServices.utorg.api.UtorgApi +import com.tangem.tap.network.exchangeServices.utorg.api.UtorgErrorResponse +import com.tangem.tap.network.exchangeServices.utorg.api.UtorgErrorType +import com.tangem.tap.network.exchangeServices.utorg.api.model.UtorgCurrencyResponse +import kotlinx.coroutines.delay + +/** +[REDACTED_AUTHOR] + */ +class MockUtorgApi : UtorgApi { + + var nextDelay = 500L + + var nextResponseType = ResponseType.Success + var nextErrorType = UtorgErrorType.UNKNOWN_ERROR + + override suspend fun getCurrency(apiVersion: String): UtorgCurrencyResponse { + delay(nextDelay) + return UtorgCurrencyResponse( + success = createIsSuccess(), + timestamp = createTime(), + data = createData(MockUtorgSuccessDataResponse.GetCurrency), + error = createError(), + ) + } + + override suspend fun setSuccessUrl(apiVersion: String, request: RequestSuccessUrl) { + delay(nextDelay) + } + + private fun createIsSuccess(): Boolean = when (nextResponseType) { + ResponseType.Success -> true + ResponseType.Error -> false + } + + private fun createTime(): Long = System.currentTimeMillis() + + private inline fun createData(response: MockUtorgSuccessDataResponse): T? = when (nextResponseType) { + ResponseType.Success -> MoshiJsonConverter.INSTANCE.fromJson(response.json)!! + ResponseType.Error -> null + } + + private fun createError(): UtorgErrorResponse? = when (nextResponseType) { + ResponseType.Success -> null + ResponseType.Error -> UtorgErrorResponse( + message = "Optional message", + type = nextErrorType, + ) + } + + enum class ResponseType { + Success, Error + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/MockUtorgSuccessDataResponse.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/MockUtorgSuccessDataResponse.kt new file mode 100644 index 0000000000..959e65a4d5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/MockUtorgSuccessDataResponse.kt @@ -0,0 +1,12 @@ +package com.tangem.tap.network.exchangeServices.utorg.mock + +import com.tangem.tap.network.exchangeServices.utorg.mock.json.getCurrencyFull + +/** +[REDACTED_AUTHOR] + */ +sealed class MockUtorgSuccessDataResponse( + val json: String, +) { + object GetCurrency : MockUtorgSuccessDataResponse(getCurrencyFull) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/json/CurrencyResponseJson.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/json/CurrencyResponseJson.kt new file mode 100644 index 0000000000..3d50462305 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/utorg/mock/json/CurrencyResponseJson.kt @@ -0,0 +1,1025 @@ +package com.tangem.tap.network.exchangeServices.utorg.mock.json + +/** +[REDACTED_AUTHOR] + */ +internal val getCurrencyFull = """ +[ + { + "currency": "APT", + "symbol": "APT", + "chain": "APTOS", + "display": "APT", + "caption": "Aptos", + "explorerTx": "https://aptoscan.com/version/", + "explorerAddr": "https://aptoscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 1.8492, + "depositMax": 739.6673, + "withdrawalMin": 1.8492, + "withdrawalMax": 739.6673, + "addressValidator": "APT", + "precision": 4, + "allowTag": false + }, + { + "currency": "ATOM", + "symbol": "ATOM", + "chain": "ATOM", + "display": "ATOM", + "caption": "Cosmos", + "explorerTx": "https://atomscan.com/transactions/", + "explorerAddr": "https://atomscan.com/accounts/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 1.8020, + "depositMax": 720.7911, + "withdrawalMin": 1.8020, + "withdrawalMax": 720.7911, + "addressValidator": "COSMOS", + "precision": 4, + "allowTag": true, + "udKey": "crypto.ATOM.address" + }, + { + "currency": "AUD", + "symbol": "AUD", + "display": "AUD", + "type": "FIAT", + "enabled": true, + "depositMin": 43.73, + "depositMax": 15013.68, + "withdrawalMin": 43.73, + "withdrawalMax": 15013.68, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "AVAXAVA", + "symbol": "AVAX", + "chain": "AVALANCHE", + "display": "Avax", + "caption": "Avalanche", + "explorerTx": "", + "explorerAddr": "", + "type": "CRYPTO", + "enabled": true, + "depositMin": 1.337428, + "depositMax": 534.971102, + "withdrawalMin": 1.337428, + "withdrawalMax": 534.971102, + "addressValidator": "ETH", + "precision": 6, + "allowTag": false, + "udKey": "crypto.AVAX.address" + }, + { + "currency": "BNB", + "symbol": "BNB", + "chain": "BNB", + "display": "BNB", + "caption": "BEP2", + "explorerTx": "https://explorer.binance.org/tx/", + "explorerAddr": "https://explorer.binance.org/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 0.082738, + "depositMax": 33.095138, + "withdrawalMin": 0.082738, + "withdrawalMax": 33.095138, + "addressValidator": "BNB", + "precision": 6, + "allowTag": true, + "udKey": "crypto.BNB.address" + }, + { + "currency": "BNBBSC", + "symbol": "BNB", + "chain": "BINANCE_SMART_CHAIN", + "display": "BNB", + "caption": "BEP20", + "explorerTx": "https://www.bscscan.com/tx/", + "explorerAddr": "https://www.bscscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 0.0828, + "depositMax": 33.0951, + "withdrawalMin": 0.0828, + "withdrawalMax": 33.0951, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "BRL", + "symbol": "BRL", + "display": "BRL", + "type": "FIAT", + "enabled": true, + "depositMin": 158.08, + "depositMax": 54293.21, + "withdrawalMin": 158.08, + "withdrawalMax": 54293.21, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "BTC", + "symbol": "BTC", + "chain": "BITCOIN", + "display": "BTC", + "caption": "", + "explorerTx": "https://blockstream.info/tx/", + "explorerAddr": "https://blockstream.info/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 528764.805415, + "depositMax": 211505922.165820, + "withdrawalMin": 528764.805415, + "withdrawalMax": 211505922.165820, + "addressValidator": "BTC", + "precision": 6, + "allowTag": false, + "udKey": "crypto.BTC.address" + }, + { + "currency": "BUSDAVA", + "symbol": "BUSD", + "chain": "AVALANCHE", + "display": "BUSD", + "caption": "on Avalanche", + "explorerTx": "https://snowtrace.io/tx/", + "explorerAddr": "https://snowtrace.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6382, + "depositMax": 10655.2775, + "withdrawalMin": 26.6382, + "withdrawalMax": 10655.2775, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "BUSDBSC", + "symbol": "BUSD", + "chain": "BINANCE_SMART_CHAIN", + "display": "BUSD", + "caption": "BEP20", + "explorerTx": "https://www.bscscan.com/tx/", + "explorerAddr": "https://www.bscscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6414, + "depositMax": 10656.5563, + "withdrawalMin": 26.6414, + "withdrawalMax": 10656.5563, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false, + "udKey": "crypto.BUSD.version.BEP20.address" + }, + { + "currency": "BUSDETH", + "symbol": "BUSD", + "chain": "ETHEREUM", + "display": "BUSD", + "caption": "ERC20", + "explorerTx": "https://etherscan.io/tx/", + "explorerAddr": "https://etherscan.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6414, + "depositMax": 10656.5563, + "withdrawalMin": 26.6414, + "withdrawalMax": 10656.5563, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false, + "udKey": "crypto.BUSD.version.ERC20.address" + }, + { + "currency": "CAD", + "symbol": "CAD", + "display": "CAD", + "type": "FIAT", + "enabled": true, + "depositMin": 40.85, + "depositMax": 14024.33, + "withdrawalMin": 40.85, + "withdrawalMax": 14024.33, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "CZK", + "symbol": "CZK", + "display": "CZK", + "type": "FIAT", + "enabled": true, + "depositMin": 672.71, + "depositMax": 231068.12, + "withdrawalMin": 672.71, + "withdrawalMax": 231068.12, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "DAIBSC", + "symbol": "DAI", + "chain": "BINANCE_SMART_CHAIN", + "display": "DAI", + "caption": "BEP20", + "explorerTx": "https://www.bscscan.com/tx/", + "explorerAddr": "https://www.bscscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6462, + "depositMax": 10658.4634, + "withdrawalMin": 26.6462, + "withdrawalMax": 10658.4634, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "DAIEAVA", + "symbol": "DAIE", + "chain": "AVALANCHE", + "display": "DAI.e", + "caption": "on Avalanche", + "explorerTx": "https://snowtrace.io/tx/", + "explorerAddr": "https://snowtrace.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6052, + "depositMax": 10642.0513, + "withdrawalMin": 26.6052, + "withdrawalMax": 10642.0513, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "DAIETH", + "symbol": "DAI", + "chain": "ETHEREUM", + "display": "DAI", + "caption": "ERC20", + "explorerTx": "https://etherscan.io/tx/", + "explorerAddr": "https://etherscan.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6436, + "depositMax": 10657.4090, + "withdrawalMin": 26.6436, + "withdrawalMax": 10657.4090, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false, + "udKey": "crypto.DAI.address" + }, + { + "currency": "DAIPOL", + "symbol": "DAI", + "chain": "POLYGON", + "display": "DAI", + "caption": "on Polygon", + "explorerTx": "https://polygonscan.com/tx/", + "explorerAddr": "https://polygonscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6436, + "depositMax": 10657.4090, + "withdrawalMin": 26.6436, + "withdrawalMax": 10657.4090, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "DKK", + "symbol": "DKK", + "display": "DKK", + "type": "FIAT", + "enabled": true, + "depositMin": 210.64, + "depositMax": 72348.14, + "withdrawalMin": 210.64, + "withdrawalMax": 72348.14, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "ETH", + "symbol": "ETH", + "chain": "ETHEREUM", + "display": "ETH", + "caption": "", + "explorerTx": "https://ropsten.etherscan.io/tx/", + "explorerAddr": "https://ropsten.etherscan.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 37688.767964, + "depositMax": 15075507.185288, + "withdrawalMin": 37688.767964, + "withdrawalMax": 15075507.185288, + "addressValidator": "ETH", + "precision": 6, + "allowTag": false, + "udKey": "crypto.ETH.address" + }, + { + "currency": "EUR", + "symbol": "EUR", + "display": "EUR", + "caption": "", + "type": "FIAT", + "enabled": true, + "depositMin": 27.24, + "depositMax": 9349.53, + "withdrawalMin": 27.24, + "withdrawalMax": 9349.53, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "GBP", + "symbol": "GBP", + "display": "GBP", + "type": "FIAT", + "enabled": true, + "depositMin": 25.14, + "depositMax": 8630.60, + "withdrawalMin": 25.14, + "withdrawalMax": 8630.60, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "GMTBSC", + "symbol": "GMT", + "chain": "BINANCE_SMART_CHAIN", + "display": "GMT", + "caption": "BEP20", + "explorerTx": "https://bscscan.com/tx/", + "explorerAddr": "https://bscscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 53.9899, + "depositMax": 21595.9496, + "withdrawalMin": 53.9899, + "withdrawalMax": 21595.9496, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "GNOETH", + "symbol": "GNO", + "chain": "ETHEREUM", + "display": "GNO", + "caption": "ERC20", + "explorerTx": "https://etherscan.io/tx/", + "explorerAddr": "https://etherscan.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 0.2250, + "depositMax": 89.9938, + "withdrawalMin": 0.2250, + "withdrawalMax": 89.9938, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false, + "udKey": "crypto.GNO.address" + }, + { + "currency": "HAIBSC", + "symbol": "HAI", + "chain": "BINANCE_SMART_CHAIN", + "display": "HAI", + "caption": "on BSC", + "explorerTx": "https://bscscan.com/tx/", + "explorerAddr": "https://bscscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 937.548948, + "depositMax": 375019.579098, + "withdrawalMin": 937.548948, + "withdrawalMax": 375019.579098, + "addressValidator": "ETH", + "precision": 6, + "allowTag": false + }, + { + "currency": "HAIVET", + "symbol": "HAI", + "chain": "VECHAIN", + "display": "HAI", + "caption": "VET", + "explorerTx": "https://vechainstats.com/transaction/", + "explorerAddr": "https://vechainstats.com/account/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 949.3974, + "depositMax": 379758.9569, + "withdrawalMin": 949.3974, + "withdrawalMax": 379758.9569, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "IDR", + "symbol": "IDR", + "display": "IDR", + "type": "FIAT", + "enabled": true, + "depositMin": 459558, + "depositMax": 157855988, + "withdrawalMin": 459558, + "withdrawalMax": 157855988, + "addressValidator": "LUHN", + "precision": 0, + "allowTag": false + }, + { + "currency": "INR", + "symbol": "INR", + "display": "INR", + "type": "FIAT", + "enabled": true, + "depositMin": 2511.35, + "depositMax": 862635.95, + "withdrawalMin": 2511.35, + "withdrawalMax": 862635.95, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "JPY", + "symbol": "JPY", + "display": "JPY", + "type": "FIAT", + "enabled": true, + "depositMin": 3992, + "depositMax": 1370884, + "withdrawalMin": 3992, + "withdrawalMax": 1370884, + "addressValidator": "LUHN", + "precision": 0, + "allowTag": false + }, + { + "currency": "KZT", + "symbol": "KZT", + "display": "KZT", + "type": "FIAT", + "enabled": true, + "depositMin": 13843.18, + "depositMax": 4755080.66, + "withdrawalMin": 13843.18, + "withdrawalMax": 4755080.66, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "MH3ETH", + "symbol": "MH3", + "chain": "ETHEREUM", + "display": "NFT", + "caption": "Meta History", + "explorerTx": "https://rinkeby.etherscan.io/tx/", + "explorerAddr": "https://rinkeby.etherscan.io/address/", + "type": "CRYPTO", + "enabled": false, + "depositMin": 0, + "depositMax": 0, + "withdrawalMin": 0, + "withdrawalMax": 0, + "addressValidator": "ETH", + "precision": 0, + "allowTag": false, + "nftContract": "0xf0192b97135abaa218bcff255b8f4b22171a7590" + }, + { + "currency": "MXN", + "symbol": "MXN", + "display": "MXN", + "type": "FIAT", + "enabled": true, + "depositMin": 574.83, + "depositMax": 197445.63, + "withdrawalMin": 574.83, + "withdrawalMax": 197445.63, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "MYR", + "symbol": "MYR", + "display": "MYR", + "type": "FIAT", + "enabled": true, + "depositMin": 131.04, + "depositMax": 45006.86, + "withdrawalMin": 131.04, + "withdrawalMax": 45006.86, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "NEAR", + "symbol": "NEAR", + "chain": "NEAR", + "display": "NEAR", + "caption": "", + "explorerTx": "", + "explorerAddr": "", + "type": "CRYPTO", + "enabled": true, + "depositMin": 10.560163, + "depositMax": 4224.064906, + "withdrawalMin": 10.560163, + "withdrawalMax": 4224.064906, + "addressValidator": "NEAR", + "precision": 6, + "allowTag": false, + "udKey": "crypto.NEAR.address" + }, + { + "currency": "NOK", + "symbol": "NOK", + "display": "NOK", + "type": "FIAT", + "enabled": true, + "depositMin": 312.23, + "depositMax": 107245.60, + "withdrawalMin": 312.23, + "withdrawalMax": 107245.60, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "NXUSDAVA", + "symbol": "NXUSD", + "chain": "AVALANCHE", + "display": "NXUSD", + "caption": "Avalanche", + "explorerTx": "https://snowtrace.io/tx/", + "explorerAddr": "https://snowtrace.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.8766, + "depositMax": 10750.6217, + "withdrawalMin": 26.8766, + "withdrawalMax": 10750.6217, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "NZD", + "symbol": "NZD", + "display": "NZD", + "type": "FIAT", + "enabled": true, + "depositMin": 47.90, + "depositMax": 16448.49, + "withdrawalMin": 47.90, + "withdrawalMax": 16448.49, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "PLN", + "symbol": "PLN", + "display": "PLN", + "type": "FIAT", + "enabled": true, + "depositMin": 134.06, + "depositMax": 46047.85, + "withdrawalMin": 134.06, + "withdrawalMax": 46047.85, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "RIF", + "symbol": "RIF", + "chain": "RSK", + "display": "RIF", + "caption": "RSK", + "explorerTx": "https://explorer.rsk.co/tx/", + "explorerAddr": "https://explorer.rsk.co/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 429.65, + "depositMax": 171859.31, + "withdrawalMin": 429.65, + "withdrawalMax": 171859.31, + "addressValidator": "ETH", + "precision": 2, + "allowTag": false, + "udKey": "crypto.RIF.address" + }, + { + "currency": "RUB", + "symbol": "RUB", + "display": "RUB", + "type": "FIAT", + "enabled": true, + "depositMin": 2217.40, + "depositMax": 761666.68, + "withdrawalMin": 2217.40, + "withdrawalMax": 761666.68, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "SOL", + "symbol": "SOL", + "chain": "SOLANA", + "display": "SOL", + "caption": "Solana", + "explorerTx": "https://explorer.solana.com/tx/", + "explorerAddr": "https://explorer.solana.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 1.155094, + "depositMax": 462.037519, + "withdrawalMin": 1.155094, + "withdrawalMax": 462.037519, + "addressValidator": "SOLANA", + "precision": 6, + "allowTag": false, + "udKey": "crypto.SOL.address" + }, + { + "currency": "TRY", + "symbol": "TRY", + "display": "TRY", + "type": "FIAT", + "enabled": true, + "depositMin": 572.28, + "depositMax": 196571.08, + "withdrawalMin": 572.28, + "withdrawalMax": 196571.08, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "TWTBNB", + "symbol": "TWT", + "chain": "BNB", + "display": "TWT", + "caption": "BEP2", + "explorerTx": "https://explorer.binance.org/tx/", + "explorerAddr": "https://explorer.binance.org/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 17.4457, + "depositMax": 6978.2637, + "withdrawalMin": 17.4457, + "withdrawalMax": 6978.2637, + "addressValidator": "BNB", + "precision": 4, + "allowTag": true, + "udKey": "crypto.TWT.address" + }, + { + "currency": "UAH", + "symbol": "UAH", + "display": "UAH", + "caption": "", + "type": "FIAT", + "enabled": true, + "depositMin": 1118.00, + "depositMax": 14005.59, + "withdrawalMin": 1118.00, + "withdrawalMax": 14005.59, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "USD", + "symbol": "USD", + "display": "USD", + "type": "FIAT", + "enabled": true, + "depositMin": 30.57, + "depositMax": 10382.96, + "withdrawalMin": 30.57, + "withdrawalMax": 10382.96, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + }, + { + "currency": "USDCARB", + "symbol": "USDC", + "chain": "ARBITRUM", + "display": "USDC", + "caption": "on arbitrum", + "explorerTx": "https://arbiscan.io/tx/", + "explorerAddr": "https://arbiscan.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6382, + "depositMax": 10655.2775, + "withdrawalMin": 26.6382, + "withdrawalMax": 10655.2775, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "USDCBSC", + "symbol": "USDC", + "chain": "BINANCE_SMART_CHAIN", + "display": "USDC", + "caption": "BEP20", + "explorerTx": "https://www.bscscan.com/tx/", + "explorerAddr": "https://www.bscscan.com/address/", + "type": "CRYPTO", + "enabled": false, + "depositMin": 0, + "depositMax": 0, + "withdrawalMin": 0, + "withdrawalMax": 0, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "USDCEAVA", + "symbol": "USDCE", + "chain": "AVALANCHE", + "display": "USDC.e", + "caption": "on Avalanche", + "explorerTx": "", + "explorerAddr": "", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6078, + "depositMax": 10643.1155, + "withdrawalMin": 26.6078, + "withdrawalMax": 10643.1155, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "USDCETH", + "symbol": "USDC", + "chain": "ETHEREUM", + "display": "USDC", + "caption": "ERC20", + "explorerTx": "https://etherscan.io/tx/", + "explorerAddr": "https://etherscan.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6382, + "depositMax": 10655.2775, + "withdrawalMin": 26.6382, + "withdrawalMax": 10655.2775, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false, + "udKey": "crypto.USDC.address" + }, + { + "currency": "USDCZK", + "symbol": "USDC", + "chain": "ZKSYNC", + "display": "USDC", + "caption": "on zkSync", + "explorerTx": "https://zkscan.io/explorer/transactions/", + "explorerAddr": "https://zkscan.io/explorer/accounts/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6078, + "depositMax": 10643.1155, + "withdrawalMin": 26.6078, + "withdrawalMax": 10643.1155, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "USDTAVA", + "symbol": "USDT", + "chain": "AVALANCHE", + "display": "USDT", + "caption": "on Avalanche", + "explorerTx": "https://snowtrace.io/tx/", + "explorerAddr": "https://snowtrace.io/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6649, + "depositMax": 10665.9328, + "withdrawalMin": 26.6649, + "withdrawalMax": 10665.9328, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "USDTE", + "symbol": "USDT", + "chain": "ETHEREUM", + "display": "USDT", + "caption": "", + "explorerTx": "", + "explorerAddr": "", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.66483213, + "depositMax": 10665.93285048, + "withdrawalMin": 26.66483213, + "withdrawalMax": 10665.93285048, + "addressValidator": "ETH", + "precision": 8, + "allowTag": false, + "udKey": "crypto.USDT.version.ERC20.address" + }, + { + "currency": "USDTPOL", + "symbol": "USDT", + "chain": "POLYGON", + "display": "USDT", + "caption": "on Polygon", + "explorerTx": "https://polygonscan.com/tx/", + "explorerAddr": "https://polygonscan.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6649, + "depositMax": 10665.9328, + "withdrawalMin": 26.6649, + "withdrawalMax": 10665.9328, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "USDVEL", + "symbol": "USDV", + "chain": "VELAS_EVM", + "display": "USDV", + "caption": "Velas EVM", + "explorerTx": "https://evmexplorer.velas.com/tx/", + "explorerAddr": "https://evmexplorer.velas.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26611608.908400, + "depositMax": 10644643563.359833, + "withdrawalMin": 26611608.908400, + "withdrawalMax": 10644643563.359833, + "addressValidator": "ETH", + "precision": 6, + "allowTag": false + }, + { + "currency": "USNNEAR", + "symbol": "USN", + "chain": "NEAR", + "display": "USN", + "caption": "Near", + "explorerTx": "https://explorer.near.org/transactions/", + "explorerAddr": "https://explorer.near.org/accounts/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6969, + "depositMax": 10678.7473, + "withdrawalMin": 26.6969, + "withdrawalMax": 10678.7473, + "addressValidator": "NEAR", + "precision": 4, + "allowTag": false + }, + { + "currency": "VLX", + "symbol": "VLX", + "chain": "VELAS", + "display": "VLX", + "caption": "Velas", + "explorerTx": "https://explorer.velas.com/tx/", + "explorerAddr": "https://explorer.velas.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 1021.951187, + "depositMax": 408780.474783, + "withdrawalMin": 1021.951187, + "withdrawalMax": 408780.474783, + "addressValidator": "SOLANA", + "precision": 6, + "allowTag": false, + "udKey": "crypto.VLX.address" + }, + { + "currency": "VLXETH", + "symbol": "VLX", + "chain": "VELAS_EVM", + "display": "VLX", + "caption": "Velas EVM", + "explorerTx": "https://evmexplorer.velas.com/tx/", + "explorerAddr": "https://evmexplorer.velas.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 1021.9512, + "depositMax": 408780.4747, + "withdrawalMin": 1021.9512, + "withdrawalMax": 408780.4747, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "WLKNSOL", + "symbol": "WLKN", + "chain": "SOLANA", + "display": "WLKN", + "caption": "Solana", + "explorerTx": "https://explorer.solana.com/tx/", + "explorerAddr": "https://explorer.solana.com/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 531.89, + "depositMax": 212755.45, + "withdrawalMin": 531.89, + "withdrawalMax": 212755.45, + "addressValidator": "SOLANA", + "precision": 2, + "allowTag": false + }, + { + "currency": "XDAIGNO", + "symbol": "xDAI", + "chain": "GNOSIS", + "display": "xDAI", + "caption": "Gnosis chain", + "explorerTx": "https://blockscout.com/xdai/mainnet/tx/", + "explorerAddr": "https://blockscout.com/xdai/mainnet/address/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 26.6782, + "depositMax": 10671.2684, + "withdrawalMin": 26.6782, + "withdrawalMax": 10671.2684, + "addressValidator": "ETH", + "precision": 4, + "allowTag": false + }, + { + "currency": "XRP", + "symbol": "XRP", + "chain": "RIPPLE", + "display": "XRP", + "caption": "Ripple", + "explorerTx": "https://xrpscan.com/tx/", + "explorerAddr": "https://xrpscan.com/account/", + "type": "CRYPTO", + "enabled": true, + "depositMin": 67.7645, + "depositMax": 27105.7684, + "withdrawalMin": 67.7645, + "withdrawalMax": 27105.7684, + "addressValidator": "XRP", + "precision": 4, + "allowTag": true, + "udKey": "crypto.XRP.address" + }, + { + "currency": "ZAR", + "symbol": "ZAR", + "display": "ZAR", + "type": "FIAT", + "enabled": true, + "depositMin": 538.27, + "depositMax": 184892.03, + "withdrawalMin": 538.27, + "withdrawalMax": 184892.03, + "addressValidator": "LUHN", + "precision": 2, + "allowTag": false + } + ] +""".trimIndent() \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/common/LogConfig.kt b/domain/src/main/java/com/tangem/domain/common/LogConfig.kt index c4f96f4b55..a0046af552 100644 --- a/domain/src/main/java/com/tangem/domain/common/LogConfig.kt +++ b/domain/src/main/java/com/tangem/domain/common/LogConfig.kt @@ -13,6 +13,7 @@ object LogConfig { object NetworkLogConfig { val mercuryoService: Boolean = false val moonPayService: Boolean = false + val utorgService: Boolean = false val tangemTechService: Boolean = BuildConfig.DEBUG val paymentologyApiService: Boolean = BuildConfig.DEBUG val blockchainSdkNetwork: Boolean = BuildConfig.DEBUG