diff --git a/app/src/main/java/org/p2p/wallet/bridge/send/ui/BridgeSendPresenter.kt b/app/src/main/java/org/p2p/wallet/bridge/send/ui/BridgeSendPresenter.kt index 8ff87bb73b..4245a145e4 100644 --- a/app/src/main/java/org/p2p/wallet/bridge/send/ui/BridgeSendPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/bridge/send/ui/BridgeSendPresenter.kt @@ -18,7 +18,7 @@ import org.p2p.core.utils.formatTokenWithSymbol import org.p2p.core.utils.isConnectionError import org.p2p.core.utils.isZero import org.p2p.core.utils.orZero -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleTwo import org.p2p.core.utils.toBigDecimalOrZero import org.p2p.uikit.utils.text.TextViewCellModel import org.p2p.wallet.R @@ -242,7 +242,7 @@ class BridgeSendPresenter( if (calculationMode.getCurrencyMode() is CurrencyMode.Fiat.Usd) { switchCurrencyMode() } - val newTextValue = inputAmount.scaleShort().toPlainString() + val newTextValue = inputAmount.scaleTwo().toPlainString() updateInputValue(newTextValue, forced = true) calculationMode.updateInputAmount(newTextValue) disableInputs() diff --git a/app/src/main/java/org/p2p/wallet/history/model/rpc/RpcHistoryTransaction.kt b/app/src/main/java/org/p2p/wallet/history/model/rpc/RpcHistoryTransaction.kt index 06205dc6b3..093df376e6 100644 --- a/app/src/main/java/org/p2p/wallet/history/model/rpc/RpcHistoryTransaction.kt +++ b/app/src/main/java/org/p2p/wallet/history/model/rpc/RpcHistoryTransaction.kt @@ -12,7 +12,7 @@ import org.p2p.core.utils.asNegativeUsdTransaction import org.p2p.core.utils.asPositiveUsdTransaction import org.p2p.core.utils.asUsdTransaction import org.p2p.core.utils.formatTokenWithSymbol -import org.p2p.core.utils.scaleShortOrFirstNotZero +import org.p2p.core.utils.scaleTwoOrFirstNotZero import org.p2p.wallet.R import org.p2p.wallet.history.model.HistoryTransaction import org.p2p.wallet.transaction.model.HistoryTransactionStatus @@ -210,7 +210,7 @@ sealed class RpcHistoryTransaction( fun getFormattedFiatValue(): String? { return amount.totalInUsd - ?.scaleShortOrFirstNotZero() + ?.scaleTwoOrFirstNotZero() ?.asUsdTransaction(getSymbol(isSend)) } @@ -263,7 +263,7 @@ sealed class RpcHistoryTransaction( fun getFormattedFiatValue(): String? { return amount.totalInUsd - ?.scaleShortOrFirstNotZero() + ?.scaleTwoOrFirstNotZero() ?.asPositiveUsdTransaction() } @@ -305,7 +305,7 @@ sealed class RpcHistoryTransaction( fun getValue(): String? { return amount.totalInUsd - ?.scaleShortOrFirstNotZero() + ?.scaleTwoOrFirstNotZero() ?.asUsdTransaction(getSymbol(isStake)) } diff --git a/app/src/main/java/org/p2p/wallet/history/ui/model/HistoryItem.kt b/app/src/main/java/org/p2p/wallet/history/ui/model/HistoryItem.kt index 1b17b296f3..823a268d1d 100644 --- a/app/src/main/java/org/p2p/wallet/history/ui/model/HistoryItem.kt +++ b/app/src/main/java/org/p2p/wallet/history/ui/model/HistoryItem.kt @@ -6,7 +6,7 @@ import org.threeten.bp.ZonedDateTime import org.p2p.core.utils.asNegativeUsdTransaction import org.p2p.core.utils.asPositiveUsdTransaction import org.p2p.core.utils.formatToken -import org.p2p.core.utils.scaleMedium +import org.p2p.core.utils.scaleSix import org.p2p.core.utils.toBigDecimalOrZero import org.p2p.uikit.utils.recycler.RoundedItem import org.p2p.wallet.bridge.model.BridgeBundle @@ -92,7 +92,7 @@ sealed interface HistoryItem { val tokenSymbol = sendDetails.amount.symbol return if (scaleMedium) { - "${totalAmount.scaleMedium().formatToken()} $tokenSymbol" + "${totalAmount.scaleSix().formatToken()} $tokenSymbol" } else { "${totalAmount.formatToken()} $tokenSymbol" } @@ -117,7 +117,7 @@ sealed interface HistoryItem { val tokenSymbol = bundle.resultAmount.symbol return if (scaleMedium) { - "${totalAmount.scaleMedium().formatToken()} $tokenSymbol" + "${totalAmount.scaleSix().formatToken()} $tokenSymbol" } else { "${totalAmount.formatToken()} $tokenSymbol" } diff --git a/app/src/main/java/org/p2p/wallet/home/deeplinks/MainFragmentDeeplinkHandler.kt b/app/src/main/java/org/p2p/wallet/home/deeplinks/MainFragmentDeeplinkHandler.kt index 4d45fa6008..81bf185869 100644 --- a/app/src/main/java/org/p2p/wallet/home/deeplinks/MainFragmentDeeplinkHandler.kt +++ b/app/src/main/java/org/p2p/wallet/home/deeplinks/MainFragmentDeeplinkHandler.kt @@ -102,13 +102,13 @@ class MainFragmentDeeplinkHandler( * Handles buy token for fiat deeplink */ private fun handleBuyDeeplink(data: DeeplinkData) { - val cryptoToken = data.args["to"] + val cryptoTokenMint = data.args["to"] val fiatToken = data.args["from"] val fiatAmount = data.args["amount"] - if (!cryptoToken.isNullOrBlank() && !fiatToken.isNullOrBlank()) { + if (!cryptoTokenMint.isNullOrBlank() && !fiatToken.isNullOrBlank()) { coroutineScope.launch { - val token = userInteractor.getSingleTokenForBuy(listOf(cryptoToken)) + val token = userInteractor.getSingleTokenForBuy(listOf(cryptoTokenMint)) if (token != null) { screenNavigator?.navigateToBuyScreen(token, fiatToken, fiatAmount) } else { diff --git a/app/src/main/java/org/p2p/wallet/home/ui/container/MainContainerPresenter.kt b/app/src/main/java/org/p2p/wallet/home/ui/container/MainContainerPresenter.kt index a36bcefd8f..5ca20e7f85 100644 --- a/app/src/main/java/org/p2p/wallet/home/ui/container/MainContainerPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/home/ui/container/MainContainerPresenter.kt @@ -24,6 +24,7 @@ import org.p2p.wallet.jupiter.ui.main.JupiterSwapFragment import org.p2p.wallet.settings.ui.settings.SettingsFragment import org.p2p.wallet.tokenservice.TokenServiceCoordinator import org.p2p.wallet.tokenservice.UserTokensState +import org.p2p.wallet.updates.SocketUpdatesManager class MainContainerPresenter( private val deeplinksManager: AppDeeplinksManager, @@ -31,6 +32,7 @@ class MainContainerPresenter( private val tokenServiceCoordinator: TokenServiceCoordinator, private val metadataInteractor: MetadataInteractor, private val walletStrigaInteractor: WalletStrigaInteractor, + private val socketUpdatesManager: SocketUpdatesManager, private val balanceMapper: WalletBalanceMapper, private val mainScreenAnalytics: MainScreenAnalytics, ) : BasePresenter(), MainContainerContract.Presenter { @@ -102,7 +104,14 @@ class MainContainerPresenter( private fun observeInternetState() { connectionManager.connectionStatus .onEach { isConnected -> - if (!isConnected) view?.showUiKitSnackBar(messageResId = R.string.error_no_internet_message) + if (!isConnected) { + view?.showUiKitSnackBar(messageResId = R.string.error_no_internet_message) + socketUpdatesManager.stop() + } else { + if (!socketUpdatesManager.isStarted()) { + socketUpdatesManager.restart() + } + } } .launchIn(this) } diff --git a/app/src/main/java/org/p2p/wallet/home/ui/crypto/MyCryptoPresenter.kt b/app/src/main/java/org/p2p/wallet/home/ui/crypto/MyCryptoPresenter.kt index f561f66621..78f33288f6 100644 --- a/app/src/main/java/org/p2p/wallet/home/ui/crypto/MyCryptoPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/home/ui/crypto/MyCryptoPresenter.kt @@ -13,7 +13,7 @@ import org.p2p.core.token.Token import org.p2p.core.token.TokenVisibility import org.p2p.core.token.filterTokensForWalletScreen import org.p2p.core.utils.isMoreThan -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleTwo import org.p2p.uikit.model.AnyCellItem import org.p2p.wallet.R import org.p2p.wallet.auth.interactor.UsernameInteractor @@ -199,7 +199,7 @@ class MyCryptoPresenter( .mapNotNull(Token.Active::totalInUsd) .filter { it.isMoreThan(MINIMAL_DUST_FOR_BALANCE) } .fold(BigDecimal.ZERO, BigDecimal::add) - .scaleShort() + .scaleTwo() } private fun handleEmptyAccount() { diff --git a/app/src/main/java/org/p2p/wallet/home/ui/select/bottomsheet/NewSelectTokenBottomSheet.kt b/app/src/main/java/org/p2p/wallet/home/ui/select/bottomsheet/BuySelectTokenBottomSheet.kt similarity index 76% rename from app/src/main/java/org/p2p/wallet/home/ui/select/bottomsheet/NewSelectTokenBottomSheet.kt rename to app/src/main/java/org/p2p/wallet/home/ui/select/bottomsheet/BuySelectTokenBottomSheet.kt index 90e576dbe4..38284050f6 100644 --- a/app/src/main/java/org/p2p/wallet/home/ui/select/bottomsheet/NewSelectTokenBottomSheet.kt +++ b/app/src/main/java/org/p2p/wallet/home/ui/select/bottomsheet/BuySelectTokenBottomSheet.kt @@ -1,56 +1,59 @@ package org.p2p.wallet.home.ui.select.bottomsheet -import android.os.Bundle -import android.view.View import androidx.core.os.bundleOf import androidx.fragment.app.FragmentManager import androidx.fragment.app.setFragmentResult import androidx.recyclerview.widget.LinearLayoutManager +import android.os.Bundle +import android.view.View import org.koin.android.ext.android.inject +import org.p2p.core.token.Token import org.p2p.uikit.utils.attachAdapter import org.p2p.wallet.common.analytics.interactor.ScreensAnalyticsInteractor import org.p2p.wallet.common.ui.bottomsheet.BaseRecyclerDoneBottomSheet import org.p2p.wallet.common.ui.recycler.adapter.DividerItemDecorator -import org.p2p.core.token.Token import org.p2p.wallet.home.ui.select.NewSelectTokenAdapter import org.p2p.wallet.moonpay.analytics.BuyAnalytics import org.p2p.wallet.utils.args import org.p2p.wallet.utils.unsafeLazy import org.p2p.wallet.utils.withArgs -private const val ARG_ALL_TOKENS = "ARG_ALL_TOKENS" +private const val ARG_TOKENS_TO_BUY = "ARG_ALL_TOKENS" private const val ARG_SELECTED_TOKEN = "ARG_SELECTED_TOKEN" -class NewSelectTokenBottomSheet : BaseRecyclerDoneBottomSheet() { +class BuySelectTokenBottomSheet : BaseRecyclerDoneBottomSheet() { companion object { fun show( fm: FragmentManager, title: String, - tokens: List, + tokensToBuy: List, preselectedToken: Token? = null, requestKey: String, resultKey: String - ) = NewSelectTokenBottomSheet().withArgs( + ) = BuySelectTokenBottomSheet().withArgs( ARG_TITLE to title, - ARG_ALL_TOKENS to tokens, + ARG_TOKENS_TO_BUY to tokensToBuy, ARG_SELECTED_TOKEN to preselectedToken, ARG_REQUEST_KEY to requestKey, ARG_RESULT_KEY to resultKey - ).show(fm, NewSelectTokenBottomSheet::javaClass.name) + ).show(fm, BuySelectTokenBottomSheet::javaClass.name) } - private val tokens: List by args(ARG_ALL_TOKENS) + private val tokensToBuy: List by args(ARG_TOKENS_TO_BUY) private val preselectedToken: Token? by args(ARG_SELECTED_TOKEN) private val buyAnalytics: BuyAnalytics by inject() private val analyticsInteractor: ScreensAnalyticsInteractor by inject() private val tokenAdapter: NewSelectTokenAdapter by unsafeLazy { - NewSelectTokenAdapter(preselectedItem = preselectedToken, onItemClicked = { - setFragmentResult(requestKey, bundleOf(resultKey to it)) - dismissAllowingStateLoss() - }) + NewSelectTokenAdapter( + preselectedItem = preselectedToken, + onItemClicked = { + setFragmentResult(requestKey, bundleOf(resultKey to it)) + dismissAllowingStateLoss() + } + ) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -61,7 +64,7 @@ class NewSelectTokenBottomSheet : BaseRecyclerDoneBottomSheet() { DividerItemDecorator(requireContext()) ) recyclerView.attachAdapter(tokenAdapter) - tokenAdapter.setItems(tokens) + tokenAdapter.setItems(tokensToBuy) } } diff --git a/app/src/main/java/org/p2p/wallet/jupiter/interactor/JupiterSwapTokensResult.kt b/app/src/main/java/org/p2p/wallet/jupiter/interactor/JupiterSwapTokensResult.kt index 962863e733..65f1c4630e 100644 --- a/app/src/main/java/org/p2p/wallet/jupiter/interactor/JupiterSwapTokensResult.kt +++ b/app/src/main/java/org/p2p/wallet/jupiter/interactor/JupiterSwapTokensResult.kt @@ -12,6 +12,7 @@ sealed interface JupiterSwapTokensResult { * https://github.com/orca-so/whirlpools/blob/e06505fb1e41508295eca116ca676c8f498398d2/programs/whirlpool/src/manager/whirlpool_manager.rs#L13 * happens randomly error occurs when sending transaction, * but it's not our fault, it depends on the node state + * https://discord.com/channels/767761912167006292/1106227799448101004 */ class InvalidTimestampRpcError(override val cause: ServerException) : Throwable(cause.message) } diff --git a/app/src/main/java/org/p2p/wallet/jupiter/repository/model/JupiterSwapMarketInformation.kt b/app/src/main/java/org/p2p/wallet/jupiter/repository/model/JupiterSwapMarketInformation.kt index 012b35c997..9366a30828 100644 --- a/app/src/main/java/org/p2p/wallet/jupiter/repository/model/JupiterSwapMarketInformation.kt +++ b/app/src/main/java/org/p2p/wallet/jupiter/repository/model/JupiterSwapMarketInformation.kt @@ -3,7 +3,7 @@ package org.p2p.wallet.jupiter.repository.model import java.math.BigDecimal import java.math.BigInteger import org.p2p.core.crypto.Base58String -import org.p2p.core.utils.scaleShortOrFirstNotZero +import org.p2p.core.utils.scaleTwoOrFirstNotZero @Deprecated("Old v4 swap logic") data class JupiterSwapMarketInformation( @@ -26,7 +26,7 @@ data class JupiterSwapMarketInformation( val mint: Base58String, val percent: BigDecimal ) { - val formattedPercent: BigDecimal = percent.times(BigDecimal(100)).scaleShortOrFirstNotZero() + val formattedPercent: BigDecimal = percent.times(BigDecimal(100)).scaleTwoOrFirstNotZero() } @Deprecated("Old v4 swap logic") @@ -35,6 +35,6 @@ data class JupiterSwapMarketInformation( val mint: Base58String, val percent: BigDecimal ) { - val formattedPercent: BigDecimal = percent.times(BigDecimal(100)).scaleShortOrFirstNotZero() + val formattedPercent: BigDecimal = percent.times(BigDecimal(100)).scaleTwoOrFirstNotZero() } } diff --git a/app/src/main/java/org/p2p/wallet/moonpay/clientsideapi/response/MoonpayCurrency.kt b/app/src/main/java/org/p2p/wallet/moonpay/clientsideapi/response/MoonpayCurrency.kt index 9c23e7f023..c88e3a8e65 100644 --- a/app/src/main/java/org/p2p/wallet/moonpay/clientsideapi/response/MoonpayCurrency.kt +++ b/app/src/main/java/org/p2p/wallet/moonpay/clientsideapi/response/MoonpayCurrency.kt @@ -6,7 +6,9 @@ sealed class MoonpayCurrency { abstract val currencyId: String abstract val amounts: MoonpayCurrencyAmounts - fun isSol() = this is CryptoToken && tokenSymbol.uppercase() == Constants.SOL_SYMBOL + // it's okay to keep equals by symbol here + // because we only have non-scam wrapped SOL in/out Moonpay + fun isSol() = this is CryptoToken && tokenSymbol.equals(Constants.SOL_SYMBOL, ignoreCase = true) data class CryptoToken( val tokenSymbol: String, diff --git a/app/src/main/java/org/p2p/wallet/moonpay/interactor/BuyInteractor.kt b/app/src/main/java/org/p2p/wallet/moonpay/interactor/BuyInteractor.kt index f5298002a3..d1220a1bee 100644 --- a/app/src/main/java/org/p2p/wallet/moonpay/interactor/BuyInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/moonpay/interactor/BuyInteractor.kt @@ -39,10 +39,13 @@ class BuyInteractor( fun getQuotes(): List = quotes.toList() - suspend fun loadQuotes(currencies: List, tokens: List): Unit = withContext(dispatchers.io) { - Timber.i("Loading quotes for buy: currencies=$currencies; tokens=${tokens.map(Token::mintAddress)}") + suspend fun loadQuotes( + currencies: List, + tokensToBuy: List + ): Unit = withContext(dispatchers.io) { + Timber.i("Loading quotes for buy: currencies=$currencies; tokens=${tokensToBuy.map(Token::mintAddress)}") currencies.flatMap { currency -> - tokens.map { token -> + tokensToBuy.map { token -> async { loadQuote(currency, token) } } } @@ -102,31 +105,30 @@ class BuyInteractor( tokenSymbol: String ): BigDecimal { val quote = quotes.find { - it.currency == FiatCurrency.getFromAbbreviation(currencyCode) && it.token.tokenSymbol == tokenSymbol + it.currency == FiatCurrency.getFromAbbreviation(currencyCode) && it.tokenToBuy.tokenSymbol == tokenSymbol } return HARDCODED_MIN_BUY_CURRENCY_AMOUNT.toBigDecimal() } - private suspend fun loadQuote(currency: FiatCurrency, token: Token) { - Timber.d("Load quote for currency=$currency; token=${token.tokenSymbol}") + private suspend fun loadQuote(currency: FiatCurrency, tokenToBuy: Token) { + Timber.d("Load quote for currency=$currency; token=${tokenToBuy.tokenSymbol}") try { val response = moonpayRepository.getBuyCurrencyData( baseCurrencyAmount = CURRENCY_AMOUNT_FOR_PRICE_REQUEST, quoteCurrencyAmount = null, - tokenToBuy = token, + tokenToBuy = tokenToBuy, baseCurrencyCode = currency.abbreviation.lowercase(), paymentMethod = DEFAULT_PAYMENT_TYPE ) - val result = MoonpayBuyQuote( + quotes += MoonpayBuyQuote( currency = currency, - token = token, + tokenToBuy = tokenToBuy, price = response.quoteCurrencyPrice, minAmount = HARDCODED_MIN_BUY_CURRENCY_AMOUNT.toBigDecimal() ) - quotes += result } catch (e: Throwable) { - Timber.e(e, "Error while loading quote for currency=$currency; token=${token.tokenSymbol}") + Timber.e(e, "Error while loading quote for currency=$currency; token=${tokenToBuy.tokenSymbol}") } } diff --git a/app/src/main/java/org/p2p/wallet/moonpay/model/MoonpayBuyQuote.kt b/app/src/main/java/org/p2p/wallet/moonpay/model/MoonpayBuyQuote.kt index 8cf350a3a7..901fdb5f59 100644 --- a/app/src/main/java/org/p2p/wallet/moonpay/model/MoonpayBuyQuote.kt +++ b/app/src/main/java/org/p2p/wallet/moonpay/model/MoonpayBuyQuote.kt @@ -6,7 +6,7 @@ import org.p2p.wallet.moonpay.repository.sell.FiatCurrency data class MoonpayBuyQuote( val currency: FiatCurrency, - val token: Token, + val tokenToBuy: Token, val price: BigDecimal, val minAmount: BigDecimal ) diff --git a/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyFragment.kt b/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyFragment.kt index a4fc3ba1c1..9d3c7de481 100644 --- a/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyFragment.kt +++ b/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyFragment.kt @@ -18,7 +18,7 @@ import org.p2p.wallet.R import org.p2p.wallet.common.analytics.interactor.ScreensAnalyticsInteractor import org.p2p.wallet.common.mvp.BaseMvpFragment import org.p2p.wallet.databinding.FragmentBuyBinding -import org.p2p.wallet.home.ui.select.bottomsheet.NewSelectTokenBottomSheet +import org.p2p.wallet.home.ui.select.bottomsheet.BuySelectTokenBottomSheet import org.p2p.wallet.home.ui.select.bottomsheet.SelectCurrencyBottomSheet import org.p2p.wallet.infrastructure.network.provider.TokenKeyProvider import org.p2p.wallet.moonpay.model.BuyDetailsState @@ -171,10 +171,10 @@ class BuyFragment : } override fun showTokensToBuy(selectedToken: Token, tokensToBuy: List) { - NewSelectTokenBottomSheet.show( + BuySelectTokenBottomSheet.show( fm = childFragmentManager, title = getString(R.string.buy_select_token_title), - tokens = tokensToBuy, + tokensToBuy = tokensToBuy, preselectedToken = selectedToken, requestKey = KEY_REQUEST, resultKey = KEY_RESULT_TOKEN diff --git a/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyPresenter.kt b/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyPresenter.kt index 7f0d7f2678..db1cf3d280 100644 --- a/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/moonpay/ui/new/BuyPresenter.kt @@ -13,7 +13,7 @@ import org.p2p.core.utils.asCurrency import org.p2p.core.utils.formatFiat import org.p2p.core.utils.formatToken import org.p2p.core.utils.isZero -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleTwo import org.p2p.core.utils.toBigDecimalOrZero import org.p2p.uikit.components.FocusField import org.p2p.wallet.R @@ -177,7 +177,7 @@ class BuyPresenter( override fun onSelectTokenClicked() { buyInteractor.getQuotesByCurrency(selectedCurrency).forEach { quote -> - tokensToBuy.find { it.tokenSymbol == quote.token.tokenSymbol }?.let { + tokensToBuy.find { it.tokenSymbol == quote.tokenToBuy.tokenSymbol }?.let { it.rate = quote.price it.currency = quote.currency.abbreviation.uppercase() } @@ -405,13 +405,13 @@ class BuyPresenter( val data = BuyViewData( tokenSymbol = selectedToken.tokenSymbol, currencySymbol = selectedCurrency.uiSymbol, - price = buyCurrencyInfo.price.scaleShort(), + price = buyCurrencyInfo.price.scaleTwo(), receiveAmount = buyCurrencyInfo.receiveAmount, - processingFee = buyCurrencyInfo.feeAmount.scaleShort(), - networkFee = buyCurrencyInfo.networkFeeAmount.scaleShort(), - extraFee = buyCurrencyInfo.extraFeeAmount.scaleShort(), + processingFee = buyCurrencyInfo.feeAmount.scaleTwo(), + networkFee = buyCurrencyInfo.networkFeeAmount.scaleTwo(), + extraFee = buyCurrencyInfo.extraFeeAmount.scaleTwo(), accountCreationCost = null, - total = buyCurrencyInfo.totalFiatAmount.scaleShort(), + total = buyCurrencyInfo.totalFiatAmount.scaleTwo(), receiveAmountText = amount, purchaseCostText = currencyForTokensAmount.asCurrency(selectedCurrency.uiSymbol) ) diff --git a/app/src/main/java/org/p2p/wallet/moonpay/ui/transaction/SellTransactionDetailsPresenter.kt b/app/src/main/java/org/p2p/wallet/moonpay/ui/transaction/SellTransactionDetailsPresenter.kt index 7c67466c79..a70eebb9ea 100644 --- a/app/src/main/java/org/p2p/wallet/moonpay/ui/transaction/SellTransactionDetailsPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/moonpay/ui/transaction/SellTransactionDetailsPresenter.kt @@ -1,6 +1,12 @@ package org.p2p.wallet.moonpay.ui.transaction import android.content.res.Resources +import org.threeten.bp.ZonedDateTime +import org.threeten.bp.format.DateTimeFormatter +import timber.log.Timber +import java.util.Locale +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.p2p.core.utils.Constants import org.p2p.core.utils.getHtmlString import org.p2p.core.utils.removeLinksUnderline @@ -8,24 +14,19 @@ import org.p2p.wallet.R import org.p2p.wallet.common.date.isSameDayAs import org.p2p.wallet.common.date.toZonedDateTime import org.p2p.wallet.common.mvp.BasePresenter +import org.p2p.wallet.history.analytics.HistoryAnalytics +import org.p2p.wallet.history.interactor.HistoryInteractor +import org.p2p.wallet.moonpay.model.SellTransaction import org.p2p.wallet.moonpay.repository.sell.MoonpaySellCancelResult import org.p2p.wallet.moonpay.serversideapi.response.SellTransactionStatus +import org.p2p.wallet.sell.interactor.HistoryItemMapper import org.p2p.wallet.sell.interactor.SellInteractor import org.p2p.wallet.sell.ui.lock.SellTransactionViewDetails +import org.p2p.wallet.user.interactor.UserTokensInteractor +import org.p2p.wallet.utils.CUT_ADDRESS_SYMBOLS_COUNT import org.p2p.wallet.utils.cutMiddle import org.p2p.wallet.utils.emptyString import org.p2p.wallet.utils.unsafeLazy -import org.threeten.bp.ZonedDateTime -import org.threeten.bp.format.DateTimeFormatter -import timber.log.Timber -import java.util.Locale -import kotlinx.coroutines.launch -import org.p2p.wallet.history.analytics.HistoryAnalytics -import org.p2p.wallet.history.interactor.HistoryInteractor -import org.p2p.wallet.moonpay.model.SellTransaction -import org.p2p.wallet.sell.interactor.HistoryItemMapper -import org.p2p.wallet.user.interactor.UserTokensInteractor -import org.p2p.wallet.utils.CUT_ADDRESS_SYMBOLS_COUNT private const val DATE_FORMAT = "MMMM dd, yyyy" private const val TIME_FORMAT = "HH:mm" @@ -213,6 +214,9 @@ class SellTransactionDetailsPresenter( when (val result = sellInteractor.cancelTransaction(transaction.transactionId)) { is MoonpaySellCancelResult.CancelSuccess -> { view?.showUiKitSnackBar(messageResId = R.string.sell_details_cancel_success) + // I put a 1 second delay here because Snackbar doesn't have any change to appear + // the view closes too fast + delay(1000) view?.close() } is MoonpaySellCancelResult.CancelFailed -> { diff --git a/app/src/main/java/org/p2p/wallet/receive/tokenselect/ReceiveTokensMapper.kt b/app/src/main/java/org/p2p/wallet/receive/tokenselect/ReceiveTokensMapper.kt index 75dbc95af4..6663a9a696 100644 --- a/app/src/main/java/org/p2p/wallet/receive/tokenselect/ReceiveTokensMapper.kt +++ b/app/src/main/java/org/p2p/wallet/receive/tokenselect/ReceiveTokensMapper.kt @@ -27,7 +27,7 @@ object ReceiveTokensMapper { ethTokenUrl: String ): AnyCellItem { val isErc20Token = ERC20Tokens.findTokenByMint(mintAddress, ERC20Tokens.MATIC) != null - val isSol = symbol == Constants.SOL_SYMBOL + val isSol = mintAddress == Constants.WRAPPED_SOL_MINT val replacedName = if (isSol) Constants.SOL_NAME else name return MainCellModel( leftSideCellModel = createLeftSideModel( diff --git a/app/src/main/java/org/p2p/wallet/restore/ui/derivable/DerivableAccountsAdapter.kt b/app/src/main/java/org/p2p/wallet/restore/ui/derivable/DerivableAccountsAdapter.kt index 7945e6743b..975ae68cad 100644 --- a/app/src/main/java/org/p2p/wallet/restore/ui/derivable/DerivableAccountsAdapter.kt +++ b/app/src/main/java/org/p2p/wallet/restore/ui/derivable/DerivableAccountsAdapter.kt @@ -5,7 +5,7 @@ import android.annotation.SuppressLint import android.view.ViewGroup import org.p2p.core.utils.Constants.SOL_SYMBOL import org.p2p.core.utils.isZero -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleTwo import org.p2p.wallet.R import org.p2p.wallet.databinding.ItemTokenSimpleBinding import org.p2p.wallet.restore.model.DerivableAccount @@ -50,7 +50,7 @@ class DerivableAccountsAdapter( @SuppressLint("SetTextI18n") fun onBind(account: DerivableAccount) { - val total = account.totalInUsd?.scaleShort() + val total = account.totalInUsd?.scaleTwo() val tokenTotal = account.totalInSol startAmountView.title = SOL_SYMBOL diff --git a/app/src/main/java/org/p2p/wallet/sell/interactor/HistoryItemMapper.kt b/app/src/main/java/org/p2p/wallet/sell/interactor/HistoryItemMapper.kt index 003ef9c7cf..8d87d0aee7 100644 --- a/app/src/main/java/org/p2p/wallet/sell/interactor/HistoryItemMapper.kt +++ b/app/src/main/java/org/p2p/wallet/sell/interactor/HistoryItemMapper.kt @@ -13,6 +13,7 @@ import org.p2p.wallet.R import org.p2p.wallet.common.date.isSameDayAs import org.p2p.wallet.common.date.toDateString import org.p2p.wallet.common.date.toZonedDateTime +import org.p2p.wallet.common.feature_toggles.toggles.remote.EthAddressEnabledFeatureToggle import org.p2p.wallet.history.model.HistoryTransaction import org.p2p.wallet.history.model.bridge.BridgeHistoryTransaction import org.p2p.wallet.history.model.rpc.RpcHistoryTransaction @@ -31,7 +32,8 @@ private const val USDC_ETH_TOKEN_SYMBOL = "USDCet" class HistoryItemMapper( private val resources: Resources, private val dispatchers: CoroutineDispatchers, - private val userLocalRepository: UserLocalRepository + private val userLocalRepository: UserLocalRepository, + private val ethAddressEnabledFeatureToggle: EthAddressEnabledFeatureToggle ) { private val historyItemFlow = MutableStateFlow?>(null) @@ -91,6 +93,10 @@ class HistoryItemMapper( } private fun createSwapBanner(tokenMintAddress: Base58String): HistoryItem.SwapBannerItem? { + if (!ethAddressEnabledFeatureToggle.isFeatureEnabled) { + return null + } + return when (tokenMintAddress.base58Value) { Constants.USDC_MINT -> { HistoryItem.SwapBannerItem( diff --git a/app/src/main/java/org/p2p/wallet/sell/ui/payload/SellPayloadPresenter.kt b/app/src/main/java/org/p2p/wallet/sell/ui/payload/SellPayloadPresenter.kt index d9ba595b2a..e89b0e0e2c 100644 --- a/app/src/main/java/org/p2p/wallet/sell/ui/payload/SellPayloadPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/sell/ui/payload/SellPayloadPresenter.kt @@ -248,6 +248,7 @@ class SellPayloadPresenter( override fun switchCurrencyMode() { val oldAmount = rawUserSelectedAmount.toBigDecimalOrZero() selectedCurrencyMode = currencyModeToSwitch + val switchedAmount = when (selectedCurrencyMode) { is CurrencyMode.Fiat -> oldAmount.toFiat() is CurrencyMode.Token -> oldAmount.toToken() @@ -290,10 +291,12 @@ class SellPayloadPresenter( val buttonState = determineButtonState() when (buttonState) { - is CashOutButtonState.CashOutAvailable, is CashOutButtonState.NotEnoughUserTokenError -> { + is CashOutButtonState.CashOutAvailable, + is CashOutButtonState.NotEnoughUserTokenError -> { startLoadSellQuoteJob() } - is CashOutButtonState.MinAmountEntered, is CashOutButtonState.MaxAmountExceeded -> { + is CashOutButtonState.MinAmountEntered, + is CashOutButtonState.MaxAmountExceeded -> { sellQuoteJob?.cancel() } } @@ -311,11 +314,9 @@ class SellPayloadPresenter( is CurrencyMode.Fiat -> userSolBalance.toFiat() is CurrencyMode.Token -> userSolBalance }.formatTokenForMoonpay() - viewState = viewState.copy( - widgetViewState = viewState.widgetViewState.copy( - inputAmount = maxAmount - ) - ) + + val newWidgetViewState = viewState.widgetViewState.copy(inputAmount = maxAmount) + viewState = viewState.copy(widgetViewState = newWidgetViewState) view?.updateViewState(viewState) onTokenAmountChanged(maxAmount) } @@ -325,15 +326,12 @@ class SellPayloadPresenter( selectedTokenAmount.isLessThan(minTokenSellAmount) -> { CashOutButtonState.MinAmountEntered(resources, minTokenSellAmount) } - selectedTokenAmount.isMoreThan(maxTokenSellAmount.orZero()) -> { CashOutButtonState.MaxAmountExceeded(resources, maxTokenSellAmount.orZero()) } - selectedTokenAmount.isMoreThan(userSolBalance) -> { CashOutButtonState.NotEnoughUserTokenError(resources) } - else -> { CashOutButtonState.CashOutAvailable(resources) } @@ -356,6 +354,10 @@ class SellPayloadPresenter( } } - private fun BigDecimal.toFiat() = this.multiply(tokenPrice).setScale(2, RoundingMode.DOWN) - private fun BigDecimal.toToken() = this.divide(tokenPrice, 2, RoundingMode.DOWN) + private fun BigDecimal.toFiat(): BigDecimal { + return this.multiply(tokenPrice).setScale(MOONPAY_DECIMAL, RoundingMode.HALF_UP) + } + private fun BigDecimal.toToken(): BigDecimal { + return this.divide(tokenPrice, MOONPAY_DECIMAL, RoundingMode.HALF_UP) + } } diff --git a/app/src/main/java/org/p2p/wallet/send/model/SendSolanaFee.kt b/app/src/main/java/org/p2p/wallet/send/model/SendSolanaFee.kt index cfa04f6914..82d3bfa269 100644 --- a/app/src/main/java/org/p2p/wallet/send/model/SendSolanaFee.kt +++ b/app/src/main/java/org/p2p/wallet/send/model/SendSolanaFee.kt @@ -7,7 +7,6 @@ import java.math.BigInteger import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import org.p2p.core.token.Token -import org.p2p.core.utils.Constants.SOL_SYMBOL import org.p2p.core.utils.formatToken import org.p2p.core.utils.fromLamports import org.p2p.core.utils.isLessThan @@ -135,7 +134,7 @@ data class SendSolanaFee constructor( minRentExemption: BigInteger ): Boolean = when { // if source is SOL, then fee payer is SOL as well - sourceTokenSymbol == SOL_SYMBOL -> { + sourceToken.isSOL -> { isEnoughSol(sourceTokenTotal, inputAmount, minRentExemption) } // assuming that source token is not SOL @@ -144,11 +143,13 @@ data class SendSolanaFee constructor( sourceTokenTotal >= inputAmount && isEnoughSol(feePayerTotalLamports, totalInSol, minRentExemption) } // assuming that source token and fee payer are same - sourceTokenSymbol == feePayerSymbol -> + sourceTokenSymbol == feePayerSymbol -> { sourceTokenTotal >= inputAmount + feeRelayerFee.totalInSourceToken + } // assuming that source token and fee payer are different - else -> + else -> { feePayerToken.totalInLamports >= feeRelayerFee.totalInFeePayerToken + } } private fun isEnoughSol( @@ -169,7 +170,7 @@ data class SendSolanaFee constructor( ): FeePayerState { val feePayerTokenCanCoverExpenses = feePayerToken.totalInLamports >= feeRelayerFee.totalInFeePayerToken val feePayerIsSourceToken = feePayerSymbol == sourceTokenSymbol - val isNotSourceSol = sourceTokenSymbol != SOL_SYMBOL + val isNotSourceSol = sourceToken.isSpl val isAllowedToCorrectAmount = strategy == CORRECT_AMOUNT && isNotSourceSol val totalNeeded = feeRelayerFee.totalInSourceToken + inputAmount val isInsufficientSolBalance = !isEnoughSolBalance() diff --git a/app/src/main/java/org/p2p/wallet/send/ui/NewSendPresenter.kt b/app/src/main/java/org/p2p/wallet/send/ui/NewSendPresenter.kt index ff0c9d6961..a04d44a06d 100644 --- a/app/src/main/java/org/p2p/wallet/send/ui/NewSendPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/send/ui/NewSendPresenter.kt @@ -24,7 +24,7 @@ import org.p2p.core.token.findByMintAddress import org.p2p.core.token.sortedWithPreferredStableCoins import org.p2p.core.utils.asNegativeUsdTransaction import org.p2p.core.utils.orZero -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleTwo import org.p2p.wallet.BuildConfig import org.p2p.wallet.R import org.p2p.wallet.alarmlogger.logger.AlarmErrorsLogger @@ -276,7 +276,7 @@ class NewSendPresenter( if (calculationMode.getCurrencyMode() is CurrencyMode.Fiat.Usd) { switchCurrencyMode() } - val newTextValue = inputAmount.scaleShort().toPlainString() + val newTextValue = inputAmount.scaleTwo().toPlainString() updateInputValue(newTextValue, forced = true) calculationMode.updateInputAmount(newTextValue) disableInputs() diff --git a/app/src/main/java/org/p2p/wallet/swap/model/orca/SwapFee.kt b/app/src/main/java/org/p2p/wallet/swap/model/orca/SwapFee.kt index a184e857a0..ab9b7bcfdf 100644 --- a/app/src/main/java/org/p2p/wallet/swap/model/orca/SwapFee.kt +++ b/app/src/main/java/org/p2p/wallet/swap/model/orca/SwapFee.kt @@ -11,7 +11,7 @@ import org.p2p.core.utils.formatToken import org.p2p.core.utils.fromLamports import org.p2p.core.utils.isLessThan import org.p2p.core.utils.isMoreThan -import org.p2p.core.utils.scaleMedium +import org.p2p.core.utils.scaleSix import org.p2p.core.utils.toUsd import org.p2p.wallet.feerelayer.model.FeePayerSelectionStrategy import org.p2p.wallet.send.model.FeePayerState @@ -54,6 +54,7 @@ class SwapFee constructor( } } + @Deprecated("Old") fun isEnoughToCoverExpenses(sourceTokenTotal: BigInteger, inputAmount: BigInteger): Boolean = when { // if source is SOL, then fee payer is SOL as well @@ -68,10 +69,10 @@ class SwapFee constructor( } val feeAmountInPayingToken: BigDecimal - get() = fee.feeInPayingToken.fromLamports(feePayerToken.decimals).scaleMedium() + get() = fee.feeInPayingToken.fromLamports(feePayerToken.decimals).scaleSix() val feeAmountInSol: BigDecimal - get() = fee.feeInSol.fromLamports(SOL_DECIMALS).scaleMedium() + get() = fee.feeInSol.fromLamports(SOL_DECIMALS).scaleSix() val feePayerSymbol: String = feePayerToken.tokenSymbol @@ -96,7 +97,7 @@ class SwapFee constructor( } else { fee.feeInPayingToken.fromLamports(feePayerToken.decimals) } - .scaleMedium() + .scaleSix() } private val accountCreationFeeUsdDecimals: BigDecimal? @@ -105,7 +106,7 @@ class SwapFee constructor( private val currentDecimals: BigDecimal = (if (feePayerToken.isSOL) fee.feeInSol else fee.feeInPayingToken) .fromLamports(feePayerToken.decimals) - .scaleMedium() + .scaleSix() private val feePayerTotalLamports: BigInteger get() = feePayerToken.totalInLamports diff --git a/app/src/main/java/org/p2p/wallet/updates/SocketUpdatesManager.kt b/app/src/main/java/org/p2p/wallet/updates/SocketUpdatesManager.kt index 860a5c1db9..fbbec05310 100644 --- a/app/src/main/java/org/p2p/wallet/updates/SocketUpdatesManager.kt +++ b/app/src/main/java/org/p2p/wallet/updates/SocketUpdatesManager.kt @@ -17,7 +17,10 @@ import kotlinx.coroutines.plus import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.p2p.core.BuildConfig.rpcPoolApiKey +import org.p2p.core.common.di.AppScope import org.p2p.core.network.ConnectionManager +import org.p2p.core.network.environment.NetworkEnvironment +import org.p2p.core.network.environment.NetworkEnvironmentManager import org.p2p.solanaj.model.types.RpcMapRequest import org.p2p.solanaj.model.types.RpcRequest import org.p2p.solanaj.ws.SocketClientCreateResult @@ -26,10 +29,7 @@ import org.p2p.solanaj.ws.SubscriptionEventListener import org.p2p.solanaj.ws.SubscriptionSocketClient import org.p2p.solanaj.ws.SubscriptionSocketClientFactory import org.p2p.solanaj.ws.impl.SocketClientException -import org.p2p.core.common.di.AppScope import org.p2p.wallet.common.feature_toggles.toggles.remote.SocketSubscriptionsFeatureToggle -import org.p2p.core.network.environment.NetworkEnvironment -import org.p2p.core.network.environment.NetworkEnvironmentManager private const val DELAY_MS = 5000L @@ -149,8 +149,10 @@ class SocketUpdatesManager( } } - override fun onClosed(code: Int, message: String) { + override fun onClientClosed(code: Int, message: String) { Timber.tag(TAG).i("Event updates connection is closed: $message") + isStarted = false + state = SocketState.DISCONNECTED } private suspend fun startActual() { diff --git a/app/src/main/java/org/p2p/wallet/user/interactor/UserInteractor.kt b/app/src/main/java/org/p2p/wallet/user/interactor/UserInteractor.kt index 6cc6e45bb0..e7b1de59b1 100644 --- a/app/src/main/java/org/p2p/wallet/user/interactor/UserInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/user/interactor/UserInteractor.kt @@ -19,7 +19,7 @@ import org.p2p.wallet.user.repository.UserTokensLocalRepository import org.p2p.wallet.utils.emptyString private const val KEY_HIDDEN_TOKENS_VISIBILITY = "KEY_HIDDEN_TOKENS_VISIBILITY" -val TOKEN_SYMBOLS_VALID_FOR_BUY: List = listOf(Constants.USDC_SYMBOL, Constants.SOL_SYMBOL) +val TOKEN_MINTS_VALID_FOR_BUY: List = listOf(Constants.USDC_MINT, Constants.WRAPPED_SOL_MINT) class UserInteractor( private val userLocalRepository: UserLocalRepository, @@ -42,17 +42,17 @@ class UserInteractor( return tokenData?.let { TokenConverter.fromNetwork(it, price) } } - suspend fun getSingleTokenForBuy(availableTokensSymbols: List = TOKEN_SYMBOLS_VALID_FOR_BUY): Token? = - getTokensForBuy(availableTokensSymbols).firstOrNull() + suspend fun getSingleTokenForBuy(availableTokensMints: List = TOKEN_MINTS_VALID_FOR_BUY): Token? = + getTokensForBuy(availableTokensMints).firstOrNull() suspend fun getTokensForBuy( - availableTokensSymbols: List = TOKEN_SYMBOLS_VALID_FOR_BUY + availableTokensMints: List = TOKEN_MINTS_VALID_FOR_BUY ): List { - val tokensToBuy = findTokensMetadataBySymbols(availableTokensSymbols) + val tokensToBuy = findTokensMetadataByMints(availableTokensMints) if (tokensToBuy.isEmpty()) { Timber.e( - IllegalStateException("No tokens to buy! All tokens: $availableTokensSymbols") + IllegalStateException("No tokens to buy! All tokens: $availableTokensMints") ) } return tokensToBuy @@ -92,8 +92,8 @@ class UserInteractor( return userTokens.any { it.publicKey == address } } - private suspend fun findTokensMetadataBySymbols(symbols: List): List { - val tokensData = symbols.mapNotNull { userLocalRepository.findTokenDataBySymbol(it) } + private suspend fun findTokensMetadataByMints(mints: List): List { + val tokensData = mints.mapNotNull(userLocalRepository::findTokenData) val prices = tokensData.let { tokenServiceRepository.getTokenPricesByAddressAsMap( tokenAddress = it.map(TokenMetadata::mintAddress), diff --git a/app/src/main/java/org/p2p/wallet/user/repository/UserLocalRepository.kt b/app/src/main/java/org/p2p/wallet/user/repository/UserLocalRepository.kt index 442900dee3..a6c7b9d928 100644 --- a/app/src/main/java/org/p2p/wallet/user/repository/UserLocalRepository.kt +++ b/app/src/main/java/org/p2p/wallet/user/repository/UserLocalRepository.kt @@ -28,6 +28,7 @@ interface UserLocalRepository { /** * Find [TokenMetadata] by its symbol */ + @Deprecated("use findTokenData with mint address") fun findTokenDataBySymbol(symbol: String): TokenMetadata? /** diff --git a/app/src/main/java/org/p2p/wallet/user/repository/UserTokensDatabaseRepository.kt b/app/src/main/java/org/p2p/wallet/user/repository/UserTokensDatabaseRepository.kt index f89dd55ce9..83de763088 100644 --- a/app/src/main/java/org/p2p/wallet/user/repository/UserTokensDatabaseRepository.kt +++ b/app/src/main/java/org/p2p/wallet/user/repository/UserTokensDatabaseRepository.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.p2p.core.crypto.Base58String import org.p2p.core.token.Token -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleTwo import org.p2p.token.service.model.TokenServicePrice import org.p2p.wallet.home.db.TokenDao import org.p2p.wallet.home.model.TokenComparator @@ -57,7 +57,7 @@ class UserTokensDatabaseRepository( private fun calculateUserBalance(tokens: List): BigDecimal = tokens.mapNotNull(Token.Active::totalInUsd) .fold(BigDecimal.ZERO, BigDecimal::add) - .scaleShort() + .scaleTwo() override suspend fun getUserTokens(): List { return tokensDao.getTokens() diff --git a/app/src/test/java/org/p2p/wallet/striga/offramp/ui/StrigaOffRampPresenterCalculationTest.kt b/app/src/test/java/org/p2p/wallet/striga/offramp/ui/StrigaOffRampPresenterCalculationTest.kt index c16eeccb04..2e3a208cb2 100644 --- a/app/src/test/java/org/p2p/wallet/striga/offramp/ui/StrigaOffRampPresenterCalculationTest.kt +++ b/app/src/test/java/org/p2p/wallet/striga/offramp/ui/StrigaOffRampPresenterCalculationTest.kt @@ -1,14 +1,15 @@ package org.p2p.wallet.striga.offramp.ui import io.mockk.verify +import org.junit.Ignore import org.junit.Test import java.math.BigDecimal import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest +import org.p2p.core.utils.divideSafe import org.p2p.wallet.striga.offramp.models.StrigaOffRampButtonState import org.p2p.wallet.striga.offramp.models.StrigaOffRampTokenType -import org.p2p.core.utils.divideSafe @OptIn(ExperimentalCoroutinesApi::class) class StrigaOffRampPresenterCalculationTest : StrigaOffRampPresenterBaseTest() { @@ -56,6 +57,7 @@ class StrigaOffRampPresenterCalculationTest : StrigaOffRampPresenterBaseTest() { } @Test + @Ignore("Striga is not used") fun `GIVEN striga off ramp WHEN clicked all amount THEN check states for A and B tokens`() = runTest { val amountA = BigDecimal("4000.21") val amountB = amountA * exchangeRate.sellRate diff --git a/buildSrc/src/main/java/AppVersions.kt b/buildSrc/src/main/java/AppVersions.kt index dd354f5386..0baa3954e4 100644 --- a/buildSrc/src/main/java/AppVersions.kt +++ b/buildSrc/src/main/java/AppVersions.kt @@ -1,7 +1,7 @@ object AppVersions { const val VERSION_MAJOR = 2 - const val VERSION_MINOR = 11 - const val VERSION_HOTFIX = 2 + const val VERSION_MINOR = 12 + const val VERSION_HOTFIX = 0 const val VERSION_PATCH = 1 - const val VERSION_BUILD = 3582 + const val VERSION_BUILD = 3586 } diff --git a/core/src/main/java/org/p2p/core/token/Token.kt b/core/src/main/java/org/p2p/core/token/Token.kt index 67ec130c2e..a1e44f5c2c 100644 --- a/core/src/main/java/org/p2p/core/token/Token.kt +++ b/core/src/main/java/org/p2p/core/token/Token.kt @@ -8,10 +8,9 @@ import kotlinx.parcelize.Parcelize import org.p2p.core.crypto.Base58String import org.p2p.core.crypto.toBase58Instance import org.p2p.core.utils.Constants -import org.p2p.core.utils.Constants.REN_BTC_SYMBOL import org.p2p.core.utils.Constants.SOL_NAME -import org.p2p.core.utils.Constants.USDC_SYMBOL -import org.p2p.core.utils.Constants.USDT_SYMBOL +import org.p2p.core.utils.Constants.USDC_MINT +import org.p2p.core.utils.Constants.USDT_MINT import org.p2p.core.utils.Constants.WRAPPED_ETH_MINT import org.p2p.core.utils.Constants.WRAPPED_SOL_MINT import org.p2p.core.utils.asCurrency @@ -20,8 +19,8 @@ import org.p2p.core.utils.formatFiat import org.p2p.core.utils.formatToken import org.p2p.core.utils.isZero import org.p2p.core.utils.orZero -import org.p2p.core.utils.scaleLong -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleNine +import org.p2p.core.utils.scaleTwo import org.p2p.core.utils.toLamports import org.p2p.core.utils.toPowerValue import org.p2p.core.wrapper.eth.EthAddress @@ -78,7 +77,7 @@ sealed class Token constructor( @IgnoredOnParcel val totalInUsdScaled: BigDecimal? - get() = totalInUsd?.scaleShort() + get() = totalInUsd?.scaleTwo() @IgnoredOnParcel val isZero: Boolean @@ -202,35 +201,31 @@ sealed class Token constructor( @IgnoredOnParcel val isSOL: Boolean - get() = mintAddress == WRAPPED_SOL_MINT + get() = mintAddress.equals(WRAPPED_SOL_MINT, ignoreCase = true) @IgnoredOnParcel val isSpl: Boolean - get() = mintAddress != WRAPPED_SOL_MINT + get() = !isSOL @IgnoredOnParcel val isToken2022: Boolean get() = programId == Constants.SOLANA_TOKEN_2022_PROGRAM_ID - @IgnoredOnParcel - val isRenBTC: Boolean - get() = tokenSymbol == REN_BTC_SYMBOL - @IgnoredOnParcel val isUSDC: Boolean - get() = tokenSymbol.equals(USDC_SYMBOL, ignoreCase = true) + get() = mintAddress.equals(USDC_MINT, ignoreCase = true) @IgnoredOnParcel val isUSDT: Boolean - get() = tokenSymbol == USDT_SYMBOL + get() = mintAddress.equals(USDT_MINT, ignoreCase = true) @IgnoredOnParcel val usdRateOrZero: BigDecimal - get() = rate ?: BigDecimal.ZERO + get() = rate.orZero() @IgnoredOnParcel val currencyFormattedRate: String - get() = (rate ?: BigDecimal.ZERO).asCurrency(currencySymbol) + get() = usdRateOrZero.asCurrency(currencySymbol) @IgnoredOnParcel val currencySymbol: String @@ -264,7 +259,7 @@ sealed class Token constructor( tokenName = SOL_NAME, iconUrl = tokenMetadata.iconUrl, totalInUsd = if (amount == 0L) null else solPrice?.let { total.multiply(it) }, - total = total.scaleLong(tokenMetadata.decimals), + total = total.scaleNine(tokenMetadata.decimals), rate = solPrice, tokenServiceAddress = Constants.TOKEN_SERVICE_NATIVE_SOL_TOKEN, visibility = TokenVisibility.DEFAULT, @@ -289,9 +284,9 @@ fun List.findByMintAddress(mintAddress: String?): Token.Active? = fun List.sortedWithPreferredStableCoins(): List { return sortedWith( compareBy { - when (it.tokenSymbol) { - USDC_SYMBOL -> 1 - USDT_SYMBOL -> 2 + when (it.mintAddress) { + USDC_MINT -> 1 + USDT_MINT -> 2 else -> 3 } } diff --git a/core/src/main/java/org/p2p/core/utils/AmountExtensions.kt b/core/src/main/java/org/p2p/core/utils/AmountExtensions.kt index 209f0a4340..4c3fe4d80a 100644 --- a/core/src/main/java/org/p2p/core/utils/AmountExtensions.kt +++ b/core/src/main/java/org/p2p/core/utils/AmountExtensions.kt @@ -11,46 +11,45 @@ const val SOL_DECIMALS = 9 const val MOONPAY_DECIMAL = 2 const val STRIGA_FIAT_DECIMALS = 2 -private const val SCALE_VALUE_SHORT = 2 -private const val SCALE_VALUE_MEDIUM = 6 -private const val SCALE_VALUE_LONG = 9 +private const val SCALE_VALUE_TWO = 2 +private const val SCALE_VALUE_SIX = 6 +private const val SCALE_VALUE_NINE = 9 private const val AMOUNT_MIN_VALUE = 0.01 fun String?.toBigDecimalOrZero(): BigDecimal { val removedZeros = this?.replace("(?<=\\d)\\.?0+(?![\\d\\.])", emptyString()) - return removedZeros?.toBigDecimalOrNull() ?: BigDecimal.ZERO + return removedZeros?.toBigDecimalOrNull().orZero() } fun String?.toBigIntegerOrZero(): BigInteger { - return this?.toBigIntegerOrNull() ?: BigInteger.ZERO + return this?.toBigIntegerOrNull().orZero() } fun Int.toPowerValue(): BigDecimal = BASE_TEN.pow(this) -fun BigDecimal.scaleShortOrFirstNotZero(): BigDecimal { - return if (isZero()) { - this +fun BigDecimal.scaleTwoOrFirstNotZero(): BigDecimal { + if (isZero()) { + return this + } + val scale = if (scale() > SCALE_VALUE_TWO) { + scale() - (unscaledValue().toString().length - SCALE_VALUE_TWO) } else { - val scale = if (scale() > SCALE_VALUE_SHORT) { - scale() - (unscaledValue().toString().length - SCALE_VALUE_SHORT) - } else { - SCALE_VALUE_SHORT - } - // removing zeros, case: 0.02000 -> 0.2 - setScale(scale, RoundingMode.DOWN).stripTrailingZeros() + SCALE_VALUE_TWO } + // removing zeros, case: 0.02000 -> 0.2 + return setScale(scale, RoundingMode.DOWN).stripTrailingZeros() } -fun BigDecimal.scaleShort(): BigDecimal = - this.setScale(SCALE_VALUE_SHORT, RoundingMode.DOWN) +fun BigDecimal.scaleTwo(): BigDecimal = + this.setScale(SCALE_VALUE_TWO, RoundingMode.DOWN) .stripTrailingZeros() // removing zeros, case: 0.02000 -> 0.02 -fun BigDecimal.scaleMedium(): BigDecimal = - if (this.isZero()) this else this.setScale(SCALE_VALUE_MEDIUM, RoundingMode.DOWN) +fun BigDecimal.scaleSix(): BigDecimal = + if (this.isZero()) this else this.setScale(SCALE_VALUE_SIX, RoundingMode.DOWN) .stripTrailingZeros() // removing zeros, case: 0.02000 -> 0.02 -fun BigDecimal.scaleLong(decimals: Int = SCALE_VALUE_LONG): BigDecimal = +fun BigDecimal.scaleNine(decimals: Int = SCALE_VALUE_NINE): BigDecimal = if (this.isZero()) this else this.setScale(decimals, RoundingMode.DOWN) .stripTrailingZeros() // removing zeros, case: 0.02000 -> 0.02 @@ -59,16 +58,16 @@ fun BigDecimal.scaleLong(decimals: Int = SCALE_VALUE_LONG): BigDecimal = fun BigInteger.fromLamports(decimals: Int): BigDecimal = (this.toBigDecimal().divide(BASE_TEN.pow(decimals), 18, RoundingMode.HALF_DOWN)) .stripTrailingZeros() // removing zeros, case: 0.02000 -> 0.02 - .scaleLong(decimals) + .scaleNine(decimals) fun BigDecimal.toLamports(decimals: Int): BigInteger = this.multiply(decimals.toPowerValue()).toBigInteger() fun BigDecimal.toUsd(usdRate: BigDecimal?): BigDecimal? = - usdRate?.let { this.multiply(it).scaleShort() } + usdRate?.let { this.multiply(it).scaleTwo() } fun BigDecimal.toUsd(token: Token): BigDecimal? = - token.rate?.let { this.multiply(it).scaleShort() } + token.rate?.let { this.multiply(it).scaleTwo() } // case: 1000.023000 -> 1 000.02 fun BigDecimal.formatFiat(): String = formatWithDecimals(FIAT_FRACTION_LENGTH) diff --git a/core/src/main/java/org/p2p/core/utils/Constants.kt b/core/src/main/java/org/p2p/core/utils/Constants.kt index f18d321f79..5010d1c355 100644 --- a/core/src/main/java/org/p2p/core/utils/Constants.kt +++ b/core/src/main/java/org/p2p/core/utils/Constants.kt @@ -1,5 +1,7 @@ package org.p2p.core.utils +import org.p2p.core.crypto.toBase58Instance + object Constants { const val MIN_REQUIRED_ACCOUNT_INFO_DATA_LENGTH = 0 @@ -26,6 +28,7 @@ object Constants { const val TOKEN_SERVICE_NATIVE_ETH_TOKEN = "native" const val WRAPPED_SOL_MINT = "So11111111111111111111111111111111111111112" + val WRAPPED_SOL_MINT_B58 = WRAPPED_SOL_MINT.toBase58Instance() const val WRAPPED_ETH_MINT = "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs" const val WRAPPED_BTC_MINT = "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh" diff --git a/core/src/main/java/org/p2p/core/utils/DecimalFormatter.kt b/core/src/main/java/org/p2p/core/utils/DecimalFormatter.kt index 3885e48461..64509284ae 100644 --- a/core/src/main/java/org/p2p/core/utils/DecimalFormatter.kt +++ b/core/src/main/java/org/p2p/core/utils/DecimalFormatter.kt @@ -48,6 +48,8 @@ object DecimalFormatter { fmt.minimumFractionDigits = if (keepInitialDecimals) clamp(value.scale(), 0, decimals) else 0 } fmt.decimalFormatSymbols = symbols + // HALF_DOWN to we won't round, for example, 1.505 to 1.51 which is not valid + fmt.roundingMode = RoundingMode.DOWN return fmt.format(value) } } diff --git a/solana/src/main/java/org/p2p/solanaj/ws/SocketStateListener.kt b/solana/src/main/java/org/p2p/solanaj/ws/SocketStateListener.kt index 4a679f90fe..fb31ff5357 100644 --- a/solana/src/main/java/org/p2p/solanaj/ws/SocketStateListener.kt +++ b/solana/src/main/java/org/p2p/solanaj/ws/SocketStateListener.kt @@ -1,10 +1,8 @@ package org.p2p.solanaj.ws -import java.lang.Exception - interface SocketStateListener { fun onConnected() fun onWebSocketPong() fun onFailed(exception: Exception) - fun onClosed(code: Int, message: String) + fun onClientClosed(code: Int, message: String) } diff --git a/solana/src/main/java/org/p2p/solanaj/ws/impl/SubscriptionWebSocketClient.kt b/solana/src/main/java/org/p2p/solanaj/ws/impl/SubscriptionWebSocketClient.kt index 2d4c51d37f..502e39c5a6 100644 --- a/solana/src/main/java/org/p2p/solanaj/ws/impl/SubscriptionWebSocketClient.kt +++ b/solana/src/main/java/org/p2p/solanaj/ws/impl/SubscriptionWebSocketClient.kt @@ -130,7 +130,7 @@ internal class SubscriptionWebSocketClient internal constructor( override fun onClose(code: Int, reason: String, remote: Boolean) { val closedFrom = if (remote) "remote peer" else "us" - stateListener.onClosed( + stateListener.onClientClosed( code = code, message = "Connection closed by $closedFrom Code: $code Reason: $reason" ) diff --git a/token-service/src/main/java/org/p2p/token/service/model/TokenServicePrice.kt b/token-service/src/main/java/org/p2p/token/service/model/TokenServicePrice.kt index 20450135fb..b4830a4b4a 100644 --- a/token-service/src/main/java/org/p2p/token/service/model/TokenServicePrice.kt +++ b/token-service/src/main/java/org/p2p/token/service/model/TokenServicePrice.kt @@ -1,7 +1,7 @@ package org.p2p.token.service.model import java.math.BigDecimal -import org.p2p.core.utils.scaleShort +import org.p2p.core.utils.scaleTwo data class TokenServicePrice( val tokenAddress: String, @@ -9,7 +9,7 @@ data class TokenServicePrice( val network: TokenServiceNetwork ) { val scaledUsdRate: BigDecimal? - get() = rate.usd?.scaleShort() + get() = rate.usd?.scaleTwo() val usdRate: BigDecimal? get() = rate.usd