diff --git a/common/src/main/java/org/dash/wallet/common/Constants.java b/common/src/main/java/org/dash/wallet/common/Constants.java index 2f3b4ecdd6..6b9bbaf5b8 100644 --- a/common/src/main/java/org/dash/wallet/common/Constants.java +++ b/common/src/main/java/org/dash/wallet/common/Constants.java @@ -15,11 +15,11 @@ public class Constants { public static final char CURRENCY_MINUS_SIGN = '\uff0d'; public static final String PREFIX_ALMOST_EQUAL_TO = Character.toString(CHAR_ALMOST_EQUAL_TO) + CHAR_THIN_SPACE; - public static Coin MAX_MONEY = MainNetParams.get().getMaxMoney(); - public static final int REQUEST_CODE_BUY_SELL = 100; public static final int USER_BUY_SELL_DASH = 101; public static final int RESULT_CODE_GO_HOME = 100; + public static Coin MAX_MONEY = MainNetParams.get().getMaxMoney(); + public static final Coin ECONOMIC_FEE = Coin.valueOf(1000); public static final MonetaryFormat SEND_PAYMENT_LOCAL_FORMAT = new MonetaryFormat().withLocale(GenericUtils.getDeviceLocale()).minDecimals(2).optionalDecimals(); } diff --git a/common/src/main/java/org/dash/wallet/common/transactions/IgnoreAddressTxFilter.kt b/common/src/main/java/org/dash/wallet/common/transactions/NotFromAddressTxFilter.kt similarity index 95% rename from common/src/main/java/org/dash/wallet/common/transactions/IgnoreAddressTxFilter.kt rename to common/src/main/java/org/dash/wallet/common/transactions/NotFromAddressTxFilter.kt index 6bd8590ff7..d5282998d8 100644 --- a/common/src/main/java/org/dash/wallet/common/transactions/IgnoreAddressTxFilter.kt +++ b/common/src/main/java/org/dash/wallet/common/transactions/NotFromAddressTxFilter.kt @@ -21,7 +21,7 @@ import org.bitcoinj.core.Address import org.bitcoinj.core.Transaction import org.bitcoinj.script.ScriptPattern -class IgnoreAddressTxFilter(private val ignoreAddress: Address): TransactionFilter { +class NotFromAddressTxFilter(private val ignoreAddress: Address): TransactionFilter { override fun matches(tx: Transaction): Boolean { val networkParameters = ignoreAddress.parameters diff --git a/common/src/main/java/org/dash/wallet/common/ui/dialogs/AdaptiveDialog.kt b/common/src/main/java/org/dash/wallet/common/ui/dialogs/AdaptiveDialog.kt index 3414179288..fef00af3ba 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/dialogs/AdaptiveDialog.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/dialogs/AdaptiveDialog.kt @@ -61,11 +61,17 @@ class AdaptiveDialog(@LayoutRes private val layout: Int): DialogFragment() { ) } - suspend fun withProgress(message: String, activity: FragmentActivity, action: suspend () -> Unit) { + suspend fun withProgress( + message: String, + activity: FragmentActivity, + action: suspend () -> T + ): T { val dialog = progress(message) dialog.show(activity) { } - action.invoke() + val result = action.invoke() dialog.dismiss() + + return result } @JvmStatic diff --git a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt index 0921755f22..b00ebcacbd 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt @@ -55,6 +55,10 @@ class EnterAmountViewModel @Inject constructor( val dashToFiatDirection: LiveData get() = _dashToFiatDirection + private val _minAmount = MutableLiveData(Coin.ZERO) + val minAmount: LiveData + get() = _minAmount + private val _maxAmount = MutableLiveData(Coin.ZERO) val maxAmount: LiveData get() = _maxAmount @@ -64,20 +68,19 @@ class EnterAmountViewModel @Inject constructor( get() = _amount val canContinue: LiveData - get() = MediatorLiveData().also { liveData -> - fun getValue(amount: Coin, maxAmount: Coin): Boolean { - return amount > Coin.ZERO && (maxAmount == Coin.ZERO || amount <= maxAmount) - } + get() = MediatorLiveData().apply { + fun canContinue(): Boolean { + val amount = _amount.value ?: Coin.ZERO + val minAmount = _minAmount.value ?: Coin.ZERO + val maxAmount = _maxAmount.value ?: Coin.ZERO - liveData.addSource(_amount) { - liveData.value = getValue(it, _maxAmount.value ?: Coin.ZERO) - } - liveData.addSource(_maxAmount) { - liveData.value = getValue(_amount.value ?: Coin.ZERO, it) - } - liveData.addSource(_dashToFiatDirection) { - liveData.value = getValue(_amount.value ?: Coin.ZERO, _maxAmount.value ?: Coin.ZERO) + return amount > minAmount && (maxAmount == Coin.ZERO || amount <= maxAmount) } + + addSource(_amount) { value = canContinue() } + addSource(_minAmount) { value = canContinue() } + addSource(_maxAmount) { value = canContinue() } + addSource(_dashToFiatDirection) { value = canContinue() } } init { @@ -90,4 +93,8 @@ class EnterAmountViewModel @Inject constructor( fun setMaxAmount(coin: Coin) { _maxAmount.value = coin } + + fun setMinAmount(coin: Coin) { + _minAmount.value = coin + } } diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index a78803b3d3..be41554c9a 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -50,4 +50,6 @@ Max Select Currency %1$s DASH = %2$s + The amount is too small to send + Insufficient funds \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeApi.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeApi.kt index 7a4096e5a7..5a565a9599 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeApi.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeApi.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.first import org.bitcoinj.core.Address import org.bitcoinj.core.Coin import org.bitcoinj.core.Transaction +import org.dash.wallet.common.Constants import org.dash.wallet.common.WalletDataProvider import org.dash.wallet.common.data.Resource import org.dash.wallet.common.services.NotificationService @@ -39,6 +40,7 @@ import org.dash.wallet.common.services.SendPaymentService import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.transactions.LockedTransaction import org.dash.wallet.integrations.crowdnode.R +import org.dash.wallet.integrations.crowdnode.model.ApiCode import org.dash.wallet.integrations.crowdnode.transactions.* import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants import org.dash.wallet.integrations.crowdnode.utils.ModuleConfiguration @@ -49,6 +51,7 @@ import java.math.RoundingMode import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import javax.inject.Inject +import kotlin.math.min enum class SignUpStatus { NotStarted, @@ -69,7 +72,8 @@ interface CrowdNodeApi { fun persistentSignUp(accountAddress: Address) suspend fun signUp(accountAddress: Address) - suspend fun deposit(accountAddress: Address, amount: Coin): Boolean + suspend fun deposit(amount: Coin): Boolean + suspend fun withdraw(amount: Coin): Boolean fun refreshBalance(retries: Int = 0) suspend fun reset() } @@ -151,7 +155,7 @@ class CrowdNodeBlockchainApi @Inject constructor( notifyIfNeeded(appContext.getString(R.string.crowdnode_account_ready), "crowdnode_ready") } catch (ex: Exception) { - log.info("CrowdNode error: $ex") + log.error("CrowdNode error: $ex") analyticsService.logError(ex, "status: ${signUpStatus.value}") apiError.value = ex @@ -161,13 +165,16 @@ class CrowdNodeBlockchainApi @Inject constructor( } } - override suspend fun deposit(accountAddress: Address, amount: Coin): Boolean { + override suspend fun deposit(amount: Coin): Boolean { + val accountAddress = this.accountAddress + requireNotNull(accountAddress) { "Account address is null, make sure to sign up" } + return try { apiError.value = null - val topUpTx = topUpAddress(accountAddress, amount) + val topUpTx = topUpAddress(accountAddress, amount + Constants.ECONOMIC_FEE) log.info("topUpTx id: ${topUpTx.txId}") val crowdNodeAddress = CrowdNodeConstants.getCrowdNodeAddress(params) - val depositTx = paymentService.sendCoins(crowdNodeAddress, amount, accountAddress, true) + val depositTx = paymentService.sendCoins(crowdNodeAddress, amount, accountAddress) log.info("depositTx id: ${depositTx.txId}") responseScope.launch { @@ -193,34 +200,76 @@ class CrowdNodeBlockchainApi @Inject constructor( } } + override suspend fun withdraw(amount: Coin): Boolean { + val accountAddress = this.accountAddress + requireNotNull(accountAddress) { "Account address is null, make sure to sign up" } + + val balance = this.balance.value.data ?: Coin.ZERO + require(amount <= balance) { "Amount is larger than CrowdNode balance" } + + return try { + apiError.value = null + + val maxPermil = ApiCode.WithdrawAll.code + val requestPermil = min(amount.value * maxPermil / balance.value, maxPermil) + val requestValue = CrowdNodeConstants.API_OFFSET + Coin.valueOf(requestPermil) + val topUpTx = topUpAddress(accountAddress, requestValue + Constants.ECONOMIC_FEE) + log.info("topUpTx id: ${topUpTx.txId}") + + val crowdNodeAddress = CrowdNodeConstants.getCrowdNodeAddress(params) + val withdrawTx = paymentService.sendCoins(crowdNodeAddress, requestValue, accountAddress) + log.info("withdrawTx id: ${withdrawTx.txId}") + + responseScope.launch { + val errorResponse = CrowdNodeErrorResponse(params, requestValue) + val tx = walletDataProvider.observeTransactions( + CrowdNodeWithdrawalQueueResponse(params), + errorResponse + ).first() + log.info("got withdrawal queue response: ${tx.txId}") + + if (errorResponse.matches(tx)) { + val ex = CrowdNodeException("Withdraw error") + handleError(ex, appContext.getString(R.string.crowdnode_withdraw_error)) + } + } + + return true + } catch (ex: Exception) { + handleError(ex, appContext.getString(R.string.crowdnode_withdraw_error)) + false + } + } + override fun refreshBalance(retries: Int) { responseScope.launch { val lastBalance = config.lastBalance.first() + var currentBalance = Resource.loading(Coin.valueOf(lastBalance)) + balance.value = currentBalance for (i in 0..retries) { if (i != 0) { delay(TimeUnit.SECONDS.toMillis(pow(5, i))) } - balance.value = Resource.loading() - val newBalance = resolveBalance() - balance.value = newBalance + currentBalance = resolveBalance() - if (lastBalance != newBalance.data?.value) { + if (lastBalance != currentBalance.data?.value) { // balance changed, no need to retry anymore break } } + + balance.value = currentBalance } } - override suspend fun reset() { log.info("reset is triggered") signUpStatus.value = SignUpStatus.NotStarted accountAddress = null apiError.value = null - config.setCrowdNodeError("") + config.clearAll() } private fun restoreStatus() { @@ -308,6 +357,7 @@ class CrowdNodeBlockchainApi @Inject constructor( } catch (ex: HttpException) { Resource.error(ex) } catch (ex: Exception) { + log.error("Error while resolving balance: $ex") analyticsService.logError(ex) Resource.error(ex) } @@ -316,19 +366,17 @@ class CrowdNodeBlockchainApi @Inject constructor( } } - private suspend fun fetchBalance(address: String): String { - val response = crowdNodeWebApi.getTransactions(address) - var total = BigDecimal.ZERO - response.body()?.value?.forEach { tx -> - total += BigDecimal.valueOf(tx.amount) - } - return total.setScale(8, RoundingMode.HALF_UP).toString() + val response = crowdNodeWebApi.getBalance(address) + val balance = BigDecimal.valueOf(response.body()?.totalBalance ?: 0.0) + + return balance.setScale(8, RoundingMode.HALF_UP).toString() } private fun handleError(ex: Exception, error: String) { apiError.value = ex notifyIfNeeded(error, "crowdnode_error") + log.error("$error: $ex") analyticsService.logError(ex) } diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeWebApi.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeWebApi.kt index fe09e6b1da..0ef8063ce2 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeWebApi.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/api/CrowdNodeWebApi.kt @@ -17,7 +17,8 @@ package org.dash.wallet.integrations.crowdnode.api -import org.dash.wallet.integrations.crowdnode.model.CrowdNodeResponse +import org.dash.wallet.integrations.crowdnode.model.CrowdNodeBalance +import org.dash.wallet.integrations.crowdnode.model.CrowdNodeTx import retrofit2.Response import retrofit2.http.* @@ -25,5 +26,10 @@ interface CrowdNodeWebApi { @GET("odata/apifundings/GetFunds(address='{address}')") suspend fun getTransactions( @Path("address") address: String - ): Response + ): Response> + + @GET("odata/apifundings/GetBalance(address='{address}')") + suspend fun getBalance( + @Path("address") address: String + ): Response } diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/di/CrowdNodeModule.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/di/CrowdNodeModule.kt index aaa3bb8c09..684126f33b 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/di/CrowdNodeModule.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/di/CrowdNodeModule.kt @@ -22,11 +22,13 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.ExperimentalCoroutinesApi import org.dash.wallet.integrations.crowdnode.api.* import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) +@ExperimentalCoroutinesApi abstract class CrowdNodeModule { companion object { @Provides diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/ApiCode.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/ApiCode.kt new file mode 100644 index 0000000000..4d28a72f2f --- /dev/null +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/ApiCode.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.crowdnode.model + +enum class ApiCode(val code: Long, val isRequest: Boolean = false) { + PleaseAcceptTerms(2, true), + WelcomeToApi(4, true), + DepositReceived(8, true), + WithdrawalQueue(16, true), + WithdrawAll(1000, false), + SignUp(131072, false), + AcceptTerms(65536, false), + MaxCode(131072, false) +} \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/CrowdNodeBalance.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/CrowdNodeBalance.kt new file mode 100644 index 0000000000..5a4a43de4c --- /dev/null +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/CrowdNodeBalance.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.crowdnode.model + +import com.google.gson.annotations.SerializedName + +data class CrowdNodeBalance ( + @SerializedName("DashAddress") + val dashAddress : String, + @SerializedName("TotalBalance") + val totalBalance : Double, + @SerializedName("TotalActiveBalance") + val totalActiveBalance : Double, + @SerializedName("TotalDividend") + val totalDividend : Double +) \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/CrowdNodeTx.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/CrowdNodeTx.kt index 2a54a5df7b..eaecef7cd5 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/CrowdNodeTx.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/model/CrowdNodeTx.kt @@ -19,13 +19,6 @@ package org.dash.wallet.integrations.crowdnode.model import com.google.gson.annotations.SerializedName -data class CrowdNodeResponse ( - @SerializedName("@odata.context") - val dataContext : String, - @SerializedName("value") - val value : List -) - data class CrowdNodeTx ( @SerializedName("FundingType") val fundingType : String, diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsResponse.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsResponse.kt index 3f657f59be..e6d2e6925f 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsResponse.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsResponse.kt @@ -20,6 +20,7 @@ package org.dash.wallet.integrations.crowdnode.transactions import org.bitcoinj.core.Coin import org.bitcoinj.core.NetworkParameters import org.dash.wallet.common.transactions.CoinsFromAddressTxFilter +import org.dash.wallet.integrations.crowdnode.model.ApiCode import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants // TODO: consider making sure that `toAddress` matches our account address @@ -27,6 +28,7 @@ class CrowdNodeAcceptTermsResponse(networkParams: NetworkParameters): CoinsFromA CrowdNodeConstants.getCrowdNodeAddress(networkParams), ACCEPT_TERMS_RESPONSE_CODE ) { companion object { - val ACCEPT_TERMS_RESPONSE_CODE: Coin = CrowdNodeConstants.CROWDNODE_OFFSET + Coin.valueOf(2) + val ACCEPT_TERMS_RESPONSE_CODE: Coin = + CrowdNodeConstants.API_OFFSET + Coin.valueOf(ApiCode.PleaseAcceptTerms.code) } } \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsTx.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsTx.kt index 2fd74b63c6..d1fc50b35b 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsTx.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeAcceptTermsTx.kt @@ -20,12 +20,14 @@ package org.dash.wallet.integrations.crowdnode.transactions import org.bitcoinj.core.Coin import org.bitcoinj.core.NetworkParameters import org.dash.wallet.common.transactions.CoinsToAddressTxFilter +import org.dash.wallet.integrations.crowdnode.model.ApiCode import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants class CrowdNodeAcceptTermsTx(networkParams: NetworkParameters): CoinsToAddressTxFilter( CrowdNodeConstants.getCrowdNodeAddress(networkParams), ACCEPT_TERMS_REQUEST_CODE ) { companion object { - val ACCEPT_TERMS_REQUEST_CODE: Coin = CrowdNodeConstants.CROWDNODE_OFFSET + Coin.valueOf(65536) + val ACCEPT_TERMS_REQUEST_CODE: Coin = + CrowdNodeConstants.API_OFFSET + Coin.valueOf(ApiCode.AcceptTerms.code) } } \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeDepositReceivedResponse.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeDepositReceivedResponse.kt index 5267bf1068..946e028388 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeDepositReceivedResponse.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeDepositReceivedResponse.kt @@ -20,12 +20,14 @@ package org.dash.wallet.integrations.crowdnode.transactions import org.bitcoinj.core.Coin import org.bitcoinj.core.NetworkParameters import org.dash.wallet.common.transactions.CoinsFromAddressTxFilter +import org.dash.wallet.integrations.crowdnode.model.ApiCode import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants class CrowdNodeDepositReceivedResponse(networkParams: NetworkParameters): CoinsFromAddressTxFilter( CrowdNodeConstants.getCrowdNodeAddress(networkParams), DEPOSIT_RECEIVED_RESPONSE_CODE ) { companion object { - val DEPOSIT_RECEIVED_RESPONSE_CODE: Coin = CrowdNodeConstants.CROWDNODE_OFFSET + Coin.valueOf(8) + val DEPOSIT_RECEIVED_RESPONSE_CODE: Coin = + CrowdNodeConstants.API_OFFSET + Coin.valueOf(ApiCode.DepositReceived.code) } } \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeSignUpTx.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeSignUpTx.kt index ef0d8f1c3b..2d89b1720f 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeSignUpTx.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeSignUpTx.kt @@ -20,12 +20,14 @@ package org.dash.wallet.integrations.crowdnode.transactions import org.bitcoinj.core.Coin import org.bitcoinj.core.NetworkParameters import org.dash.wallet.common.transactions.CoinsToAddressTxFilter +import org.dash.wallet.integrations.crowdnode.model.ApiCode import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants class CrowdNodeSignUpTx(networkParams: NetworkParameters): CoinsToAddressTxFilter( CrowdNodeConstants.getCrowdNodeAddress(networkParams), SIGNUP_REQUEST_CODE ) { companion object { - val SIGNUP_REQUEST_CODE: Coin = CrowdNodeConstants.CROWDNODE_OFFSET + Coin.valueOf(131072) + val SIGNUP_REQUEST_CODE: Coin = + CrowdNodeConstants.API_OFFSET + Coin.valueOf(ApiCode.SignUp.code) } } \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWelcomeToApiResponse.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWelcomeToApiResponse.kt index 078531b6cb..aba0cb1fff 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWelcomeToApiResponse.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWelcomeToApiResponse.kt @@ -20,6 +20,7 @@ package org.dash.wallet.integrations.crowdnode.transactions import org.bitcoinj.core.Coin import org.bitcoinj.core.NetworkParameters import org.dash.wallet.common.transactions.CoinsFromAddressTxFilter +import org.dash.wallet.integrations.crowdnode.model.ApiCode import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants // TODO: consider making sure that `toAddress` matches our account address @@ -27,6 +28,7 @@ class CrowdNodeWelcomeToApiResponse(networkParams: NetworkParameters): CoinsFrom CrowdNodeConstants.getCrowdNodeAddress(networkParams), WELCOME_TO_API_RESPONSE_CODE ) { companion object { - val WELCOME_TO_API_RESPONSE_CODE: Coin = CrowdNodeConstants.CROWDNODE_OFFSET + Coin.valueOf(4) + val WELCOME_TO_API_RESPONSE_CODE: Coin = + CrowdNodeConstants.API_OFFSET + Coin.valueOf(ApiCode.WelcomeToApi.code) } } \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWithdrawalQueueResponse.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWithdrawalQueueResponse.kt new file mode 100644 index 0000000000..5fe8ac3a71 --- /dev/null +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWithdrawalQueueResponse.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.crowdnode.transactions + +import org.bitcoinj.core.Coin +import org.bitcoinj.core.NetworkParameters +import org.dash.wallet.common.transactions.CoinsFromAddressTxFilter +import org.dash.wallet.integrations.crowdnode.model.ApiCode +import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants + +class CrowdNodeWithdrawalQueueResponse(networkParams: NetworkParameters): CoinsFromAddressTxFilter( + CrowdNodeConstants.getCrowdNodeAddress(networkParams), WITHDRAWAL_QUEUE_RESPONSE_CODE +) { + companion object { + val WITHDRAWAL_QUEUE_RESPONSE_CODE: Coin = + CrowdNodeConstants.API_OFFSET + Coin.valueOf(ApiCode.WithdrawalQueue.code) + } +} \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWithdrawalReceivedTx.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWithdrawalReceivedTx.kt new file mode 100644 index 0000000000..3c19d66c38 --- /dev/null +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/transactions/CrowdNodeWithdrawalReceivedTx.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.crowdnode.transactions + +import org.bitcoinj.core.Coin +import org.bitcoinj.core.NetworkParameters +import org.bitcoinj.core.Transaction +import org.bitcoinj.script.ScriptPattern +import org.dash.wallet.common.transactions.TransactionFilter +import org.dash.wallet.integrations.crowdnode.model.ApiCode +import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants + +class CrowdNodeWithdrawalReceivedTx( + private val networkParams: NetworkParameters +): TransactionFilter { + override fun matches(tx: Transaction): Boolean { + val fromAddress = CrowdNodeConstants.getCrowdNodeAddress(networkParams) + + for (input in tx.inputs) { + input.outpoint.connectedOutput?.let { connectedOutput -> + val script = connectedOutput.scriptPubKey + + if ((ScriptPattern.isP2PKH(script) || ScriptPattern.isP2SH(script)) && + script.getToAddress(networkParams) == fromAddress + ) { + return !tx.outputs.any { isApiResponse(it.value) } + } + } + } + + return false + } + + private fun isApiResponse(coin: Coin): Boolean { + val toCheck = (coin - CrowdNodeConstants.API_OFFSET).value + + return toCheck in 1..1024 || (toCheck <= ApiCode.MaxCode.code && isPowerOfTwo(coin.value)) + } + + private fun isPowerOfTwo(number: Long): Boolean { + return number and number - 1 == 0L + } +} \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/CrowdNodeViewModel.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/CrowdNodeViewModel.kt index 4104107fbf..eb94296c0a 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/CrowdNodeViewModel.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/CrowdNodeViewModel.kt @@ -51,6 +51,7 @@ class CrowdNodeViewModel @Inject constructor( exchangeRatesProvider: ExchangeRatesProvider ) : ViewModel() { val navigationCallback = SingleLiveEvent() + val networkErrorEvent = SingleLiveEvent() private val _accountAddress = MutableLiveData
() val accountAddress: LiveData
@@ -75,6 +76,7 @@ class CrowdNodeViewModel @Inject constructor( var crowdNodeError: LiveData = MediatorLiveData().apply { addSource(crowdNodeApi.apiError.asLiveData(), this::setValue) + value = crowdNodeApi.apiError.value } private val _exchangeRate: MutableLiveData = MutableLiveData() @@ -111,14 +113,17 @@ class CrowdNodeViewModel @Inject constructor( crowdNodeApi.balance .onEach { when (it.status) { - Status.LOADING -> _isBalanceLoading.postValue(true) + Status.LOADING -> { + _isBalanceLoading.postValue(true) + _crowdNodeBalance.postValue(it.data ?: Coin.ZERO) + } Status.SUCCESS -> { _isBalanceLoading.postValue(false) _crowdNodeBalance.postValue(it.data ?: Coin.ZERO) } Status.ERROR -> { _isBalanceLoading.postValue(false) - // TODO: Displaying network error + networkErrorEvent.call() } else -> _isBalanceLoading.postValue(false) } @@ -127,7 +132,6 @@ class CrowdNodeViewModel @Inject constructor( viewModelScope.launch { _accountAddress.value = getOrCreateAccountAddress() - _crowdNodeBalance.value = Coin.valueOf(config.lastBalance.first()) crowdNodeApi.refreshBalance() } } @@ -188,12 +192,11 @@ class CrowdNodeViewModel @Inject constructor( } suspend fun deposit(coin: Coin): Boolean { - return crowdNodeApi.deposit(crowdNodeApi.accountAddress!!, coin) + return crowdNodeApi.deposit(coin) } - fun withdraw(coin: Coin): Boolean { - // TODO: Withdraw - return false + suspend fun withdraw(coin: Coin): Boolean { + return crowdNodeApi.withdraw(coin) } private suspend fun getOrCreateAccountAddress(): Address { diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/ResultFragment.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/ResultFragment.kt index cacea7b2c9..bea68dae36 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/ResultFragment.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/ResultFragment.kt @@ -28,6 +28,8 @@ import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import org.bitcoinj.core.InsufficientMoneyException +import org.bitcoinj.wallet.Wallet.CouldNotAdjustDownwards +import org.bitcoinj.wallet.Wallet.DustySendRequested import org.dash.wallet.common.services.SecurityModel import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.integrations.crowdnode.R @@ -55,17 +57,12 @@ class ResultFragment : Fragment(R.layout.fragment_result) { binding.icon.setImageResource(R.drawable.ic_error_red) binding.title.text = args.title binding.title.setTextAppearance(R.style.Headline5_Bold_Red) - binding.subtitle.text = viewModel.crowdNodeError.value?.message binding.sendReportBtn.isVisible = true binding.negativeBtn.isVisible = true binding.positiveBtn.text = getString(R.string.button_retry) viewModel.crowdNodeError.value?.let { ex -> - if (ex is InsufficientMoneyException || - ex.message?.startsWith(INSUFFICIENT_MONEY_PREFIX) == true - ) { - binding.positiveBtn.isVisible = false - } + setErrorMessage(ex) } binding.sendReportBtn.setOnClickListener { @@ -110,4 +107,18 @@ class ResultFragment : Fragment(R.layout.fragment_result) { } } } + + private fun setErrorMessage(ex: Exception) { + binding.subtitle.text = when (ex) { + is DustySendRequested, is CouldNotAdjustDownwards -> getString(R.string.send_coins_error_dusty_send) + is InsufficientMoneyException -> getString(R.string.send_coins_error_insufficient_money) + else -> ex.message ?: "" + } + + if (ex is InsufficientMoneyException || + ex.message?.startsWith(INSUFFICIENT_MONEY_PREFIX) == true + ) { + binding.positiveBtn.isVisible = false + } + } } \ No newline at end of file diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/PortalFragment.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/PortalFragment.kt index e9b664f076..2d2bc81f03 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/PortalFragment.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/PortalFragment.kt @@ -22,6 +22,7 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint @@ -103,12 +104,21 @@ class PortalFragment : Fragment(R.layout.fragment_portal) { viewModel.crowdNodeError.observe(viewLifecycleOwner) { error -> error?.let { - // TODO: withdraw safeNavigate(PortalFragmentDirections.portalToResult( - true, getString(R.string.crowdnode_deposit_error), "") - ) + true, + getString(R.string.crowdnode_transfer_error), + "" + )) } } + + viewModel.networkErrorEvent.observe(viewLifecycleOwner) { + Toast.makeText( + requireContext(), + R.string.network_unavailable_balance_not_accurate, + Toast.LENGTH_LONG + ).show() + } } private fun updateFiatAmount(balance: Coin?, exchangeRate: ExchangeRate?) { diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/TransferFragment.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/TransferFragment.kt index 6e3d214474..ebc59f215b 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/TransferFragment.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/ui/portal/TransferFragment.kt @@ -17,6 +17,7 @@ package org.dash.wallet.integrations.crowdnode.ui.portal +import android.animation.ObjectAnimator import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment @@ -39,8 +40,11 @@ import org.dash.wallet.common.util.GenericUtils import org.dash.wallet.common.util.safeNavigate import org.dash.wallet.integrations.crowdnode.R import org.dash.wallet.integrations.crowdnode.databinding.FragmentTransferBinding -import org.dash.wallet.integrations.crowdnode.databinding.ViewKeyboardHeaderBinding +import org.dash.wallet.integrations.crowdnode.databinding.ViewKeyboardDepositHeaderBinding +import org.dash.wallet.integrations.crowdnode.databinding.ViewKeyboardWithdrawHeaderBinding +import org.dash.wallet.integrations.crowdnode.model.ApiCode import org.dash.wallet.integrations.crowdnode.ui.CrowdNodeViewModel +import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants import javax.inject.Inject @AndroidEntryPoint @@ -50,6 +54,7 @@ class TransferFragment : Fragment(R.layout.fragment_transfer) { private val args by navArgs() private val viewModel by activityViewModels() private val amountViewModel by activityViewModels() + private var balanceAnimator: ObjectAnimator? = null @Inject lateinit var securityModel: SecurityModel @@ -59,15 +64,24 @@ class TransferFragment : Fragment(R.layout.fragment_transfer) { if (savedInstanceState == null) { val fragment = EnterAmountFragment.newInstance( + dashToFiat = amountViewModel.dashToFiatDirection.value ?: false, isMaxButtonVisible = true, showCurrencySelector = true ) - val headerBinding = ViewKeyboardHeaderBinding.inflate(layoutInflater, null, false) - fragment.setViewDetails(if (args.withdraw) { + + val headerBinding = if (args.withdraw) { + ViewKeyboardWithdrawHeaderBinding.inflate(layoutInflater, null, false) + } else { + ViewKeyboardDepositHeaderBinding.inflate(layoutInflater, null, false) + } + + val buttonText = if (args.withdraw) { getString(R.string.withdraw) } else { getString(R.string.deposit) - }, headerBinding.root) + } + + fragment.setViewDetails(buttonText, headerBinding.root) parentFragmentManager.commit { setReorderingAllowed(true) @@ -75,23 +89,59 @@ class TransferFragment : Fragment(R.layout.fragment_transfer) { } } + if (args.withdraw) { + binding.toolbarTitle.text = getString(R.string.withdraw) + binding.sourceIcon.setImageResource(R.drawable.ic_crowdnode_logo) + binding.sourceLabel.text = getString(R.string.from_crowdnode) + } else { + binding.toolbarTitle.text = getString(R.string.deposit) + binding.sourceIcon.setImageResource(R.drawable.ic_dash_pay) + binding.sourceLabel.text = getString(R.string.from_wallet) + } + binding.toolbar.setNavigationOnClickListener { findNavController().popBackStack() } - viewModel.dashBalance.observe(viewLifecycleOwner) { balance -> - amountViewModel.setMaxAmount(balance) - val dashToFiat = amountViewModel.dashToFiatDirection.value == true - val rate = amountViewModel.selectedExchangeRate.value - setAvailableBalance(balance, rate, dashToFiat) + if (args.withdraw) { + viewModel.crowdNodeBalance.observe(viewLifecycleOwner) { + updateAvailableBalance() + } + + this.balanceAnimator = ObjectAnimator.ofFloat( + binding.balanceText, + View.ALPHA.name, + 0f, 0.5f + ).apply { + duration = 500 + repeatCount = ObjectAnimator.INFINITE + repeatMode = ObjectAnimator.REVERSE + } + + viewModel.isBalanceLoading.observe(viewLifecycleOwner) { isLoading -> + if (isLoading) { + this.balanceAnimator?.start() + } else { + this.balanceAnimator?.end() + } + } + } else { + viewModel.dashBalance.observe(viewLifecycleOwner) { + updateAvailableBalance() + } } viewModel.crowdNodeError.observe(viewLifecycleOwner) { error -> error?.let { - // TODO: withdraw safeNavigate(TransferFragmentDirections.transferToResult( - true, getString(R.string.crowdnode_deposit_error), "") - ) + true, + getString(if (args.withdraw) { + R.string.crowdnode_withdraw_error + } else { + R.string.crowdnode_deposit_error + }), + "" + )) } } @@ -103,21 +153,19 @@ class TransferFragment : Fragment(R.layout.fragment_transfer) { ) } - amountViewModel.dashToFiatDirection.observe(viewLifecycleOwner) { dashToFiat -> - val balance = viewModel.dashBalance.value ?: Coin.ZERO - val rate = amountViewModel.selectedExchangeRate.value - setAvailableBalance(balance, rate, dashToFiat) + amountViewModel.dashToFiatDirection.observe(viewLifecycleOwner) { + updateAvailableBalance() } - amountViewModel.selectedExchangeRate.observe(viewLifecycleOwner) { rate -> - val balance = viewModel.dashBalance.value ?: Coin.ZERO - val dashToFiat = amountViewModel.dashToFiatDirection.value == true - setAvailableBalance(balance, rate, dashToFiat) + amountViewModel.selectedExchangeRate.observe(viewLifecycleOwner) { + updateAvailableBalance() } amountViewModel.amount.observe(viewLifecycleOwner) { amount -> - binding.availableDashText.setTextAppearance( - if (amount > viewModel.dashBalance.value ?: Coin.ZERO) { + val maxValue = if (args.withdraw) viewModel.crowdNodeBalance else viewModel.dashBalance + + binding.balanceText.setTextAppearance( + if (amount > maxValue.value ?: Coin.ZERO) { R.style.Caption_Red } else { R.style.Caption_SteelGray @@ -133,31 +181,61 @@ class TransferFragment : Fragment(R.layout.fragment_transfer) { } private suspend fun continueTransfer(value: Coin) { - securityModel.requestPinCode(requireActivity()) ?: return + if (!args.withdraw) { + securityModel.requestPinCode(requireActivity()) ?: return + } - var result = false - AdaptiveDialog.withProgress(getString(R.string.please_wait_title), requireActivity()) { - result = if (args.withdraw) { + val isSuccess = AdaptiveDialog.withProgress(getString(R.string.please_wait_title), requireActivity()) { + if (args.withdraw) { viewModel.withdraw(value) } else { viewModel.deposit(value) } } - if (result) { - // TODO: withdraw - safeNavigate(TransferFragmentDirections.transferToResult( - false, - getString(R.string.deposit_sent), - getString(R.string.deposit_sent_message) - )) + + if (isSuccess) { + if (args.withdraw) { + safeNavigate(TransferFragmentDirections.transferToResult( + false, + getString(R.string.withdrawal_requested), + getString(R.string.withdrawal_requested_message) + )) + } else { + safeNavigate(TransferFragmentDirections.transferToResult( + false, + getString(R.string.deposit_sent), + getString(R.string.deposit_sent_message) + )) + } + } + } + + private fun updateAvailableBalance() { + val balance = if (args.withdraw) { + viewModel.crowdNodeBalance.value + } else { + viewModel.dashBalance.value + } ?: Coin.ZERO + + val minValue = if (args.withdraw) { + balance.div(ApiCode.WithdrawAll.code) + } else { + CrowdNodeConstants.API_OFFSET + Coin.valueOf(ApiCode.MaxCode.code) } + + amountViewModel.setMinAmount(minValue) + amountViewModel.setMaxAmount(balance) + + val dashToFiat = amountViewModel.dashToFiatDirection.value == true + val rate = amountViewModel.selectedExchangeRate.value + setAvailableBalanceText(balance, rate, dashToFiat) } - private fun setAvailableBalance(balance: Coin, exchangeRate: ExchangeRate?, dashToFiat: Boolean) { + private fun setAvailableBalanceText(balance: Coin, exchangeRate: ExchangeRate?, dashToFiat: Boolean) { val rate = exchangeRate?.let { org.bitcoinj.utils.ExchangeRate(Coin.COIN, it.fiat) } - binding.availableDashText.text = when { + binding.balanceText.text = when { dashToFiat -> getString(R.string.available_balance, balance.toFriendlyString()) rate != null -> getString(R.string.available_balance, GenericUtils.fiatToString(rate.coinToFiat(balance))) diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/CrowdNodeConstants.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/CrowdNodeConstants.kt index aa58537fdb..18f338bcff 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/CrowdNodeConstants.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/CrowdNodeConstants.kt @@ -28,7 +28,7 @@ object CrowdNodeConstants { val MINIMUM_REQUIRED_DASH: Coin = Coin.valueOf(1000000) val REQUIRED_FOR_SIGNUP: Coin = MINIMUM_REQUIRED_DASH - Coin.valueOf(100000) - val CROWDNODE_OFFSET: Coin = Coin.valueOf(20000) + val API_OFFSET: Coin = Coin.valueOf(20000) fun getCrowdNodeAddress(params: NetworkParameters): Address { return Address.fromBase58(params, if (params == MainNetParams.get()) { diff --git a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/ModuleConfiguration.kt b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/ModuleConfiguration.kt index fa09485475..90bb0b1d1b 100644 --- a/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/ModuleConfiguration.kt +++ b/integrations/crowdnode/src/main/java/org/dash/wallet/integrations/crowdnode/utils/ModuleConfiguration.kt @@ -18,6 +18,7 @@ package org.dash.wallet.integrations.crowdnode.utils import android.content.Context +import androidx.datastore.dataStoreFile import androidx.datastore.preferences.core.* import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow @@ -93,4 +94,8 @@ class ModuleConfiguration @Inject constructor(private val context: Context) { preferences[INFO_SHOWN_KEY] = isShown } } + + suspend fun clearAll() { + context.dataStore.edit { it.clear() } + } } \ No newline at end of file diff --git a/integrations/crowdnode/src/main/res/layout/fragment_transfer.xml b/integrations/crowdnode/src/main/res/layout/fragment_transfer.xml index 59102db799..1d06eb61e9 100644 --- a/integrations/crowdnode/src/main/res/layout/fragment_transfer.xml +++ b/integrations/crowdnode/src/main/res/layout/fragment_transfer.xml @@ -27,9 +27,9 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:background="@android:color/white" app:navigationIcon="@drawable/ic_back_arrow" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" - android:background="@android:color/white" app:layout_constraintTop_toTopOf="parent"> \ No newline at end of file diff --git a/integrations/crowdnode/src/main/res/layout/view_keyboard_deposit_header.xml b/integrations/crowdnode/src/main/res/layout/view_keyboard_deposit_header.xml new file mode 100644 index 0000000000..90f4632f27 --- /dev/null +++ b/integrations/crowdnode/src/main/res/layout/view_keyboard_deposit_header.xml @@ -0,0 +1,40 @@ + + + + + + + + \ No newline at end of file diff --git a/integrations/crowdnode/src/main/res/layout/view_keyboard_withdraw_header.xml b/integrations/crowdnode/src/main/res/layout/view_keyboard_withdraw_header.xml new file mode 100644 index 0000000000..b26ef2102a --- /dev/null +++ b/integrations/crowdnode/src/main/res/layout/view_keyboard_withdraw_header.xml @@ -0,0 +1,39 @@ + + + + + + + \ No newline at end of file diff --git a/integrations/crowdnode/src/main/res/navigation/nav_crowdnode.xml b/integrations/crowdnode/src/main/res/navigation/nav_crowdnode.xml index 27ae49418d..c5c250bde7 100644 --- a/integrations/crowdnode/src/main/res/navigation/nav_crowdnode.xml +++ b/integrations/crowdnode/src/main/res/navigation/nav_crowdnode.xml @@ -21,7 +21,7 @@ + android:label="EntryPointFragment"> Accepting terms of use… We couldn’t create your CrowdNode account. We couldn’t make a deposit to your CrowdNode account. + We couldn’t withdraw from your CrowdNode account. + Your CrowdNode transfer was unsuccessful. + Your deposit to CrowdNode is received. Deposit Deposit sent It can take a minute for your balance to be updated. From Dash Wallet to CrowdNode From Dash Wallet + From CrowdNode Withdraw + Withdrawal requested + It can take a minute for your funds to arrive. From CrowdNode to Dash Wallet Online Account Make it as secure as possible @@ -75,6 +81,7 @@ Get Rewards Instantly Receive your share of rewards daily. Sending to CrowdNode account + Sending to Dash Wallet on this device Available: %s https://crowdnode.io/ diff --git a/wallet/res/values-ar/strings.xml b/wallet/res/values-ar/strings.xml index 596e15f774..d3a44cd820 100644 --- a/wallet/res/values-ar/strings.xml +++ b/wallet/res/values-ar/strings.xml @@ -65,8 +65,8 @@ أدخل الرمز السري سيتم دفع رسوم شبكة قدرها %s. سيتم دفع رسم أولوية بنسبة%s إذا كنت تهتم برسوم منخفضة، tاستخدم \"الأولوية\" فقط إذا كنت بحاجة إلى تأكيد في أقرب وقت ممكن. - المبلغ صغير جدا بحيث لا يمكن إرساله. - رصيد غير كاف + المبلغ صغير جدا بحيث لا يمكن إرساله. + رصيد غير كاف لا يضيف مبلغ المدفوعات الصغيرة في محفظتك قيمة قابلة للإرسال. لا يمكن الدفع حاليًا لأن المحفظة غير متزامنة بالكامل مع الشبكة إرسال الدفعة مباشرة إلى المستفيد. diff --git a/wallet/res/values-bg/strings.xml b/wallet/res/values-bg/strings.xml index 78d3756ef1..d1dd01cc79 100644 --- a/wallet/res/values-bg/strings.xml +++ b/wallet/res/values-bg/strings.xml @@ -64,8 +64,8 @@ Въведи PIN Мрежова такса от %s ще бъде платена. Приоритетна такса от %s ще бъде платена. Ако се интересувате от ниски такси, използвайте \'приоритетна\' само ако се нуждаете от бързо потвърждение. - Тази сума е прекалено малка за да бъде изпратена. - Недостатъчно средства + Тази сума е прекалено малка за да бъде изпратена. + Недостатъчно средства Сумата на малките плащания във вашият портфейл не може да се добави към стойността за изпращане. В момента плащанията не са възможни, защото портфейлът не е напълно синхронизиран с мрежата Изпратете плащането директно до получателя. diff --git a/wallet/res/values-ca/strings.xml b/wallet/res/values-ca/strings.xml index 30b0086e6b..7e119e9a6d 100644 --- a/wallet/res/values-ca/strings.xml +++ b/wallet/res/values-ca/strings.xml @@ -61,8 +61,8 @@ complex\nadreça (%s esperant confirmació) Total a pagar - La quantitat per enviar és massa petita. - No hi ha prou monedes. Et falten %s. + La quantitat per enviar és massa petita. + No hi ha prou monedes. Et falten %s. L\'import dels petits pagaments de la teva cartera no es sumen a la quantitat que es pot enviar. Enviar el pagament directament al beneficiari. El teu pagament directe s\'ha enviat correctament. diff --git a/wallet/res/values-cs/strings.xml b/wallet/res/values-cs/strings.xml index fc328c9dff..0284c3ed11 100644 --- a/wallet/res/values-cs/strings.xml +++ b/wallet/res/values-cs/strings.xml @@ -65,8 +65,8 @@ Zadejte PIN Bude uhrazen poplatek síti ve výši %s. Bude uhrazen prioritní poplatek %s. Pokud vám záleží na nízkých poplatcích, použijte ‚prioritu‘, pouze pokud potřebujete konfirmaci co nejdříve. - Částka je příliš nízká pro odeslání - Nedostatečný zůstatek + Částka je příliš nízká pro odeslání + Nedostatečný zůstatek Částka drobných plateb ve vaší peněžence nepřesahuje hodnotu, kterou lze odeslat. Platby momentálně nelze provádět, protože peněženka není synchronizována se sítí. Poslat platbu přímo příjemci. diff --git a/wallet/res/values-da/strings.xml b/wallet/res/values-da/strings.xml index c98cd96b43..5c7920036a 100644 --- a/wallet/res/values-da/strings.xml +++ b/wallet/res/values-da/strings.xml @@ -61,8 +61,8 @@ kompleks\nadresse (%s venter på bekræftelse) Beløb at betale - Beløbet er for lille til at blive sendt. - Ikke nok tilgængelige Dash. Du mangler %s. + Beløbet er for lille til at blive sendt. + Ikke nok tilgængelige Dash. Du mangler %s. Det samlede beløb af bittesmå betalinger i din tegnebog er ikke nok til at kunne sendes. Send betaling direkte til modtageren. Din betaling blev sendt direkte med succes. diff --git a/wallet/res/values-de/strings.xml b/wallet/res/values-de/strings.xml index dff805f42c..f8d839c72a 100644 --- a/wallet/res/values-de/strings.xml +++ b/wallet/res/values-de/strings.xml @@ -66,8 +66,8 @@ PIN eingeben Eine Netzwerk-Gebühr von %szahlen. Eine Vorzugs-Gebühr von %s zahlen. Wenn Dir eine geringe Gebühr wichtig ist, verwende diese Einstellung nur, wenn Du sehr schnell eine Bestätigung brauchst. - Der Betrag ist zu klein zum Senden - Guthaben nicht ausreichend + Der Betrag ist zu klein zum Senden + Guthaben nicht ausreichend Die Summe an winzigen Zahlungen in Deiner Wallet ist zu klein, um verschickt zu werden. Da die Wallet momentan nicht mit dem Netzwerk synchronisiert ist, kann keine Zahlung durchgeführt werden. Zahlung direkt an den Empfänger senden. diff --git a/wallet/res/values-el/strings.xml b/wallet/res/values-el/strings.xml index 3b8b72525e..0b9f319259 100644 --- a/wallet/res/values-el/strings.xml +++ b/wallet/res/values-el/strings.xml @@ -65,8 +65,8 @@ Εισάγετε PIN Θα πληρώσετε %s για το δίκτυο. Θα πληρώσετε τέλη προτεραιότητας %s. Αν σας ενδιαφέρουν χαμηλά τέλη, χρησιμοποιήστε την επιλογή "προτεραιότητα" μόνο αν χρειάζεστε επιβεβαίωση το συντομότερο δυνατόν. - Το ποσό είναι πολύ μικρό για να σταλθεί - Ανεπαρκή χρήματα + Το ποσό είναι πολύ μικρό για να σταλθεί + Ανεπαρκή χρήματα Το ποσό των μικρών πληρωμών στο πορτοφόλι σας δεν επαρκεί για το ποσό που είναι προς αποστολή. Αυτή την στιγμή δεν διεκπεραιώνονται πληρωμές γιατί το πορτοφόλι δεν έχει συγχρονιστεί πλήρως με το δίκτυο Στείλτε πληρωμή απευθείας στον δικαιούχο διακομιστή. diff --git a/wallet/res/values-es/strings.xml b/wallet/res/values-es/strings.xml index e1f4dad565..570dcfe2fe 100644 --- a/wallet/res/values-es/strings.xml +++ b/wallet/res/values-es/strings.xml @@ -65,8 +65,8 @@ Ingresar PIN Una comisión de red de %s será pagada. Una comisión prioritaria de %s será pagada. Si te importan las comisiones bajas, usa "Prioritaria" solo si necesitas confirmación lo más pronto posible. - La cantidad es demasiado pequeña para ser enviada - Fondos insuficientes + La cantidad es demasiado pequeña para ser enviada + Fondos insuficientes La cantidad de pequeños pagos en tu billetera no es suficiente para poder enviarlos. Actualmente, pagos no son posibles porque la billetera no está completamente sincronizada con la red Enviar pago directamente al receptor. diff --git a/wallet/res/values-fa/strings.xml b/wallet/res/values-fa/strings.xml index a4929512aa..e0b8539c84 100644 --- a/wallet/res/values-fa/strings.xml +++ b/wallet/res/values-fa/strings.xml @@ -66,8 +66,8 @@ رمز را وارد کنید کارمزد شبکه معادل با %s پرداخت خواهد شد. کارمزد اولویت معادل با %s پرداخت خواهد شد. اگر به دنبال کارمزد پایین هستید، از گزینه «اولویت» فقط در مواردی که نیازمند تایید سریع تراکنش از سوی شبکه هستید استفاده کنید. - مبلغ مورد نظر، بیش از حد کم است - موجودی ناکافی + مبلغ مورد نظر، بیش از حد کم است + موجودی ناکافی پرداخت‌های جزیی در کیف پول‌تان روی هم رفته آن قدر نیست که بتواند به یک میزان قابل ارسال برسد. پرداخت‌های کنونی امکان‌پذیر نیست، زیرا کیف پول به خوبی با شبکه همزمان نشده است مبلغ را به صورت مستقیم برای دریافت‌کننده ارسال کنید. diff --git a/wallet/res/values-fi/strings.xml b/wallet/res/values-fi/strings.xml index 7ec9a5fcb5..58895570ba 100644 --- a/wallet/res/values-fi/strings.xml +++ b/wallet/res/values-fi/strings.xml @@ -61,8 +61,8 @@ monimutkainen\nosoite (%s odottaa vahvistusta) Maksettava määrä - Määrä lähettämistä varten on liian pieni. - Käytettäviä varoja ei ole tarpeeksi. Sinulta puuttuu %s. + Määrä lähettämistä varten on liian pieni. + Käytettäviä varoja ei ole tarpeeksi. Sinulta puuttuu %s. Pieniä maksuja lompakossasi ei yhteenlasketa lähetyskelpoiseen arvoon. Lähetä maksu suoraan saajalle. Maksu lähetettiin onnistuneesti suoraan. diff --git a/wallet/res/values-fil/strings.xml b/wallet/res/values-fil/strings.xml index 6cd7a29ae5..fb48764cb9 100644 --- a/wallet/res/values-fil/strings.xml +++ b/wallet/res/values-fil/strings.xml @@ -65,8 +65,8 @@ I-enter ang PIN Ang network fee na %s ay mababayaran na. Ang priority fee na %say mababayaran. Kung alintana ang mababang bayad, gamitin lamang ang \'priority\' kung kailangan ng kumpirmasyon sa lalong madaling panahon. - Ang halaga ay sobrang liit para maipadala - Hindi sapat na pondo + Ang halaga ay sobrang liit para maipadala + Hindi sapat na pondo Ang halaga ng maliit na bayad sa iyong pitaka ay hindi dumagdag sa ipinadalang halaga. Ang kasalukuyang bayad ay hindi posible dahil ang pitaka ay hindi sync sa network Magbayad ng direkta sa binabayaran. diff --git a/wallet/res/values-fr/strings.xml b/wallet/res/values-fr/strings.xml index b6122a2dc7..6227297439 100644 --- a/wallet/res/values-fr/strings.xml +++ b/wallet/res/values-fr/strings.xml @@ -65,8 +65,8 @@ Saisissez le code PIN Des frais de réseau de %s seront appliqués. Des frais prioritaires de %s seront appliqués. Si vous préférez des frais plus bas, n\'utilisez l\'option "prioritaire" que s\'il vous faut une confirmation la plus rapide possible. - Le montant est trop faible pour être envoyé - Fonds insuffisants + Le montant est trop faible pour être envoyé + Fonds insuffisants Le total des petits montants dans votre portefeuille ne forme pas une somme pouvant être envoyée. Les paiements ne sont pas encore possibles car le portefeuille n\'est pas entièrement synchronisé avec le réseau Envoyer le paiement directement au bénéficiaire. diff --git a/wallet/res/values-hu/strings.xml b/wallet/res/values-hu/strings.xml index c562ddd592..5bb13ea03b 100644 --- a/wallet/res/values-hu/strings.xml +++ b/wallet/res/values-hu/strings.xml @@ -61,8 +61,8 @@ egész\ncím (%s megerősítésre vár) Fizetendő összeg - Az összeg túl kicsi a küldéshez. - Nincs elég elérhető érme. Neked %s hiányzik. + Az összeg túl kicsi a küldéshez. + Nincs elég elérhető érme. Neked %s hiányzik. A tárcádban lévő apró fizetségek összege nem tesz ki egy küldhető értéket. Fizetség küldése közvetlenül a kedvezményezettnek. A fizetésed sikeresen elküldve közvetlenül. diff --git a/wallet/res/values-id/strings.xml b/wallet/res/values-id/strings.xml index 4b6aa29fc4..a7556657b2 100644 --- a/wallet/res/values-id/strings.xml +++ b/wallet/res/values-id/strings.xml @@ -65,8 +65,8 @@ Masukkan pin Biaya jaringan %s akan dibayarkan. Biaya prioritas %s akan dibayarkan. Jika Anda peduli dengan biaya rendah, gunakan \'prioritas\' hanya jika Anda perlu konfirmasi sesegera mungkin. - Jumlah yang terlalu kecil untuk dikirim - Dana tidak mencukupi + Jumlah yang terlalu kecil untuk dikirim + Dana tidak mencukupi Jumlah pembayaran kecil di dompet Anda tidak menambah nilai yang dapat dikirim. Saat ini pembayaran tidak dimungkinkan karena dompet tidak sepenuhnya disinkronkan dengan jaringan Kirim pembayaran langsung ke penerima pembayaran. diff --git a/wallet/res/values-it/strings.xml b/wallet/res/values-it/strings.xml index bf07b74a32..8c7933abba 100644 --- a/wallet/res/values-it/strings.xml +++ b/wallet/res/values-it/strings.xml @@ -65,8 +65,8 @@ Immetti il PIN Sarà pagata una piccola commissione di rete di %s. Una tassa di priorità %s sara pagata. Usa \'priorità\' solo se hai bisogno di una conferma il prima possibile. - L\'importo è troppo piccolo per l\'invio - Fondi insufficenti + L\'importo è troppo piccolo per l\'invio + Fondi insufficenti La quantità di piccoli pagamenti nel vostro portamonete non raggiunge valori inviabili. Attualmente i pagamenti non sono possibili perché il portafoglio non è completamente sincronizzato con la rete Invia il pagamento al beneficiario diff --git a/wallet/res/values-ja/strings.xml b/wallet/res/values-ja/strings.xml index eb1d64e319..468b0f5833 100644 --- a/wallet/res/values-ja/strings.xml +++ b/wallet/res/values-ja/strings.xml @@ -65,8 +65,8 @@ PINを入力 ネットワーク手数料 %s が支払われます。 優先手数料%s が支払われます。できるだけ早い承認が必要な場合のみ「優先」をお使いください。 - 送金額が小さすぎます - 資金不足 + 送金額が小さすぎます + 資金不足 ウォレット中の少額の支払いを合計しても送信可能な額には足りませんでした。 ウォレットがネットワークと完全に同期されていないため、現在支払いはできません 受取人へ直接送金する。 diff --git a/wallet/res/values-ko/strings.xml b/wallet/res/values-ko/strings.xml index 38c4295ebc..0840cb013e 100644 --- a/wallet/res/values-ko/strings.xml +++ b/wallet/res/values-ko/strings.xml @@ -65,8 +65,8 @@ PIN 입력하기 네트워크 승인을 위해 %s가 지불됩니다. %s의 선행 수수료가 지불됩니다. 네트워크에서 최대한 빠른 확인이 필요하시다면 \'선행\'을 사용하세요. - 송금액이 너무 적어 전송할 수 없습니다 - 잔액 부족 + 송금액이 너무 적어 전송할 수 없습니다 + 잔액 부족 극소량의 금액은 송금 가능액에 포함되지 않습니다. 지갑이 네트워크와 완전히 동기화되지 않아 현재 지불 할 수 없습니다 대시가 수신인에게 바로 송금됩니다. diff --git a/wallet/res/values-nb/strings.xml b/wallet/res/values-nb/strings.xml index 538fd32a2a..aca4b3c239 100644 --- a/wallet/res/values-nb/strings.xml +++ b/wallet/res/values-nb/strings.xml @@ -61,8 +61,8 @@ kompleks\nadresse (%s venter på bekreftelse) Beløp å betale - Beløpet er for lite til å sende. - Ikke nok tilgjengelige mynter. Du mangler %s. + Beløpet er for lite til å sende. + Ikke nok tilgjengelige mynter. Du mangler %s. Summen av små betalinger i lommeboken er ikke en verdi stor nok til å sendes. Send betaling direkte til mottakeren. Din betaling ble sendt direkte med suksess. diff --git a/wallet/res/values-nl/strings.xml b/wallet/res/values-nl/strings.xml index 884d137c10..e05208bc70 100644 --- a/wallet/res/values-nl/strings.xml +++ b/wallet/res/values-nl/strings.xml @@ -65,8 +65,8 @@ Voer pin in Netwerkkosten %s worden betaald. Een prioriteitstoeslag van %s wordt betaald. Als je een lage toeslag belangrijk vindt, gebruik \'prioriteit\' alleen als je zo spoedig mogelijk bevestiging nodig hebt. - bedrag is te klein om te versturen - Ontoereikende fondsen + bedrag is te klein om te versturen + Ontoereikende fondsen Het saldo van kleine betalingen in je Dashportemonnee is bij elkaar niet hoog genoeg om te kunnen versturen. Momenteel zijn betalingen niet mogelijk omdat de portemonnee niet volledig is gesynchroniseerd met het netwerk Betaling direct versturen aan de begunstigde. diff --git a/wallet/res/values-pl/strings.xml b/wallet/res/values-pl/strings.xml index 618851cbda..8482d79559 100644 --- a/wallet/res/values-pl/strings.xml +++ b/wallet/res/values-pl/strings.xml @@ -65,8 +65,8 @@ Wpisz PIN Opłata sieci %s zostanie pobrana. zostanie pobrana opłata priorytetowa %s. Jeśli zależy ci na niskich opłatach, używaj \'opłaty priorytetowej\', jedynie gdy potrzebujesz szybkiego potwierdzenia transakcji. - Kwota jest zbyt mała, aby wysłać - Niewystarczająca ilość funduszy + Kwota jest zbyt mała, aby wysłać + Niewystarczająca ilość funduszy Ilość małych płatności w twoim portfelu nie dodaje się do wartości możliwej do wysyłki. Obecnie, płatności nie są możliwe, ponieważ portfel nie jest w pełni zsynchronizowany z siecią. Wyślij płatność bezpośrednio do odbiorcy. diff --git a/wallet/res/values-pt/strings.xml b/wallet/res/values-pt/strings.xml index 9a2cbd6f08..199443e27e 100644 --- a/wallet/res/values-pt/strings.xml +++ b/wallet/res/values-pt/strings.xml @@ -65,8 +65,8 @@ Inserir PIN Uma taxa de rede de %s será paga. Uma taxa para a prioridade de%s será paga. Se você se preocupa com baixas taxas, use "prioridade" apenas se você precisar de confirmação mais rapido possível. - O valor é muito baixo para ser enviado - Fundos insuficientes + O valor é muito baixo para ser enviado + Fundos insuficientes A quantidade de pequenos pagamentos em sua carteira não somam um valor para envio. Pagamentos não são possíveis no momento porque a carteira não está totalmente sincronizada com a rede Enviar pagamento diretamente ao destinatário. diff --git a/wallet/res/values-ro/strings.xml b/wallet/res/values-ro/strings.xml index 6c6af3b416..a98c7deff7 100644 --- a/wallet/res/values-ro/strings.xml +++ b/wallet/res/values-ro/strings.xml @@ -58,7 +58,7 @@ Introdu codul PIN O taxă de rețea de %s va fi plătită. O taxă de prioritate de %s va fi plătită. Dacă vrei taxe reduse, utilizează "prioritate" numai dacă ai nevoie de confirmare cât mai curând posibil. - Fonduri nesuficiente + Fonduri nesuficiente Suma plăților minuscule din portofel tău nu reprezintă o valoare care poate fi trimisă. Trimite plata direct beneficiarului. Plata ta directă a fost trimisă cu succes. diff --git a/wallet/res/values-ru/strings.xml b/wallet/res/values-ru/strings.xml index c33a95c7c7..5c32f7d35e 100644 --- a/wallet/res/values-ru/strings.xml +++ b/wallet/res/values-ru/strings.xml @@ -65,8 +65,8 @@ Введите PIN-код Будет заплачена комиссия %s. Приоритетный размер платы составит %s. Если размер комиссии имеет для Вас значение, используйте опцию приоритета, только когда Вам необходимо срочное подтверждение транзакции. - Сумма слишком мала для отправки - Недостаточно средств + Сумма слишком мала для отправки + Недостаточно средств Количества малых платежей в Вашем кошельке недостаточно для отправки средств. В данный момент платежи невозможны, так как кошелёк не полностью синхронизирован с сетью. Отправить платёж непосредственно получателю. diff --git a/wallet/res/values-sk/strings.xml b/wallet/res/values-sk/strings.xml index f90aa6454d..265a2a3a49 100644 --- a/wallet/res/values-sk/strings.xml +++ b/wallet/res/values-sk/strings.xml @@ -65,8 +65,8 @@ Zadať PIN Bude zaplatený sieťový poplatok %s. Bude zaplatený prioritný poplatok %s. Ak Vám záleží na nízkych poplatkoch, používajte "prioritu" iba ak potrebujete rýchle potvrdenie. - Čiastka je príliš nízka pre odoslanie - Nedostatok prostriedkov + Čiastka je príliš nízka pre odoslanie + Nedostatok prostriedkov Súčet malých platieb vo vašej peňaženke neumožňuje v súčte zaslanie platby. Platby nie sú momentálne možne, pretože peňaženka nie je plne synchronizovaná so sieťou Odoslať platbu priamo príjemcovi. diff --git a/wallet/res/values-sq/strings.xml b/wallet/res/values-sq/strings.xml index c3b62554ed..996135d64e 100644 --- a/wallet/res/values-sq/strings.xml +++ b/wallet/res/values-sq/strings.xml @@ -61,8 +61,8 @@ kompleks\nadresa (%s pret konfirmimin) Shuma për të paguar - Shuma është tepër e vogël për tu dërguar. - Nuk keni monedha të mjaftueshme. Jeni %s monedha mangu. + Shuma është tepër e vogël për tu dërguar. + Nuk keni monedha të mjaftueshme. Jeni %s monedha mangu. Shuma e vogël e pagesave te vogla në kuletën tuaj nuk është e mjaftueshme të jetë e shpenzueshme. Dërgoni pagesën drejtpërdrejt tek pranuesi. Pagesa juaj e drejtpërdrejt është zbatuar me sukses. diff --git a/wallet/res/values-sr/strings.xml b/wallet/res/values-sr/strings.xml index 1d183bd11e..59a559b2a4 100644 --- a/wallet/res/values-sr/strings.xml +++ b/wallet/res/values-sr/strings.xml @@ -58,7 +58,7 @@ Unesite PIN Mrežna provizija od %s će biti plaćena. Prioritetna provizija od %s će biti plaćena. Ako vam je bitna mala provizija koristite "prioritet" samo ukoliko vam je bitno da dobijete potvrdu što pre. - Iznos je suviše mali da bi bio poslat + Iznos je suviše mali da bi bio poslat Iznos zbira malih uplata u vašem novčaniku ne odgovara vrednosti koja se može poslati. Pošalji uplatu primaocu direktno. Direktno slanje vaše uplate je uspešno izvršeno. diff --git a/wallet/res/values-sv/strings.xml b/wallet/res/values-sv/strings.xml index 6cfcb26de6..5fed061d28 100644 --- a/wallet/res/values-sv/strings.xml +++ b/wallet/res/values-sv/strings.xml @@ -61,8 +61,8 @@ Komplex\nadress (%s väntar på bekräftelse) Belopp att skicka - Summan är för låg för att kunna skickas. - Du saknar tillräckliga medel. Det fattas %s + Summan är för låg för att kunna skickas. + Du saknar tillräckliga medel. Det fattas %s Det sammanlagda värdet av betalningarna i din wallet räcker inte för att kunna skickas. Skicka betalning direkt till betalningsmottagaren. Din betalning har skickats direkt. diff --git a/wallet/res/values-sw/strings.xml b/wallet/res/values-sw/strings.xml index 313cd8f6df..e816e8b6f7 100644 --- a/wallet/res/values-sw/strings.xml +++ b/wallet/res/values-sw/strings.xml @@ -58,8 +58,8 @@ complex\nanwali (%s kusubiri kwa ajili ya uthibitisho) Kiasi kulipa - Kiasi ni ndogo mno kutuma. - Haitoshi mizani. Wewe ni kukosa %s. + Kiasi ni ndogo mno kutuma. + Haitoshi mizani. Wewe ni kukosa %s. Malipo kidogo katika Pochi yako si kuongeza hadi thamani ya kutosha. Lipa moja kwa moja na mpokeaji. Wewe mafanikio kulipwa moja kwa moja kwa mpokeaji. diff --git a/wallet/res/values-th/strings.xml b/wallet/res/values-th/strings.xml index b655cacb9f..06f6e098fb 100644 --- a/wallet/res/values-th/strings.xml +++ b/wallet/res/values-th/strings.xml @@ -65,8 +65,8 @@ ใส่รหัส PIN ค่าธรรมเนียมระบบเครือข่ายของ %s จะถูกจ่าย ค่าธรรมเนียมพิเศษของ %s จะถูกจ่าย หากคุณสนใจค่าธรรมเนียมพิเศษนี้ โปรดใช้ \'priority\' เมื่อต้องการการยืนยันอย่างรวดเร็ว - จำนวนเงินน้อยเกินไปที่จะส่ง - ยอดเงินไม่เพียงพอ + จำนวนเงินน้อยเกินไปที่จะส่ง + ยอดเงินไม่เพียงพอ จำนวนเงินในการชำระเงิน wallet ของคุณ ไม่เพิ่มขึ้นที่จะสามารถส่งได้ ขณะนี้ยังไม่สามารถทำการชำระเงินได้เนื่องกระเป๋าสตางค์ยังไม่เชื่อมต่อกับเครือข่าย ส่งการชำระเงินโดยตรงไปยังผู้รับเงิน diff --git a/wallet/res/values-tr/strings.xml b/wallet/res/values-tr/strings.xml index 04accb6aa6..3316b53279 100644 --- a/wallet/res/values-tr/strings.xml +++ b/wallet/res/values-tr/strings.xml @@ -65,8 +65,8 @@ PIN Girin %s tutarında bir ağ ücreti ödenecektir. %s tutarında bir öncelik ücreti ödenecektir. Eğer düşük ücretler sizin için önemliyse, \'öncelikli\' seçeneğini sadece mümkün olduğunca hızlı bir şekilde onaya ihtiyacınız olduğu durumlarda kullanın. - Bu miktar göndermek için çok düşüktür - Yetersiz bakiye + Bu miktar göndermek için çok düşüktür + Yetersiz bakiye Cüzdanınıza gelen küçük ödemelerin toplamı göndermek için yetersiz kalıyor. Cüzdanınız ağ ile senkronize olmadığından şuan ödeme yapabilmeniz mümkün değil. Ödemeyi doğrudan alıcıya gönder. diff --git a/wallet/res/values-vi/strings.xml b/wallet/res/values-vi/strings.xml index eeef0da0fe..3e3b834d9d 100644 --- a/wallet/res/values-vi/strings.xml +++ b/wallet/res/values-vi/strings.xml @@ -64,8 +64,8 @@ Nhập mã PIN! Một phần phí giao dịch của %s sẽ được trả. Một mức phí ưu tiên %s sẽ được trả. Nếu bạn quan tâm đến mức phí thấp, hãy sử dụng \'ưu tiên\' chỉ khi bạn cần xác thực càng nhanh càng tốt. - Khoản này quá nhỏ để có thể gửi - Không đủ tiền + Khoản này quá nhỏ để có thể gửi + Không đủ tiền Một khoản thanh toán nhỏ xíu trong ví của bạn không làm tăng thêm giá trị có thể tiêu được. Giao dịch hiện chưa thực hiện được vì ví chưa hoàn thành được việc đồng bộ với mạng lưới Gửi thanh toán trực tiếp cho người trả. diff --git a/wallet/res/values-zh-rTW/strings.xml b/wallet/res/values-zh-rTW/strings.xml index eb579f7b82..ec58234421 100644 --- a/wallet/res/values-zh-rTW/strings.xml +++ b/wallet/res/values-zh-rTW/strings.xml @@ -65,8 +65,8 @@ 輸入 PIN 將收取%s作為網絡費用。 將支付%s優先級的手續費。如果您希望支付較低的費用,則您只有在需要盡快確認時才使用“優先級”。 - 這個金額太小,無法發送。 - 餘額不足 + 這個金額太小,無法發送。 + 餘額不足 在你的錢包微小金額加起來不足以發送。 目前無法付款,因為錢包未與網絡完全同步 直接付款給收款人 diff --git a/wallet/res/values-zh/strings.xml b/wallet/res/values-zh/strings.xml index c09d1bc66b..5061d762f2 100644 --- a/wallet/res/values-zh/strings.xml +++ b/wallet/res/values-zh/strings.xml @@ -65,8 +65,8 @@ 输入 PIN 将支付 %s 的网络费用. 将支付 %s 作为优先确认费用. 如果您不在意些许的手续费, 使用\'优先确认\'可以使您的交易尽快完成确认. - 该金额太小, 无法支付 - 资金不足 + 该金额太小, 无法支付 + 资金不足 您钱包中的微小付款金额无法达到可支付的金额. 因钱包与网络还未完成同步, 当前交易不可用. 直接向收款人付款. diff --git a/wallet/res/values/strings.xml b/wallet/res/values/strings.xml index 16aec33106..c3a1e49e6b 100644 --- a/wallet/res/values/strings.xml +++ b/wallet/res/values/strings.xml @@ -65,8 +65,6 @@ Enter PIN A network fee of %s will be paid. A priority fee of %s will be paid. If you care about low fees, use \'priority\' only if you need confirmation as soon as possible. - The amount is too small to send - Insufficient funds The amount of tiny payments in your wallet doesn\'t add up to a sendable value. Currently payments are not possible because the wallet is not fully synced with the network Send payment directly to the payee. diff --git a/wallet/src/de/schildbach/wallet/Constants.java b/wallet/src/de/schildbach/wallet/Constants.java index b605878d36..979f81b3bb 100644 --- a/wallet/src/de/schildbach/wallet/Constants.java +++ b/wallet/src/de/schildbach/wallet/Constants.java @@ -52,8 +52,6 @@ public final class Constants { /** Network this wallet is on (e.g. testnet or mainnet). */ public static final NetworkParameters NETWORK_PARAMETERS; - public static final Coin ECONOMIC_FEE = Coin.valueOf(1000); - private static String FILENAME_NETWORK_SUFFIX; /** Currency code for the wallet name resolver. */ diff --git a/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt b/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt index dbd5045762..2e3ff2a650 100644 --- a/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt +++ b/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt @@ -17,7 +17,6 @@ package de.schildbach.wallet.payments import com.google.common.base.Preconditions -import de.schildbach.wallet.Constants import de.schildbach.wallet.WalletApplication import de.schildbach.wallet.ui.security.SecurityGuard import kotlinx.coroutines.Dispatchers @@ -30,6 +29,7 @@ import org.bitcoinj.wallet.SendRequest import org.bitcoinj.wallet.Wallet import org.bitcoinj.wallet.ZeroConfCoinSelector import org.bouncycastle.crypto.params.KeyParameter +import org.dash.wallet.common.Constants import org.dash.wallet.common.services.SendPaymentService import org.dash.wallet.common.transactions.ByAddressCoinSelector import org.slf4j.LoggerFactory diff --git a/wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.java b/wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.java index 708e4aaada..dbcd894bcf 100644 --- a/wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.java +++ b/wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.java @@ -82,7 +82,11 @@ import org.bitcoinj.wallet.DefaultRiskAnalysis; import org.bitcoinj.wallet.Wallet; import org.dash.wallet.common.Configuration; -import org.dash.wallet.common.transactions.IgnoreAddressTxFilter; +import org.dash.wallet.common.services.NotificationService; +import org.dash.wallet.common.transactions.NotFromAddressTxFilter; +import org.dash.wallet.common.transactions.TransactionFilter; +import org.dash.wallet.integrations.crowdnode.transactions.CrowdNodeDepositReceivedResponse; +import org.dash.wallet.integrations.crowdnode.transactions.CrowdNodeWithdrawalReceivedTx; import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,7 +110,9 @@ import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nullable; +import javax.inject.Inject; +import dagger.hilt.android.AndroidEntryPoint; import de.schildbach.wallet.AppDatabase; import de.schildbach.wallet.Constants; import de.schildbach.wallet.WalletApplication; @@ -115,6 +121,7 @@ import de.schildbach.wallet.data.BlockchainState; import de.schildbach.wallet.data.BlockchainStateDao; import de.schildbach.wallet.ui.OnboardingActivity; +import de.schildbach.wallet.ui.staking.StakingActivity; import de.schildbach.wallet.util.AllowLockTimeRiskAnalysis; import de.schildbach.wallet.util.BlockchainStateUtils; import de.schildbach.wallet.util.CrashReporter; @@ -127,6 +134,7 @@ /** * @author Andreas Schildbach */ +@AndroidEntryPoint public class BlockchainServiceImpl extends LifecycleService implements BlockchainService { private WalletApplication application; @@ -180,14 +188,20 @@ public class BlockchainServiceImpl extends LifecycleService implements Blockchai AllowLockTimeRiskAnalysis.Analyzer riskAnalyzer; DefaultRiskAnalysis.Analyzer defaultRiskAnalyzer = DefaultRiskAnalysis.FACTORY; + @Inject + public NotificationService notificationService; + private final ThrottlingWalletChangeListener walletEventListener = new ThrottlingWalletChangeListener( APPWIDGET_THROTTLE_MS) { - // TODO: don't filter out notifications for withdrawals from CrowdNode - private final IgnoreAddressTxFilter ignoreCrowdNodeFilter = new IgnoreAddressTxFilter( - CrowdNodeConstants.INSTANCE.getCrowdNodeAddress(Constants.NETWORK_PARAMETERS) + private final List crowdnodeFilters = Arrays.asList( + new NotFromAddressTxFilter(CrowdNodeConstants.INSTANCE.getCrowdNodeAddress(Constants.NETWORK_PARAMETERS)), + new CrowdNodeWithdrawalReceivedTx(Constants.NETWORK_PARAMETERS) ); + private final CrowdNodeDepositReceivedResponse depositReceivedResponse = + new CrowdNodeDepositReceivedResponse(Constants.NETWORK_PARAMETERS); + @Override public void onThrottledWalletChanged() { updateAppWidget(); @@ -230,8 +244,16 @@ public void onCoinsReceived(final Wallet wallet, final Transaction tx, final Coi final boolean isReceived = amount.signum() > 0; final boolean isReplayedTx = confidenceType == ConfidenceType.BUILDING && (replaying || isRestoringBackup); - if (isReceived && !isReplayedTx && ignoreCrowdNodeFilter.matches(tx)) + if (depositReceivedResponse.matches(tx)) { + notificationService.showNotification( + "deposit_received", + getString(R.string.crowdnode_deposit_received), + false, + new Intent(BlockchainServiceImpl.this, StakingActivity.class) + ); + } else if (isReceived && !isReplayedTx && passFilters(tx)) { notifyCoinsReceived(address, amount, tx.getExchangeRate()); + } }); updateAppWidget(); } @@ -242,6 +264,19 @@ public void onCoinsSent(final Wallet wallet, final Transaction tx, final Coin pr transactionsReceived.incrementAndGet(); updateAppWidget(); } + + private Boolean passFilters(final Transaction tx) { + boolean passFilters = false; + + for (TransactionFilter filter: crowdnodeFilters) { + if (filter.matches(tx)) { + passFilters = true; + break; + } + } + + return passFilters; + } }; private void notifyCoinsReceived(@Nullable final Address address, final Coin amount, diff --git a/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt b/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt index a28369196d..f243dae7ad 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt @@ -39,7 +39,8 @@ import org.slf4j.LoggerFactory class PaymentProtocolViewModel(application: Application) : SendCoinsBaseViewModel(application) { companion object { - val FAKE_FEE_FOR_EXCEPTIONS: Coin = Constants.ECONOMIC_FEE.multiply(261).divide(1000) + val FAKE_FEE_FOR_EXCEPTIONS: Coin = + org.dash.wallet.common.Constants.ECONOMIC_FEE.multiply(261).divide(1000) } private val log = LoggerFactory.getLogger(PaymentProtocolFragment::class.java) diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt index 69327fc4c4..bda8b06931 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt @@ -22,7 +22,6 @@ import android.os.Looper import android.os.Process import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData -import de.schildbach.wallet.Constants import de.schildbach.wallet.WalletApplication import de.schildbach.wallet.data.PaymentIntent import de.schildbach.wallet.livedata.Resource @@ -35,6 +34,7 @@ import org.bitcoinj.wallet.SendRequest import org.bitcoinj.wallet.Wallet import org.bitcoinj.wallet.ZeroConfCoinSelector import org.bouncycastle.crypto.params.KeyParameter +import org.dash.wallet.common.Constants import org.slf4j.LoggerFactory open class SendCoinsBaseViewModel(application: Application) : AndroidViewModel(application) { diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.java b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.java index 1db28dafde..c993cf6b18 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.java +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.java @@ -70,7 +70,6 @@ import org.dash.wallet.common.ui.BaseLockScreenFragment; import de.schildbach.wallet.ui.CheckPinDialog; import de.schildbach.wallet.ui.CheckPinSharedModel; -import de.schildbach.wallet.ui.InputParser; import de.schildbach.wallet.ui.SingleActionSharedViewModel; import de.schildbach.wallet.ui.TransactionResultActivity; import de.schildbach.wallet_test.R; @@ -514,14 +513,14 @@ protected void updateView() { enterAmountSharedViewModel.getMessageTextStringData().setValue(message); } else { if (Coin.ZERO.equals(enterAmountSharedViewModel.getDashAmount()) && wasAmountChangedByTheUser) { - message = coloredString(getString(R.string.send_coins_fragment_hint_dusty_send), R.color.dash_red, true); + message = coloredString(getString(R.string.send_coins_error_dusty_send), R.color.dash_red, true); } else if (viewModel.dryrunException != null) { if (viewModel.dryrunException instanceof DustySendRequested) - message = coloredString(getString(R.string.send_coins_fragment_hint_dusty_send), R.color.dash_red, true); + message = coloredString(getString(R.string.send_coins_error_dusty_send), R.color.dash_red, true); else if (viewModel.dryrunException instanceof InsufficientMoneyException) { - message = coloredString(getString(R.string.send_coins_fragment_hint_insufficient_money), R.color.dash_red, true); + message = coloredString(getString(R.string.send_coins_error_insufficient_money), R.color.dash_red, true); } else if (viewModel.dryrunException instanceof CouldNotAdjustDownwards) { - message = coloredString(getString(R.string.send_coins_fragment_hint_dusty_send), R.color.dash_red, true); + message = coloredString(getString(R.string.send_coins_error_dusty_send), R.color.dash_red, true); } else { message = coloredString(viewModel.dryrunException.toString(), R.color.dash_red, true); }