diff --git a/README.md b/README.md index 0d5a17e3d4..e46cbc498c 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,15 @@ This project contains several sub-projects: The Android app itself. This is probably what you're searching for. [wallet README](wallet/README.md) * __common__: Contains common components used by integrations. - * __integrations/uphold__ - Contains the uphold integration + * __features__: + Contains features such as Explore Dash * __market__: App description and promo material for the Google Play app store. * __integration-android__: A tiny library for integrating Dash payments into your own Android app (e.g. donations, in-app purchases). + * __integrations__ + Contains the various integrations: Crowdnode, Coinbase, Uphold, * __sample-integration-android__: A minimal example app to demonstrate integration of digital payments into your Android app. diff --git a/build.gradle b/build.gradle index 45972f8863..7f6905ea5e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,15 @@ buildscript { ext { - kotlin_version = '1.8.22' + kotlin_version = '1.9.23' coroutinesVersion = '1.6.4' ok_http_version = '4.9.1' - dashjVersion = '20.0.2-CJ-SNAPSHOT' - hiltVersion = '2.45' + dashjVersion = '20.0.5-CJ-SNAPSHOT' + hiltVersion = '2.51' + hiltCompilerVersion = '1.2.0' hiltWorkVersion = '1.0.0' workRuntimeVersion='2.7.1' firebaseVersion = '32.1.1' - roomVersion = '2.4.3' + roomVersion = '2.5.2' jetpackVersion = '1.8.0' appCompatVersion = '1.4.2' @@ -16,7 +17,6 @@ buildscript { lifecycleVersion = '2.5.1' navigationVersion = '2.6.0' datastoreVersion = "1.0.0" - hiltVersion = '2.45' hiltWorkVersion = '1.0.0' serializationVersion = '1.0.1' preferenceVersion = '1.2.0' @@ -62,6 +62,10 @@ buildscript { } } +plugins { + id 'com.google.devtools.ksp' version '1.9.23-1.0.19' apply false +} + allprojects { repositories { google() diff --git a/common/build.gradle b/common/build.gradle index 69c25b5e42..b7a4228476 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' +apply plugin: 'com.google.devtools.ksp' apply plugin: 'kotlin-parcelize' apply plugin: 'dagger.hilt.android.plugin' @@ -8,7 +8,7 @@ android { defaultConfig { compileSdk 33 - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 33 vectorDrawables.useSupportLibrary = true @@ -36,6 +36,10 @@ android { buildFeatures { viewBinding true + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.11" } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { @@ -76,9 +80,14 @@ dependencies { implementation "androidx.room:room-ktx:$roomVersion" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion" - // UI + implementation(platform("androidx.compose:compose-bom:2023.03.00")) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.compose.material3:material3") + debugImplementation 'androidx.compose.ui:ui-tooling:1.0.0' + implementation "com.github.bumptech.glide:glide:$glideVersion" - kapt "com.github.bumptech.glide:compiler:$glideVersion" + ksp "com.github.bumptech.glide:compiler:$glideVersion" implementation "io.coil-kt:coil:$coilVersion" implementation "com.google.android.material:material:$materialVersion" implementation "androidx.browser:browser:$browserVersion" @@ -90,7 +99,7 @@ dependencies { // DI implementation "com.google.dagger:hilt-android:$hiltVersion" - kapt "com.google.dagger:hilt-compiler:$hiltVersion" + ksp "com.google.dagger:hilt-compiler:$hiltVersion" implementation 'javax.inject:javax.inject:1' testImplementation "junit:junit:$junitVersion" diff --git a/common/src/main/java/org/dash/wallet/common/data/ExchangeRatesConfig.kt b/common/src/main/java/org/dash/wallet/common/data/ExchangeRatesConfig.kt new file mode 100644 index 0000000000..5dd70b7235 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/data/ExchangeRatesConfig.kt @@ -0,0 +1,25 @@ +package org.dash.wallet.common.data + +import android.content.Context +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.longPreferencesKey +import org.dash.wallet.common.WalletDataProvider +import javax.inject.Inject + +class ExchangeRatesConfig + @Inject + constructor( + context: Context, + walletDataProvider: WalletDataProvider + ) : BaseConfig( + context, + PREFERENCES_NAME, + walletDataProvider + ) { + companion object { + private const val PREFERENCES_NAME = "exchange_rates_config" + val EXCHANGE_RATES_RETRIEVAL_TIME = longPreferencesKey("exchange_rates_retrieval_time") + val EXCHANGE_RATES_RETRIEVAL_FAILURE = booleanPreferencesKey("exchange_rates_retrieval_error") + val EXCHANGE_RATES_PREVIOUS_RETRIEVAL_TIME = longPreferencesKey("exchange_rates_previous_retrieval_time") + } + } diff --git a/common/src/main/java/org/dash/wallet/common/data/entity/ExchangeRate.kt b/common/src/main/java/org/dash/wallet/common/data/entity/ExchangeRate.kt index 047b8c852d..1b928336e0 100644 --- a/common/src/main/java/org/dash/wallet/common/data/entity/ExchangeRate.kt +++ b/common/src/main/java/org/dash/wallet/common/data/entity/ExchangeRate.kt @@ -34,6 +34,8 @@ open class ExchangeRate : Parcelable { @PrimaryKey var currencyCode: String var rate: String? = null + @Ignore + var retrievalTime: Long = -1L @Ignore private var currencyName: String? = null @@ -41,9 +43,16 @@ open class ExchangeRate : Parcelable { @Ignore private var currency: Currency? = null + constructor(currencyCode: String, rate: String?, retrievalTime: Long) { + this.currencyCode = currencyCode + this.rate = rate + this.retrievalTime = retrievalTime + } + constructor(currencyCode: String, rate: String?) { this.currencyCode = currencyCode this.rate = rate + this.retrievalTime = -1 } protected constructor(input: Parcel) { @@ -79,13 +88,16 @@ open class ExchangeRate : Parcelable { // If the currency is not a valid ISO 4217 code, then set the // currency name to be equal to the currency code // exchanges often have "invalid" currency codes like USDT and CNH - currencyName = if (currencyCode.length == 3) { - try { - getCurrency().displayName - } catch (x: IllegalArgumentException) { + currencyName = + if (currencyCode.length == 3) { + try { + getCurrency().displayName + } catch (x: IllegalArgumentException) { + currencyCode + } + } else { currencyCode } - } else currencyCode if (currencyCode.equals(currencyName!!, ignoreCase = true)) { currencyName = CurrencyInfo.getOtherCurrencyName(currencyCode, context) } @@ -97,7 +109,10 @@ open class ExchangeRate : Parcelable { return 0 } - override fun writeToParcel(dest: Parcel, flags: Int) { + override fun writeToParcel( + dest: Parcel, + flags: Int + ) { dest.writeString(currencyCode) dest.writeString(rate) dest.writeString(currencyName) diff --git a/common/src/main/java/org/dash/wallet/common/services/ExchangeRatesProvider.kt b/common/src/main/java/org/dash/wallet/common/services/ExchangeRatesProvider.kt index d6626cd109..f8b2845850 100644 --- a/common/src/main/java/org/dash/wallet/common/services/ExchangeRatesProvider.kt +++ b/common/src/main/java/org/dash/wallet/common/services/ExchangeRatesProvider.kt @@ -25,4 +25,5 @@ interface ExchangeRatesProvider { fun observeExchangeRate(currencyCode: String): Flow suspend fun getExchangeRate(currencyCode: String): ExchangeRate? suspend fun cleanupObsoleteCurrencies() + fun observeStaleRates(currencyCode: String): Flow } diff --git a/common/src/main/java/org/dash/wallet/common/services/RateRetrievalState.kt b/common/src/main/java/org/dash/wallet/common/services/RateRetrievalState.kt new file mode 100644 index 0000000000..db62957382 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/services/RateRetrievalState.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 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.common.services + +data class RateRetrievalState( + val lastAttemptFailed: Boolean = false, + val staleRates: Boolean, + val volatile: Boolean +) { + val isStale = lastAttemptFailed || staleRates || volatile +} \ No newline at end of file diff --git a/common/src/main/java/org/dash/wallet/common/ui/components/ComposeHostFrameLayout.kt b/common/src/main/java/org/dash/wallet/common/ui/components/ComposeHostFrameLayout.kt new file mode 100644 index 0000000000..de58294431 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/ui/components/ComposeHostFrameLayout.kt @@ -0,0 +1,30 @@ +package org.dash.wallet.common.ui.components + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.ComposeView + +class ComposeHostFrameLayout + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 + ) : FrameLayout(context, attrs, defStyleAttr) { + private var composeView: ComposeView? = null + + fun setContent(content: @Composable () -> Unit) { + if (composeView == null) { + composeView = ComposeView(context) + addView(composeView) + } + composeView?.setContent(content) + } + + override fun removeAllViews() { + composeView = null + super.removeAllViews() + } + } diff --git a/common/src/main/java/org/dash/wallet/common/ui/components/MenuItem.kt b/common/src/main/java/org/dash/wallet/common/ui/components/MenuItem.kt new file mode 100644 index 0000000000..3df31e41c2 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/ui/components/MenuItem.kt @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024 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.common.ui.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.dash.wallet.common.R + +@Composable +fun MenuItem( + title: String, + subtitle: String? = null, + details: String? = null, + icon: Int? = null, // Assuming you use resource IDs for icons + showInfo: Boolean = false, + showChevron: Boolean = false, + isToggled: (() -> Boolean)? = null, // Change to a lambda that returns a Boolean + onToggleChanged: ((Boolean) -> Unit)? = null, + action: (() -> Unit)? = null +) { + var toggleState by remember { mutableStateOf(isToggled?.invoke() ?: false) } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp) + .background(Color.White, RoundedCornerShape(8.dp)) + .clickable { action?.invoke() } + .padding(10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + icon?.let { + Image( + painter = painterResource(id = it), + contentDescription = null, + modifier = Modifier.size(34.dp) + ) + } + + Column( + modifier = Modifier + .weight(1f) + .padding(start = 8.dp) + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = title, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + color = Color.Black, + modifier = Modifier.weight(1f) + ) + + if (showInfo) { + Icon( + painter = painterResource(id = android.R.drawable.ic_dialog_info), + contentDescription = "Info", + tint = Color.Gray, + modifier = Modifier.size(18.dp) + ) + } + } + + subtitle?.let { + Text( + text = it, + fontSize = 12.sp, + color = Color.DarkGray, + modifier = Modifier.padding(top = 2.dp) + ) + } + + details?.let { + Text( + text = it, + fontSize = 12.sp, + color = Color.DarkGray, + modifier = Modifier.padding(top = 2.dp) + ) + } + } + + isToggled?.let { + Switch( + checked = toggleState, + onCheckedChange = { newState -> + toggleState = newState + onToggleChanged?.invoke(newState) + }, + modifier = Modifier.size(60.dp) + ) + } + + if (showChevron) { + Icon( + painter = painterResource(id = R.drawable.ic_menu_row_arrow), + contentDescription = "Chevron", + tint = Color.Gray, + modifier = Modifier.padding(end = 10.dp) + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun PreviewMenuItem() { + MenuItem( + title = "Title", + subtitle = "Easily stake Dash and earn passive income with a few simple steps", + icon = R.drawable.ic_dash_blue_filled, + showInfo = true, + isToggled = { true }, + onToggleChanged = { } + ) +} diff --git a/common/src/main/java/org/dash/wallet/common/ui/components/MyTheme.kt b/common/src/main/java/org/dash/wallet/common/ui/components/MyTheme.kt new file mode 100644 index 0000000000..4bd661bdc9 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/ui/components/MyTheme.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2024 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.common.ui.components + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.sp +import org.dash.wallet.common.R + +object MyTheme { + val ToastBackground = Color(0xff191c1f).copy(alpha = 0.9f) + + val Body2Regular = TextStyle( + fontSize = 14.sp, + lineHeight = 20.sp, + fontFamily = FontFamily.Default, // FontFamily(Font(R.font.inter)) // crashes, + fontWeight = FontWeight(400), + color = Color.White + ) + + val OverlineSemibold = TextStyle( + fontSize = 12.sp, + lineHeight = 16.sp, + fontFamily = FontFamily.Default, // FontFamily(Font(R.font.inter)) // crashes, + fontWeight = FontWeight(600), + color = Color.White, + textAlign = TextAlign.Center + ) +} \ No newline at end of file diff --git a/common/src/main/java/org/dash/wallet/common/ui/components/Toast.kt b/common/src/main/java/org/dash/wallet/common/ui/components/Toast.kt new file mode 100644 index 0000000000..4d2e4cc561 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/ui/components/Toast.kt @@ -0,0 +1,122 @@ +package org.dash.wallet.common.ui.components + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.dash.wallet.common.R + +enum class ToastDuration { + SHORT, + LONG, + INDEFINITE +} + +enum class ToastImageResource(@DrawableRes val resourceId: Int) { + Information(R.drawable.ic_toast_info), + Warning(R.drawable.ic_toast_info_warning), + Copy(R.drawable.ic_toast_copy), + Error(R.drawable.ic_toast_error), + NoInternet(R.drawable.ic_toast_no_wifi) +} + +@Composable +fun Toast( + text: String, + actionText: String, + modifier: Modifier = Modifier, + imageResource: Int? = null, + onActionClick: () -> Unit +) { + Box( + modifier = + modifier.fillMaxWidth() + .padding(horizontal = 5.dp, vertical = 5.dp) + .background( + color = MyTheme.ToastBackground, + shape = RoundedCornerShape(size = 10.dp) + ) + ) { + Row( + modifier = + Modifier + .fillMaxWidth() + .wrapContentSize(Alignment.BottomCenter) + .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Row( + modifier = + Modifier + .weight(1f), + verticalAlignment = Alignment.CenterVertically + ) { + if (imageResource != null) { + Image( + painter = painterResource(id = imageResource), + contentDescription = null, + modifier = + Modifier + .size(15.dp) + ) + } + Text( + text = text, + style = MyTheme.Body2Regular, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(start = 8.dp, end = 8.dp) + ) + } + TextButton( + onClick = onActionClick, + modifier = + Modifier + .padding(start = 0.dp) + .wrapContentSize() + .padding(horizontal = 0.dp, vertical = 0.dp), + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = actionText, + style = MyTheme.OverlineSemibold, + textAlign = TextAlign.End + ) + } + } + } +} + +@Preview +@Composable +private fun ToastPreview() { + Box(Modifier.width(400.dp).height(100.dp).background(Color.White)) { + Toast( + text = "The exchange rates are out of date, please do something about it right away", + actionText = "OK", + Modifier, + R.drawable.ic_image_placeholder + ) { + } + } +} diff --git a/common/src/main/java/org/dash/wallet/common/util/GenericUtils.kt b/common/src/main/java/org/dash/wallet/common/util/GenericUtils.kt index eb10bb6792..ef5324d620 100644 --- a/common/src/main/java/org/dash/wallet/common/util/GenericUtils.kt +++ b/common/src/main/java/org/dash/wallet/common/util/GenericUtils.kt @@ -21,7 +21,8 @@ import android.os.Build import android.os.LocaleList import java.math.RoundingMode import java.text.NumberFormat -import java.util.* +import java.util.Currency +import java.util.Locale /** * @author Andreas Schildbach @@ -32,6 +33,14 @@ object GenericUtils { roundingMode = RoundingMode.HALF_UP } + private val isRunningUnitTest: Boolean + get() = try { + Class.forName("org.junit.Test") + true + } catch (e: ClassNotFoundException) { + false + } + fun startsWithIgnoreCase(string: String, prefix: String): Boolean = string.regionMatches(0, prefix, 0, prefix.length, ignoreCase = true) @@ -45,7 +54,7 @@ object GenericUtils { } fun getDeviceLocale(): Locale { - val countryCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val countryCode = if (!isRunningUnitTest) { LocaleList.getDefault()[0].country } else { Locale.getDefault().country diff --git a/wallet/res/drawable/ic_menu_row_arrow.xml b/common/src/main/res/drawable/ic_menu_row_arrow.xml similarity index 100% rename from wallet/res/drawable/ic_menu_row_arrow.xml rename to common/src/main/res/drawable/ic_menu_row_arrow.xml diff --git a/common/src/main/res/drawable/ic_toast_copy.xml b/common/src/main/res/drawable/ic_toast_copy.xml new file mode 100644 index 0000000000..67abccd337 --- /dev/null +++ b/common/src/main/res/drawable/ic_toast_copy.xml @@ -0,0 +1,14 @@ + + + + diff --git a/common/src/main/res/drawable/ic_toast_error.xml b/common/src/main/res/drawable/ic_toast_error.xml new file mode 100644 index 0000000000..dd3a2affe3 --- /dev/null +++ b/common/src/main/res/drawable/ic_toast_error.xml @@ -0,0 +1,10 @@ + + + diff --git a/common/src/main/res/drawable/ic_toast_info.xml b/common/src/main/res/drawable/ic_toast_info.xml new file mode 100644 index 0000000000..d09b4faa46 --- /dev/null +++ b/common/src/main/res/drawable/ic_toast_info.xml @@ -0,0 +1,10 @@ + + + diff --git a/common/src/main/res/drawable/ic_toast_info_warning.xml b/common/src/main/res/drawable/ic_toast_info_warning.xml new file mode 100644 index 0000000000..be5f37366e --- /dev/null +++ b/common/src/main/res/drawable/ic_toast_info_warning.xml @@ -0,0 +1,10 @@ + + + diff --git a/common/src/main/res/drawable/ic_toast_no_wifi.xml b/common/src/main/res/drawable/ic_toast_no_wifi.xml new file mode 100644 index 0000000000..6fae325799 --- /dev/null +++ b/common/src/main/res/drawable/ic_toast_no_wifi.xml @@ -0,0 +1,9 @@ + + + diff --git a/common/src/main/res/values-ar/strings.xml b/common/src/main/res/values-ar/strings.xml index 89ac54fe86..bafa7f54ef 100644 --- a/common/src/main/res/values-ar/strings.xml +++ b/common/src/main/res/values-ar/strings.xml @@ -98,4 +98,7 @@ متاح غير متاح تسجيل الدخول + + السلسلة قيد المزامنة ... + \ No newline at end of file diff --git a/common/src/main/res/values-de/strings.xml b/common/src/main/res/values-de/strings.xml index 59b512fe7d..79a34f895b 100644 --- a/common/src/main/res/values-de/strings.xml +++ b/common/src/main/res/values-de/strings.xml @@ -98,4 +98,7 @@ verfügbar Nicht verfügbar Einloggen + + Die Chain synchronisiert... + \ No newline at end of file diff --git a/common/src/main/res/values-el/strings.xml b/common/src/main/res/values-el/strings.xml index 24f440c49e..7f7273ad21 100644 --- a/common/src/main/res/values-el/strings.xml +++ b/common/src/main/res/values-el/strings.xml @@ -98,4 +98,7 @@ διαθέσιμο Μη διαθέσιμο Είσοδος + + Το blockchain συγχρονίζεται... + \ No newline at end of file diff --git a/common/src/main/res/values-es/strings.xml b/common/src/main/res/values-es/strings.xml index aae7a5ca86..73e6e71dde 100644 --- a/common/src/main/res/values-es/strings.xml +++ b/common/src/main/res/values-es/strings.xml @@ -67,7 +67,7 @@ Pagar con Elige el método de pago Tarjeta de crédito/débito - Máximo + Máx Selecciona la moneda Dash Billetera de Dash @@ -98,4 +98,7 @@ disponible No disponible Iniciar sesión + + La cadena esta sincronizando... + \ No newline at end of file diff --git a/common/src/main/res/values-fa/strings.xml b/common/src/main/res/values-fa/strings.xml index b88ea4f46e..b80fed1bdb 100644 --- a/common/src/main/res/values-fa/strings.xml +++ b/common/src/main/res/values-fa/strings.xml @@ -98,4 +98,7 @@ موجود در دسترس نیست ورود + + بلاکچین در حال به‌روزرسانی... + \ No newline at end of file diff --git a/common/src/main/res/values-fil/strings.xml b/common/src/main/res/values-fil/strings.xml index 5644356894..793b9e7fa6 100644 --- a/common/src/main/res/values-fil/strings.xml +++ b/common/src/main/res/values-fil/strings.xml @@ -98,4 +98,7 @@ magagamit Hindi available Mag log in + + Nagsi-sync ang chain… + \ No newline at end of file diff --git a/common/src/main/res/values-fr/strings.xml b/common/src/main/res/values-fr/strings.xml index 4da664ba06..92a1159265 100644 --- a/common/src/main/res/values-fr/strings.xml +++ b/common/src/main/res/values-fr/strings.xml @@ -67,7 +67,7 @@ Payer avec Choisir une méthode de paiement Carte de débit ou de crédit - Maximum + Max Choisir les pièces Dash Portefeuille Dash @@ -98,4 +98,7 @@ disponible Non disponible Se connecter + + Synchronisation de la chaîne... + \ No newline at end of file diff --git a/common/src/main/res/values-id/strings.xml b/common/src/main/res/values-id/strings.xml index e4e96ec961..aac2a7e8c8 100644 --- a/common/src/main/res/values-id/strings.xml +++ b/common/src/main/res/values-id/strings.xml @@ -98,4 +98,7 @@ tersedia Tak tersedia masuk + + Rantai sedang menyinkronkan… + \ No newline at end of file diff --git a/common/src/main/res/values-it/strings.xml b/common/src/main/res/values-it/strings.xml index a067f375e2..ae42efa092 100644 --- a/common/src/main/res/values-it/strings.xml +++ b/common/src/main/res/values-it/strings.xml @@ -67,7 +67,7 @@ Paga con Scegli metodo di pagamento Debit/Credit card - Massimo + Max Seleziona una coin Dash Dash Wallet @@ -98,4 +98,7 @@ disponibile Non disponibile Accedi + + La catena si sta sincronizzando... + \ No newline at end of file diff --git a/common/src/main/res/values-ja/strings.xml b/common/src/main/res/values-ja/strings.xml index d5f76befb7..d9efc3eed7 100644 --- a/common/src/main/res/values-ja/strings.xml +++ b/common/src/main/res/values-ja/strings.xml @@ -98,4 +98,7 @@ ご利用可能 ご利用できません ログイン + + チェーンを同期中 + \ No newline at end of file diff --git a/common/src/main/res/values-ko/strings.xml b/common/src/main/res/values-ko/strings.xml index a5e0fb64e4..529622e9b6 100644 --- a/common/src/main/res/values-ko/strings.xml +++ b/common/src/main/res/values-ko/strings.xml @@ -98,4 +98,7 @@ 가능 이용할 수 없음 로그인 + + 체인 동기화 중... + \ No newline at end of file diff --git a/common/src/main/res/values-nl/strings.xml b/common/src/main/res/values-nl/strings.xml index 5b0097ad88..7ac90afc85 100644 --- a/common/src/main/res/values-nl/strings.xml +++ b/common/src/main/res/values-nl/strings.xml @@ -98,4 +98,7 @@ beschikbaar Niet beschikbaar Log in + + De blockchain wordt gesynchroniseerd... + \ No newline at end of file diff --git a/common/src/main/res/values-pl/strings.xml b/common/src/main/res/values-pl/strings.xml index cad7c56b10..1c0bac26bf 100644 --- a/common/src/main/res/values-pl/strings.xml +++ b/common/src/main/res/values-pl/strings.xml @@ -98,4 +98,7 @@ dostępne Niedostępne Zaloguj się + + Łańcuch synchronizuje się… + \ No newline at end of file diff --git a/common/src/main/res/values-pt/strings.xml b/common/src/main/res/values-pt/strings.xml index 404ea23c70..64d9e93996 100644 --- a/common/src/main/res/values-pt/strings.xml +++ b/common/src/main/res/values-pt/strings.xml @@ -67,7 +67,7 @@ Pagar com Escolher método de pagamento Cartão de débito/crédito - Máximo + Máx Selecione a moeda Dash Carteira Dash @@ -98,4 +98,7 @@ disponível Não disponível Entrar + + A cadeia está sendo sincronizada… + \ No newline at end of file diff --git a/common/src/main/res/values-ru/strings.xml b/common/src/main/res/values-ru/strings.xml index 3c7d71c0d4..a962bfe1da 100644 --- a/common/src/main/res/values-ru/strings.xml +++ b/common/src/main/res/values-ru/strings.xml @@ -98,4 +98,7 @@ доступно Недоступно Войти + + Блокчейн синхронизируется... + \ No newline at end of file diff --git a/common/src/main/res/values-sk/strings.xml b/common/src/main/res/values-sk/strings.xml index a67ac8b67a..6b2e6e0975 100644 --- a/common/src/main/res/values-sk/strings.xml +++ b/common/src/main/res/values-sk/strings.xml @@ -98,4 +98,7 @@ dostupné Nie je k dispozícií Prihlásiť sa + + Reťazec sa synchronizuje... + \ No newline at end of file diff --git a/common/src/main/res/values-th/strings.xml b/common/src/main/res/values-th/strings.xml index fc4b097c62..415370009f 100644 --- a/common/src/main/res/values-th/strings.xml +++ b/common/src/main/res/values-th/strings.xml @@ -98,4 +98,7 @@ ที่สามารถใช้ได้ ไม่สามารถใช้ได้ เข้าสู่ระบบ + + โซ่กำลังซิงค์... + \ No newline at end of file diff --git a/common/src/main/res/values-tr/strings.xml b/common/src/main/res/values-tr/strings.xml index 13cebb4810..8c756555c5 100644 --- a/common/src/main/res/values-tr/strings.xml +++ b/common/src/main/res/values-tr/strings.xml @@ -98,4 +98,7 @@ mevcut Mevcut değil Giriş Yap + + Zincir senkronize ediliyor… + \ No newline at end of file diff --git a/common/src/main/res/values-uk/strings.xml b/common/src/main/res/values-uk/strings.xml index 9b7c63ed57..367d3c972a 100644 --- a/common/src/main/res/values-uk/strings.xml +++ b/common/src/main/res/values-uk/strings.xml @@ -67,7 +67,7 @@ Платіть з Виберіть спосіб оплати Дебетова/кредитна картка - Максимум + Макс Виберіть монету Dash Dash Wallet @@ -98,4 +98,7 @@ доступно Недоступний Увійти + + Блокчейн синхронізується... + \ No newline at end of file diff --git a/common/src/main/res/values-zh-rTW/strings.xml b/common/src/main/res/values-zh-rTW/strings.xml index 526d9b0b8a..f042c0c3f1 100644 --- a/common/src/main/res/values-zh-rTW/strings.xml +++ b/common/src/main/res/values-zh-rTW/strings.xml @@ -98,4 +98,7 @@ 可用的 無法使用 登入 + + 區塊鏈正在同步… + \ No newline at end of file diff --git a/common/src/main/res/values-zh/strings.xml b/common/src/main/res/values-zh/strings.xml index 70f0080e01..a7e35e8a32 100644 --- a/common/src/main/res/values-zh/strings.xml +++ b/common/src/main/res/values-zh/strings.xml @@ -98,4 +98,7 @@ 可用 不可用 登录 + + 区块链正在同步... + \ No newline at end of file diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 2a32d0e56c..a18fcd9b41 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -106,6 +106,9 @@ available Not available Log In + + The chain is syncing… + %d%% \ No newline at end of file diff --git a/features/exploredash/build.gradle b/features/exploredash/build.gradle index e10c01513a..2a3ce40bba 100644 --- a/features/exploredash/build.gradle +++ b/features/exploredash/build.gradle @@ -1,7 +1,7 @@ plugins { id 'com.android.library' id 'kotlin-android' - id 'kotlin-kapt' + id 'com.google.devtools.ksp' id 'androidx.navigation.safeargs.kotlin' id 'dagger.hilt.android.plugin' id 'kotlin-parcelize' @@ -11,7 +11,7 @@ plugins { android { defaultConfig { compileSdk 33 - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" @@ -49,10 +49,6 @@ android { namespace 'org.dash.wallet.features.exploredash' } -kapt { - correctErrorTypes true -} - hilt { enableAggregatingTask = true } @@ -77,11 +73,13 @@ dependencies { // Database implementation "androidx.room:room-ktx:$roomVersion" implementation "androidx.room:room-paging:$roomVersion" + implementation "androidx.room:room-runtime:$roomVersion" + ksp "androidx.room:room-compiler:$roomVersion" // DI implementation "com.google.dagger:hilt-android:$hiltVersion" - kapt "com.google.dagger:hilt-compiler:$hiltVersion" - kapt 'androidx.hilt:hilt-compiler:1.0.0' + ksp "com.google.dagger:hilt-compiler:$hiltVersion" + ksp "androidx.hilt:hilt-compiler:$hiltCompilerVersion" implementation "androidx.hilt:hilt-work:$hiltWorkVersion" // UI @@ -119,7 +117,6 @@ dependencies { testImplementation "androidx.arch.core:core-testing:$coreTestingVersion" testImplementation "org.robolectric:robolectric:4.9.2" testImplementation "androidx.room:room-testing:$roomVersion" - kapt "androidx.room:room-compiler:$roomVersion" androidTestImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoVersion" androidTestImplementation "org.mockito:mockito-android:4.6.1" diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt index e4300a6a10..a5d2c0c8db 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ExploreDatabase.kt @@ -26,7 +26,6 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import androidx.sqlite.db.SupportSQLiteDatabase import kotlinx.coroutines.suspendCancellableCoroutine -import org.dash.wallet.common.data.RoomConverters import org.dash.wallet.features.exploredash.data.explore.AtmDao import org.dash.wallet.features.exploredash.data.explore.MerchantDao import org.dash.wallet.features.exploredash.data.explore.model.Atm @@ -35,6 +34,7 @@ import org.dash.wallet.features.exploredash.data.explore.model.Merchant import org.dash.wallet.features.exploredash.data.explore.model.MerchantFTS import org.dash.wallet.features.exploredash.repository.ExploreRepository import org.dash.wallet.features.exploredash.utils.ExploreConfig +import org.dash.wallet.features.exploredash.utils.RoomConverters import org.slf4j.LoggerFactory import java.io.File import kotlin.coroutines.resume diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.kt index 3ef703ec20..57a33b46aa 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/explore/SearchFragment.kt @@ -26,6 +26,7 @@ import android.os.Looper import android.view.View import android.view.inputmethod.InputMethodManager import androidx.activity.OnBackPressedCallback +import androidx.activity.addCallback import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.animation.doOnEnd import androidx.core.content.ContextCompat @@ -81,6 +82,7 @@ class SearchFragment : Fragment(R.layout.fragment_search) { private var isKeyboardShowing: Boolean = false private var hasLocationBeenRequested: Boolean = false private var previousScreenState: ScreenState = ScreenState.SearchResults + private var onBackPressedCallback: OnBackPressedCallback? = null private val isPhysicalSearch: Boolean get() = viewModel.exploreTopic == ExploreTopic.ATMs || viewModel.filterMode.value == FilterMode.Nearby @@ -267,6 +269,13 @@ class SearchFragment : Fragment(R.layout.fragment_search) { override fun onDestroy() { super.onDestroy() viewModel.onExitSearch() + // clear this listener + requireActivity().window?.decorView?.let { decor -> + ViewCompat.setOnApplyWindowInsetsListener(decor) { _, _ -> + WindowInsetsCompat.CONSUMED + } + } + onBackPressedCallback?.remove() } private fun refreshManageGpsView() { @@ -554,19 +563,12 @@ class SearchFragment : Fragment(R.layout.fragment_search) { } } - requireActivity() - .onBackPressedDispatcher - .addCallback( - viewLifecycleOwner, - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - hardBackAction.invoke() - if (isMerchantTopic) { - viewModel.logEvent(AnalyticsConstants.Explore.MERCHANT_DETAILS_BACK_BOTTOM) - } - } - } - ) + onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { + hardBackAction.invoke() + if (isMerchantTopic) { + viewModel.logEvent(AnalyticsConstants.Explore.MERCHANT_DETAILS_BACK_BOTTOM) + } + } } private fun transitToDetails() { diff --git a/common/src/main/java/org/dash/wallet/common/data/RoomConverters.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/RoomConverters.kt similarity index 89% rename from common/src/main/java/org/dash/wallet/common/data/RoomConverters.kt rename to features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/RoomConverters.kt index 1bde713c63..7f36362a38 100644 --- a/common/src/main/java/org/dash/wallet/common/data/RoomConverters.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/RoomConverters.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2024 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 @@ -15,10 +15,10 @@ * along with this program. If not, see . */ -package org.dash.wallet.common.data +package org.dash.wallet.features.exploredash.utils import androidx.room.TypeConverter -import java.util.* +import java.util.Date class RoomConverters { @TypeConverter @@ -30,4 +30,4 @@ class RoomConverters { fun dateToTimestamp(date: Date?): Long? { return date?.time } -} \ No newline at end of file +} diff --git a/integrations/coinbase/build.gradle b/integrations/coinbase/build.gradle index 600c9b9aac..5b7f5afe21 100644 --- a/integrations/coinbase/build.gradle +++ b/integrations/coinbase/build.gradle @@ -1,7 +1,7 @@ plugins { id 'com.android.library' id 'kotlin-android' - id 'kotlin-kapt' + id 'com.google.devtools.ksp' id 'androidx.navigation.safeargs.kotlin' id 'dagger.hilt.android.plugin' id 'kotlin-parcelize' @@ -11,7 +11,7 @@ android { defaultConfig { compileSdk 33 - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 33 vectorDrawables.useSupportLibrary = true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -44,10 +44,6 @@ android { namespace 'org.dash.wallet.integrations.coinbase' } -kapt { - correctErrorTypes true -} - hilt { enableAggregatingTask = true } @@ -68,8 +64,8 @@ dependencies { // DI implementation "com.google.dagger:hilt-android:$hiltVersion" - kapt "com.google.dagger:hilt-android-compiler:$hiltVersion" - kapt "androidx.hilt:hilt-compiler:1.0.0" + ksp "com.google.dagger:hilt-android-compiler:$hiltVersion" + ksp "androidx.hilt:hilt-compiler:$hiltCompilerVersion" // UI implementation "com.google.android.material:material:$materialVersion" @@ -85,12 +81,10 @@ dependencies { // Tests testImplementation 'junit:junit:4.13.2' - testImplementation "org.mockito:mockito-core:4.0.0" - testImplementation 'org.mockito:mockito-inline:3.8.0' testImplementation "io.mockk:mockk:1.9.3" - implementation 'org.hamcrest:hamcrest:2.2' testImplementation "androidx.test:core-ktx:1.4.0" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion" + testImplementation 'org.hamcrest:hamcrest:2.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/CoinbaseConstants.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/CoinbaseConstants.kt index bd8300462e..ccd0315534 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/CoinbaseConstants.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/CoinbaseConstants.kt @@ -17,6 +17,7 @@ package org.dash.wallet.integrations.coinbase import android.content.Context +import org.dash.wallet.integrations.coinbase.service.CoinBaseClientConstants import java.io.File object CoinbaseConstants { @@ -36,6 +37,20 @@ object CoinbaseConstants { const val MIN_USD_COINBASE_AMOUNT = "2" const val BASE_IDS_REQUEST_URL = "v2/assets/prices?filter=holdable&resolution=latest" const val BUY_FEE = 0.006 + const val REDIRECT_URL = "dashwallet://brokers/coinbase/connect" + const val AUTH_RESULT_ACTION = "Coinbase.AUTH_RESULT" + val LINK_URL = "https://www.coinbase.com/oauth/authorize?client_id=${CoinBaseClientConstants.CLIENT_ID}" + + "&redirect_uri=${REDIRECT_URL}&response_type" + + "=code&scope=wallet:accounts:read,wallet:user:read,wallet:payment-methods:read," + + "wallet:buys:read,wallet:buys:create,wallet:transactions:transfer," + + "wallet:sells:create,wallet:sells:read,wallet:deposits:create," + + "wallet:transactions:request,wallet:transactions:read,wallet:trades:create," + + "wallet:supported-assets:read,wallet:transactions:send," + + "wallet:addresses:read,wallet:addresses:create" + + "&meta[send_limit_amount]=10" + + "&meta[send_limit_currency]=USD" + + "&meta[send_limit_period]=month" + + "&account=all" fun getCacheDir(context: Context): File { return File(context.cacheDir, "coinbase") diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/AccountsResponse.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/AccountsResponse.kt index 551d220934..2753905aaa 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/AccountsResponse.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/AccountsResponse.kt @@ -19,7 +19,15 @@ package org.dash.wallet.integrations.coinbase.model import android.os.Parcelable import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize +import org.bitcoinj.core.Coin +import org.dash.wallet.common.util.toCoin +import java.lang.Exception +import java.lang.IllegalArgumentException +import java.math.BigDecimal +import java.math.MathContext +import java.math.RoundingMode import java.util.UUID @Parcelize @@ -51,6 +59,17 @@ data class CoinbaseAccount( ready = false ) } + + fun coinBalance(): Coin = try { + Coin.parseCoin(availableBalance.value) + } catch (ex: IllegalArgumentException) { + try { + val rounded = BigDecimal(availableBalance.value).round(MathContext(8, RoundingMode.HALF_UP)) + rounded.toCoin() + } catch (ex: Exception) { + Coin.ZERO + } + } } @Parcelize diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/PaymentMethods.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/PaymentMethods.kt index 7827faaebc..7006ae640a 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/PaymentMethods.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/model/PaymentMethods.kt @@ -19,10 +19,19 @@ package org.dash.wallet.integrations.coinbase.model import com.google.gson.annotations.SerializedName data class PaymentMethodsResponse( - val `data`: List? + @SerializedName("payment_methods") + val paymentMethods: List? ) data class PaymentMethodsData( + val id: String, + val type: String, + val name: String, + val currency: String, + @SerializedName("created_at") + val createdAt: String? = null, + @SerializedName("updated_at") + val updatedAt: String? = null, @SerializedName("allow_buy") val isBuyingAllowed: Boolean? = null, @SerializedName("allow_deposit") @@ -31,46 +40,5 @@ data class PaymentMethodsData( val isSellingAllowed: Boolean? = null, @SerializedName("allow_withdraw") val isWithdrawalAllowed: Boolean? = null, - @SerializedName("created_at") - val createdAt: String? = null, - val currency: String? = null, - @SerializedName("fiat_account") - val fiatAccount: FiatAccount? = null, - val id: String? = null, - @SerializedName("instant_buy") - val isInstantBuyAllowed: Boolean? = null, - @SerializedName("instant_sell") - val isInstantSellAllowed: Boolean? = null, - val limits: Limits? = null, - @SerializedName("minimum_purchase_amount") - val minimumPurchaseAmount: MinimumPurchaseAmount? = null, - val name: String? = null, - @SerializedName("primary_buy") - val isPrimaryBuying: Boolean? = null, - @SerializedName("primary_sell") - val isPrimarySelling: Boolean? = null, - val resource: String? = null, - @SerializedName("resource_path") - val resourcePath: String? = null, - val type: String? = null, - @SerializedName("updated_at") - val updatedAt: String? = null, val verified: Boolean? = null -) - -data class FiatAccount( - val id: String? = null, - val resource: String? = null, - @SerializedName("resource_path") - val resourcePath: String? = null -) - -data class Limits( - val name: String? = null, - val type: String? = null -) - -data class MinimumPurchaseAmount( - val amount: String? = null, - val currency: String? = null -) +) \ No newline at end of file diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/repository/CoinBaseRepository.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/repository/CoinBaseRepository.kt index 05d80a074f..037a4012b9 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/repository/CoinBaseRepository.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/repository/CoinBaseRepository.kt @@ -29,6 +29,7 @@ import org.dash.wallet.common.data.safeApiCall import org.dash.wallet.common.services.ExchangeRatesProvider import org.dash.wallet.common.util.Constants import org.dash.wallet.common.util.GenericUtils +import org.dash.wallet.common.util.toCoin import org.dash.wallet.integrations.coinbase.* import org.dash.wallet.integrations.coinbase.model.* import org.dash.wallet.integrations.coinbase.service.CoinBaseAuthApi @@ -37,6 +38,8 @@ import org.dash.wallet.integrations.coinbase.service.CoinBaseServicesApi import org.dash.wallet.integrations.coinbase.utils.CoinbaseConfig import org.dash.wallet.integrations.coinbase.viewmodels.toDoubleOrZero import java.math.BigDecimal +import java.math.MathContext +import java.math.RoundingMode import javax.inject.Inject interface CoinBaseRepositoryInt { @@ -60,10 +63,10 @@ interface CoinBaseRepositoryInt { ): ResponseResource suspend fun swapTrade(tradesRequest: TradesRequest): ResponseResource suspend fun commitSwapTrade(buyOrderId: String): ResponseResource - suspend fun completeCoinbaseAuthentication(authorizationCode: String): ResponseResource + suspend fun completeCoinbaseAuthentication(authorizationCode: String): Boolean suspend fun refreshWithdrawalLimit() suspend fun getExchangeRateFromCoinbase(): ResponseResource - suspend fun getWithdrawalLimitInDash(): Double + suspend fun isInputGreaterThanLimit(amountInDash: Coin): Boolean } class CoinBaseRepository @Inject constructor( @@ -98,10 +101,10 @@ class CoinBaseRepository @Inject constructor( it.currency == Constants.DASH_CURRENCY } ?: throw IllegalStateException("No DASH account found") - return userAccountData.also { - config.set(CoinbaseConfig.USER_ACCOUNT_ID, it.uuid.toString()) - config.set(CoinbaseConfig.LAST_BALANCE, Coin.parseCoin(it.availableBalance.value).value) - } + config.set(CoinbaseConfig.USER_ACCOUNT_ID, userAccountData.uuid.toString()) + config.set(CoinbaseConfig.LAST_BALANCE, userAccountData.coinBalance().value) + + return userAccountData } override suspend fun getUserAccounts(exchangeCurrencyCode: String): List { @@ -158,7 +161,7 @@ class CoinBaseRepository @Inject constructor( override suspend fun getActivePaymentMethods(): List { val apiResult = servicesApi.getActivePaymentMethods() - return apiResult?.data ?: emptyList() + return apiResult?.paymentMethods ?: emptyList() } override suspend fun depositToFiatAccount(paymentMethodId: String, amountUSD: String) { @@ -217,15 +220,14 @@ class CoinBaseRepository @Inject constructor( ) } - override suspend fun completeCoinbaseAuthentication(authorizationCode: String) = safeApiCall { - authApi.getToken(code = authorizationCode).also { - it?.let { tokenResponse -> - config.set(CoinbaseConfig.LAST_ACCESS_TOKEN, tokenResponse.accessToken) - config.set(CoinbaseConfig.LAST_REFRESH_TOKEN, tokenResponse.refreshToken) - getUserAccount() - } + override suspend fun completeCoinbaseAuthentication(authorizationCode: String): Boolean { + authApi.getToken(code = authorizationCode)?.let { tokenResponse -> + config.set(CoinbaseConfig.LAST_ACCESS_TOKEN, tokenResponse.accessToken) + config.set(CoinbaseConfig.LAST_REFRESH_TOKEN, tokenResponse.refreshToken) + getUserAccount() } - !config.get(CoinbaseConfig.LAST_ACCESS_TOKEN).isNullOrEmpty() + + return !config.get(CoinbaseConfig.LAST_ACCESS_TOKEN).isNullOrEmpty() } override suspend fun refreshWithdrawalLimit() { @@ -264,7 +266,14 @@ class CoinBaseRepository @Inject constructor( servicesApi.createAddress(accountId = userAccountId)?.addresses?.address ?: "" } - override suspend fun getWithdrawalLimitInDash(): Double { + override suspend fun isInputGreaterThanLimit(amountInDash: Coin): Boolean { + // TODO: disabled until Coinbase changes are clear + return false +// val withdrawalLimitInDash = getWithdrawalLimitInDash() +// return amountInDash.toPlainString().toDoubleOrZero.compareTo(withdrawalLimitInDash) > 0 + } + + private suspend fun getWithdrawalLimitInDash(): Double { val withdrawalLimit = config.get(CoinbaseConfig.USER_WITHDRAWAL_LIMIT) val withdrawalLimitCurrency = config.get(CoinbaseConfig.SEND_LIMIT_CURRENCY) ?: CoinbaseConstants.DEFAULT_CURRENCY_USD diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseAuthApi.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseAuthApi.kt index 7adeeb17ff..16727967a8 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseAuthApi.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseAuthApi.kt @@ -16,6 +16,7 @@ */ package org.dash.wallet.integrations.coinbase.service +import org.dash.wallet.integrations.coinbase.CoinbaseConstants import org.dash.wallet.integrations.coinbase.model.TokenResponse import retrofit2.http.Field import retrofit2.http.FormUrlEncoded @@ -26,12 +27,13 @@ interface CoinBaseAuthApi { @POST("oauth/token") suspend fun getToken( @Field("client_id") clientId: String = CoinBaseClientConstants.CLIENT_ID, - @Field("redirect_uri") redirectUri: String = "authhub://oauth-callback", - @Field("grant_type") grant_type: String = "authorization_code", - @Field("client_secret") client_secret: String = CoinBaseClientConstants.CLIENT_SECRET, + @Field("redirect_uri") redirectUri: String = CoinbaseConstants.REDIRECT_URL, + @Field("grant_type") grantType: String = "authorization_code", + @Field("client_secret") clientSecret: String = CoinBaseClientConstants.CLIENT_SECRET, @Field("code") code: String ): TokenResponse? + @FormUrlEncoded @POST("oauth/revoke") suspend fun revokeToken(@Field("token") token: String) } diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseServicesApi.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseServicesApi.kt index a0477aa916..1ba1fc8209 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseServicesApi.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/service/CoinBaseServicesApi.kt @@ -42,7 +42,7 @@ interface CoinBaseServicesApi { @Query("currency") currency: String = Constants.DASH_CURRENCY ): CoinBaseExchangeRates? - @GET("v2/payment-methods") + @GET("api/v3/brokerage/payment_methods") suspend fun getActivePaymentMethods( @Header(CoinbaseConstants.CB_VERSION_KEY) apiVersion: String = CoinbaseConstants.CB_VERSION_VALUE ): PaymentMethodsResponse? diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinBaseWebClientActivity.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinBaseWebClientActivity.kt deleted file mode 100644 index 244814bd70..0000000000 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinBaseWebClientActivity.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2023 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.coinbase.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.MenuItem -import android.webkit.* -import org.dash.wallet.common.InteractionAwareActivity -import org.dash.wallet.common.databinding.FragmentWebviewBinding -import org.dash.wallet.integrations.coinbase.R -import org.dash.wallet.integrations.coinbase.service.CoinBaseClientConstants - -class CoinBaseWebClientActivity : InteractionAwareActivity() { - - private lateinit var binding: FragmentWebviewBinding - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = FragmentWebviewBinding.inflate(layoutInflater) - setContentView(binding.root) - initUI() - turnOffAutoLogout() - } - - @SuppressLint("SetJavaScriptEnabled") - private fun initUI() { - setSupportActionBar(binding.toolbar) - val actionBar = supportActionBar - actionBar?.apply { - setDisplayHomeAsUpEnabled(true) - setDisplayShowHomeEnabled(true) - } - setTitle(R.string.coinbase) - // Clear all the Application Cache, Web SQL Database and the HTML5 Web Storage - WebStorage.getInstance().deleteAllData() - - // Clear all the cookies - CookieManager.getInstance().removeAllCookies(null) - CookieManager.getInstance().flush() - - binding.webView.clearCache(true) - binding.webView.clearFormData() - binding.webView.clearHistory() - binding.webView.clearSslPreferences() - - binding.webView.webViewClient = MyWebViewClient() - binding.webView.webChromeClient = WebChromeClient() - binding.webView.settings.javaScriptEnabled = true - binding.webView.settings.allowFileAccess = false - WebStorage.getInstance().deleteAllData() - - val loginUrl = - "https://www.coinbase.com/oauth/authorize?client_id=${CoinBaseClientConstants.CLIENT_ID}" + - "&redirect_uri=authhub://oauth-callback&response_type" + - "=code&scope=wallet:accounts:read,wallet:user:read,wallet:payment-methods:read," + - "wallet:buys:read,wallet:buys:create,wallet:transactions:transfer," + - "wallet:sells:create,wallet:sells:read,wallet:deposits:create," + - "wallet:transactions:request,wallet:transactions:read,wallet:trades:create," + - "wallet:supported-assets:read,wallet:transactions:send," + - "wallet:addresses:read,wallet:addresses:create" + - "&meta[send_limit_amount]=1" + - "&meta[send_limit_currency]=USD" + - "&meta[send_limit_period]=month" + - "&account=all" - - binding.webView.loadUrl(loginUrl) - } - - override fun onDestroy() { - super.onDestroy() - turnOnAutoLogout() - } - - override fun onPause() { - super.onPause() - binding.webView.onPause() - } - - override fun onResume() { - super.onResume() - binding.webView.onResume() - } - - private fun setActivityResult(code: String) { - val intent = Intent() - intent.putExtra(RESULT_TEXT, code) - setResult(RESULT_OK, intent) - finish() - } - - private inner class MyWebViewClient : WebViewClient() { - - override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { - val uri = Uri.parse(url) - val host = uri.host - if (Uri.parse(url).host == "oauth-callback") { - // This is my web site, so do not override; let my WebView load the page - uri.getQueryParameter("code")?.let { - // viewModel.setLoginToken(it) - setActivityResult(it) - } - - return false - } - - url?.let { view?.loadUrl(it) } - return true - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> { - onBackPressed() - return true - } - } - return super.onOptionsItemSelected(item) - } - - companion object { - val RESULT_TEXT = "RESULT_TEXT" - } -} diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseConversionPreviewFragment.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseConversionPreviewFragment.kt index a3d93cb1b1..fca8e5cf69 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseConversionPreviewFragment.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseConversionPreviewFragment.kt @@ -19,6 +19,7 @@ package org.dash.wallet.integrations.coinbase.ui import android.os.Bundle import android.os.CountDownTimer import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.activity.addCallback import androidx.annotation.ColorRes import androidx.annotation.StyleRes @@ -52,7 +53,7 @@ class CoinbaseConversionPreviewFragment : Fragment(R.layout.fragment_coinbase_co private var isRetrying = false private var transactionStateDialog: CoinBaseResultDialog? = null private var newSwapOrderId: String? = null - + private var onBackPressedCallback: OnBackPressedCallback? = null private val countDownTimer by lazy { object : CountDownTimer(10000, 1000) { @@ -360,9 +361,14 @@ class CoinbaseConversionPreviewFragment : Fragment(R.layout.fragment_coinbase_co findNavController().popBackStack() } - requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { + onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { viewModel.logEvent(AnalyticsConstants.Coinbase.CONVERT_QUOTE_ANDROID_BACK) findNavController().popBackStack() } } + + override fun onDestroy() { + super.onDestroy() + onBackPressedCallback?.remove() + } } diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseOrderReviewFragment.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseOrderReviewFragment.kt index 17a511be0c..274b9f840e 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseOrderReviewFragment.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseOrderReviewFragment.kt @@ -18,6 +18,7 @@ package org.dash.wallet.integrations.coinbase.ui import android.os.Bundle import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.activity.addCallback import androidx.core.view.isVisible import androidx.fragment.app.Fragment @@ -51,11 +52,12 @@ class CoinbaseOrderReviewFragment : Fragment(R.layout.fragment_coinbase_order_re private val dashFormat = MonetaryFormat().withLocale( GenericUtils.getDeviceLocale() ).noCode().minDecimals(6).optionalDecimals() + private var onBackPressedCallback: OnBackPressedCallback? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { + onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { viewModel.logEvent(AnalyticsConstants.Coinbase.BUY_QUOTE_ANDROID_BACK) findNavController().popBackStack() } @@ -186,4 +188,9 @@ class CoinbaseOrderReviewFragment : Fragment(R.layout.fragment_coinbase_order_re return Pair("", "") } + + override fun onDestroy() { + super.onDestroy() + onBackPressedCallback?.remove() + } } diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseServicesFragment.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseServicesFragment.kt index 6d76ab36ab..0d9c4b99ad 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseServicesFragment.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/CoinbaseServicesFragment.kt @@ -18,7 +18,10 @@ package org.dash.wallet.integrations.coinbase.ui import android.animation.ObjectAnimator import android.app.Activity +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.net.Uri import android.os.Bundle import android.view.View @@ -27,8 +30,11 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.withResumed +import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import org.bitcoinj.core.Coin import org.dash.wallet.common.databinding.FragmentIntegrationPortalBinding import org.dash.wallet.common.services.analytics.AnalyticsConstants @@ -36,8 +42,10 @@ import org.dash.wallet.common.ui.blinkAnimator import org.dash.wallet.common.ui.dialogs.AdaptiveDialog import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.common.util.observe +import org.dash.wallet.common.util.openCustomTab import org.dash.wallet.common.util.safeNavigate import org.dash.wallet.common.util.toFormattedString +import org.dash.wallet.integrations.coinbase.CoinbaseConstants import org.dash.wallet.integrations.coinbase.R import org.dash.wallet.integrations.coinbase.model.CoinbaseErrorType import org.dash.wallet.integrations.coinbase.viewmodels.CoinbaseViewModel @@ -51,17 +59,20 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal) private val sharedViewModel by coinbaseViewModels() private var balanceAnimator: ObjectAnimator? = null - private val coinbaseAuthLauncher = registerForActivityResult( - ActivityResultContracts.StartActivityForResult() - ) { result -> - val data = result.data + private val coinbaseAuthResultReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val uri = intent.extras?.get("uri") as Uri? + val code = uri?.getQueryParameter("code") - if (result.resultCode == Activity.RESULT_OK) { - data?.extras?.getString(CoinBaseWebClientActivity.RESULT_TEXT)?.let { code -> - lifecycleScope.launchWhenResumed { - handleCoinbaseAuthResult(code) + if (code != null) { + lifecycleScope.launch { + withResumed { + handleCoinbaseAuthResult(code) + } } } + + startActivity(intent) } } @@ -125,9 +136,7 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal) it.isCancelable = false }.show(requireActivity()) { login -> if (login == true) { - coinbaseAuthLauncher.launch( - Intent(requireContext(), CoinBaseWebClientActivity::class.java) - ) + requireActivity().openCustomTab(CoinbaseConstants.LINK_URL) } else { findNavController().popBackStack() } @@ -170,6 +179,11 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal) viewModel.refreshBalance() sharedViewModel.getBaseIdForFiatModel() + + LocalBroadcastManager.getInstance(requireContext()).registerReceiver( + coinbaseAuthResultReceiver, + IntentFilter(CoinbaseConstants.AUTH_RESULT_ACTION) + ) } private fun setNetworkState(hasInternet: Boolean) { @@ -186,30 +200,39 @@ class CoinbaseServicesFragment : Fragment(R.layout.fragment_integration_portal) startActivity(defaultBrowser) } - private suspend fun handleCoinbaseAuthResult(code: String) { - val success = AdaptiveDialog.withProgress(getString(R.string.loading), requireActivity()) { - sharedViewModel.loginToCoinbase(code) - } + private fun handleCoinbaseAuthResult(code: String) { + lifecycleScope.launch { + val success = AdaptiveDialog.withProgress(getString(R.string.loading), requireActivity()) { + sharedViewModel.loginToCoinbase(code) + } - if (success) { - return - } + if (success) { + return@launch + } - val retry = AdaptiveDialog.create( - R.drawable.ic_error, - getString(R.string.login_error_title, getString(R.string.coinbase)), - getString(R.string.login_error_message, getString(R.string.coinbase)), - getString(android.R.string.cancel), - getString(R.string.retry) - ).showAsync(requireActivity()) - - if (retry == true) { - handleCoinbaseAuthResult(code) - } else { - findNavController().popBackStack() + val retry = AdaptiveDialog.create( + R.drawable.ic_error, + getString(R.string.login_error_title, getString(R.string.coinbase)), + getString(R.string.login_error_message, getString(R.string.coinbase)), + getString(android.R.string.cancel), + getString(R.string.retry) + ).showAsync(requireActivity()) + + if (retry == true) { + handleCoinbaseAuthResult(code) + } else { + findNavController().popBackStack() + } } } + override fun onDestroyView() { + super.onDestroyView() + LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver( + coinbaseAuthResultReceiver + ) + } + override fun onDestroy() { super.onDestroy() this.balanceAnimator = null diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/EnterTwoFaCodeFragment.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/EnterTwoFaCodeFragment.kt index 38dcaab08c..a180a937eb 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/EnterTwoFaCodeFragment.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/EnterTwoFaCodeFragment.kt @@ -24,6 +24,7 @@ import android.net.Uri import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.activity.OnBackPressedCallback import androidx.activity.addCallback import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged @@ -50,6 +51,7 @@ class EnterTwoFaCodeFragment : Fragment(R.layout.enter_two_fa_code_fragment), Lo private val binding by viewBinding(EnterTwoFaCodeFragmentBinding::bind) private val viewModel by viewModels() private lateinit var loadingDialog: AdaptiveDialog + private var onBackPressedCallback: OnBackPressedCallback? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -160,7 +162,7 @@ class EnterTwoFaCodeFragment : Fragment(R.layout.enter_two_fa_code_fragment), Lo findNavController().popBackStack() } - requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner){ + onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { findNavController().previousBackStackEntry?.savedStateHandle?.set("resume_review", true) findNavController().popBackStack() } @@ -220,4 +222,9 @@ class EnterTwoFaCodeFragment : Fragment(R.layout.enter_two_fa_code_fragment), Lo override fun onLockScreenActivated() { findNavController().popBackStack(R.id.coinbaseServicesFragment, false) } + + override fun onDestroy() { + super.onDestroy() + onBackPressedCallback?.remove() + } } diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/TransferDashFragment.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/TransferDashFragment.kt index c2d59587c4..d3f97aa95b 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/TransferDashFragment.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/ui/TransferDashFragment.kt @@ -21,6 +21,7 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.activity.addCallback import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible @@ -72,6 +73,7 @@ class TransferDashFragment : Fragment(R.layout.transfer_dash_fragment) { private var dashValue: Coin = Coin.ZERO private val dashFormat = MonetaryFormat().withLocale(GenericUtils.getDeviceLocale()) .noCode().minDecimals(2).optionalDecimals() + private var onBackPressedCallback: OnBackPressedCallback? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -350,7 +352,9 @@ class TransferDashFragment : Fragment(R.layout.transfer_dash_fragment) { private fun handleBackButtonPress(){ binding.toolbar.setNavigationOnClickListener { findNavController().popBackStack() } - requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner){ findNavController().popBackStack() } + onBackPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { + findNavController().popBackStack() + } } private fun setInternetAccessState(hasInternet: Boolean) { @@ -452,4 +456,9 @@ class TransferDashFragment : Fragment(R.layout.transfer_dash_fragment) { guidePercent = 0.09f } } + + override fun onDestroy() { + super.onDestroy() + onBackPressedCallback?.remove() + } } \ No newline at end of file diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseBuyDashViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseBuyDashViewModel.kt index 4a3459fa36..2926dcb5a9 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseBuyDashViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseBuyDashViewModel.kt @@ -87,14 +87,14 @@ class CoinbaseBuyDashViewModel @Inject constructor( ) } else if (retryWithDeposit) { val bankAccount = coinBaseRepository.getActivePaymentMethods().firstOrNull { - paymentMethodTypeFromCoinbaseType(it.type ?: "") == PaymentMethodType.BankAccount + paymentMethodTypeFromCoinbaseType(it.type) == PaymentMethodType.BankAccount } ?: return CoinbaseErrorType.NO_BANK_ACCOUNT paymentMethod = PaymentMethod( - bankAccount.id ?: "", - bankAccount.name ?: "", + bankAccount.id, + bankAccount.name, account = "", - accountType = bankAccount.type ?: "", + accountType = bankAccount.type, paymentMethodType = PaymentMethodType.BankAccount, isValid = true ) @@ -177,13 +177,14 @@ class CoinbaseBuyDashViewModel @Inject constructor( private fun paymentMethodTypeFromCoinbaseType(type: String?): PaymentMethodType { return when (type) { - "fiat_account" -> PaymentMethodType.Fiat - "secure3d_card", "worldpay_card", "credit_card", "debit_card" -> PaymentMethodType.Card - "ach_bank_account", "sepa_bank_account", - "ideal_bank_account", "eft_bank_account", "interac" -> PaymentMethodType.BankAccount - "bank_wire" -> PaymentMethodType.WireTransfer - "paypal_account" -> PaymentMethodType.PayPal - "apple_pay" -> PaymentMethodType.ApplePay + "COINBASE_FIAT_ACCOUNT" -> PaymentMethodType.Fiat + "SECURE3D_CARD", "WORLDPAY_CARD", "CREDIT_CARD", "DEBIT_CARD" -> PaymentMethodType.Card + "ACH", "SEPA", + "IDEAL", "EFT", "INTERAC" -> PaymentMethodType.BankAccount + "BANK_WIRE" -> PaymentMethodType.WireTransfer + "PAYPAL", "PAYPAL_ACCOUNT" -> PaymentMethodType.PayPal + "APPLE_PAY" -> PaymentMethodType.ApplePay + "GOOGLE_PAY" -> PaymentMethodType.GooglePay else -> PaymentMethodType.Unknown } } diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseConvertCryptoViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseConvertCryptoViewModel.kt index 92db1b5b08..23dec36aec 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseConvertCryptoViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseConvertCryptoViewModel.kt @@ -186,7 +186,6 @@ class CoinbaseConvertCryptoViewModel @Inject constructor( } suspend fun isInputGreaterThanLimit(amountInDash: Coin): Boolean { - val withdrawalLimitInDash = coinBaseRepository.getWithdrawalLimitInDash() - return amountInDash.toPlainString().toDoubleOrZero.compareTo(withdrawalLimitInDash) > 0 + return coinBaseRepository.isInputGreaterThanLimit(amountInDash) } } diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseServicesViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseServicesViewModel.kt index 18f23f7dd2..de25178b0c 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseServicesViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseServicesViewModel.kt @@ -90,7 +90,8 @@ class CoinbaseServicesViewModel @Inject constructor( }.onEach { state -> _uiState.value = state } .launchIn(viewModelScope) - viewModelScope.launch { coinBaseRepository.refreshWithdrawalLimit() } + // TODO: disabled until Coinbase changes are clear +// viewModelScope.launch { coinBaseRepository.refreshWithdrawalLimit() } } fun refreshBalance() { @@ -100,7 +101,7 @@ class CoinbaseServicesViewModel @Inject constructor( val response = coinBaseRepository.getUserAccount() config.set( CoinbaseConfig.LAST_BALANCE, - Coin.parseCoin(response.availableBalance.value).value + response.coinBalance().value ) } catch (ex: IllegalStateException) { _uiState.value = _uiState.value.copy(error = CoinbaseErrorType.USER_ACCOUNT_ERROR) diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseViewModel.kt index f23597831b..f5bab00471 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/CoinbaseViewModel.kt @@ -38,6 +38,7 @@ import org.dash.wallet.integrations.coinbase.repository.CoinBaseRepositoryInt import org.dash.wallet.integrations.coinbase.ui.convert_currency.model.BaseIdForFiatData import org.dash.wallet.integrations.coinbase.utils.CoinbaseConfig import org.slf4j.LoggerFactory +import java.lang.Exception import javax.inject.Inject data class CoinbaseUIState( @@ -79,19 +80,12 @@ class CoinbaseViewModel @Inject constructor( } suspend fun loginToCoinbase(code: String): Boolean { - when (val response = coinBaseRepository.completeCoinbaseAuthentication(code)) { - is ResponseResource.Success -> { - if (response.value) { - return true - } - } - - is ResponseResource.Failure -> { - log.error("Coinbase login error ${response.errorCode}: ${response.errorBody ?: "empty"}") - } + return try { + coinBaseRepository.completeCoinbaseAuthentication(code) + } catch (ex: Exception) { + log.error("Coinbase login error $ex") + false } - - return false } fun getBaseIdForFiatModel() = viewModelScope.launch { _uiState.update { it.copy(baseIdForFiatModel = BaseIdForFiatData.LoadingState) } @@ -126,9 +120,7 @@ class CoinbaseViewModel @Inject constructor( } suspend fun isInputGreaterThanLimit(amountInDash: Coin): Boolean { - return amountInDash.toPlainString().toDoubleOrZero.compareTo( - coinBaseRepository.getWithdrawalLimitInDash() - ) > 0 + return coinBaseRepository.isInputGreaterThanLimit(amountInDash) } fun logEvent(eventName: String) { diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/TransferDashViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/TransferDashViewModel.kt index 40d9997ee8..39dc45e11b 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/TransferDashViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integrations/coinbase/viewmodels/TransferDashViewModel.kt @@ -146,17 +146,11 @@ class TransferDashViewModel @Inject constructor( } private fun calculateCoinbaseMaxAllowedValue(account:CoinbaseToDashExchangeRateUIModel) { - val maxCoinValue = try { - Coin.parseCoin(account.coinbaseAccount.availableBalance.value) - } catch (x: Exception) { - Coin.ZERO - } - maxForDashCoinBaseAccount = maxCoinValue + maxForDashCoinBaseAccount = account.coinbaseAccount.coinBalance() } private suspend fun isInputGreaterThanLimit(amountInDash: Coin): Boolean { - val withdrawalLimitInDash = coinBaseRepository.getWithdrawalLimitInDash() - return amountInDash.toPlainString().toDoubleOrZero.compareTo(withdrawalLimitInDash) > 0 + return coinBaseRepository.isInputGreaterThanLimit(amountInDash) } suspend fun checkEnteredAmountValue(amountInDash: Coin): SwapValueErrorType { diff --git a/integrations/coinbase/src/test/java/org/dash/wallet/integrations/coinbase/TestUtils.kt b/integrations/coinbase/src/test/java/org/dash/wallet/integrations/coinbase/TestUtils.kt index 0954413395..ac7ff13251 100644 --- a/integrations/coinbase/src/test/java/org/dash/wallet/integrations/coinbase/TestUtils.kt +++ b/integrations/coinbase/src/test/java/org/dash/wallet/integrations/coinbase/TestUtils.kt @@ -53,7 +53,7 @@ object TestUtils { return gson.fromJson(dataSetAsString, resourceGenerator.type) } - val paymentMethodsData = getPaymentMethodsApiResponse().data + val paymentMethodsData = getPaymentMethodsApiResponse().paymentMethods fun getPaymentMethodsApiResponse(): PaymentMethodsResponse { val apiResponse = readFileWithoutNewLineFromResources("payment_methods.json") diff --git a/integrations/coinbase/src/test/resources/payment_methods.json b/integrations/coinbase/src/test/resources/payment_methods.json index 87f50782c3..1f280a00fd 100644 --- a/integrations/coinbase/src/test/resources/payment_methods.json +++ b/integrations/coinbase/src/test/resources/payment_methods.json @@ -1,101 +1,43 @@ { - "data": [ + "payment_methods" : [ { - "id": "453eeb6d-a446-53c7-be6a-c0bb829488f6", - "type": "fiat_account", - "name": "Cash (USD)", - "currency": "USD", - "primary_buy": false, - "primary_sell": true, - "instant_buy": true, - "instant_sell": true, - "created_at": "2018-04-06T15:01:02Z", - "updated_at": "2018-04-06T15:01:02Z", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/453eeb6d-a446-53c7-be6a-c0bb829488f6", - "allow_buy": false, - "allow_sell": true, - "allow_deposit": false, - "allow_withdraw": false, - "fiat_account": { - "id": "7c0bfa90-14d3-52f5-801e-7624a1234567", - "resource": "account", - "resource_path": "/v2/accounts/7c0bfa90-14d3-52f5-801e-7624a7670366" - }, - "verified": true, - "minimum_purchase_amount": { - "amount": "0.00", - "currency": "USD" - } + "updated_at" : "2022-06-30T14:04:13Z", + "allow_deposit" : false, + "id" : "bfb4c087-b26d-5058-b614-4c6f9e411aaf", + "allow_withdraw" : false, + "allow_buy" : false, + "verified" : true, + "created_at" : "2022-06-30T14:04:13Z", + "type" : "COINBASE_FIAT_ACCOUNT", + "currency" : "USD", + "name" : "Cash (USD)", + "allow_sell" : false }, { - "id": "931aa7a2-6500-505b-bf0b-35f031466711", - "type": "ach_bank_account", - "name": "Bank of America - Busi... ********1234", - "currency": "USD", - "primary_buy": true, - "primary_sell": false, - "instant_buy": true, - "instant_sell": false, - "created_at": "2018-03-16T18:50:09Z", - "updated_at": "2018-03-16T18:50:13Z", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/931aa7a2-6500-505b-bf0b-35f031466711", - "allow_buy": true, - "allow_sell": false, - "allow_deposit": true, - "allow_withdraw": true, - "verified": true, - "minimum_purchase_amount": { - "amount": "1.00", - "currency": "USD" - } + "updated_at" : "2022-12-09T06:15:10Z", + "allow_deposit" : false, + "id" : "018b3577-47ce-5d15-8b25-2931166088db", + "allow_withdraw" : false, + "allow_buy" : false, + "verified" : true, + "created_at" : "2022-12-08T09:36:36Z", + "type" : "APPLE_PAY", + "currency" : "USD", + "name" : "Apple Pay", + "allow_sell" : false }, { - "id": "ec4b15d0-a430-5a03-9dbf-bddf3c12bc73", - "type": "worldpay_card", - "name": "0007********2021", - "currency": "USD", - "primary_buy": false, - "primary_sell": false, - "instant_buy": true, - "instant_sell": false, - "created_at": "2021-10-28T17:13:26Z", - "updated_at": "2021-10-28T17:15:58Z", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/ec4b15d0-a430-5a03-9dbf-bddf3c12bc73", - "allow_buy": false, - "allow_sell": false, - "allow_deposit": false, - "allow_withdraw": false, - "verified": true, - "minimum_purchase_amount": { - "amount": "1.00", - "currency": "USD" - } - }, - { - "id": "2e55fc67-db8d-5e14-81f6-25432dc27227", - "type": "paypal_account", - "name": "PayPal - t***t@fakemail.com", - "currency": "USD", - "primary_buy": false, - "primary_sell": false, - "instant_buy": true, - "instant_sell": true, - "created_at": "2021-12-14T18:34:51Z", - "updated_at": "2021-12-14T18:34:51Z", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/2e55fc67-db8d-5e14-81f6-25432dc27227", - "allow_buy": true, - "allow_sell": false, - "allow_deposit": true, - "allow_withdraw": true, - "verified": true, - "minimum_purchase_amount": { - "amount": "1.00", - "currency": "USD" - } + "updated_at" : null, + "allow_deposit" : false, + "id" : "662eec93-2862-438a-b7e1-6b52c01b81b6", + "allow_withdraw" : false, + "allow_buy" : false, + "verified" : true, + "created_at" : "2022-12-09T06:15:02Z", + "type" : "GOOGLE_PAY", + "currency" : "USD", + "name" : "Google Pay", + "allow_sell" : false } ] } \ No newline at end of file diff --git a/integrations/crowdnode/build.gradle b/integrations/crowdnode/build.gradle index fbfa08a380..b7ada16529 100644 --- a/integrations/crowdnode/build.gradle +++ b/integrations/crowdnode/build.gradle @@ -1,7 +1,7 @@ plugins { id 'com.android.library' id 'kotlin-android' - id 'kotlin-kapt' + id 'com.google.devtools.ksp' id 'androidx.navigation.safeargs.kotlin' id 'dagger.hilt.android.plugin' id 'kotlin-parcelize' @@ -11,7 +11,7 @@ plugins { android { defaultConfig { compileSdk 33 - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" @@ -37,10 +37,6 @@ android { namespace 'org.dash.wallet.integrations.crowdnode' } -kapt { - correctErrorTypes true -} - hilt { enableAggregatingTask = true } @@ -65,8 +61,8 @@ dependencies { implementation "com.google.dagger:hilt-android:$hiltVersion" implementation "androidx.hilt:hilt-work:$hiltWorkVersion" implementation "androidx.work:work-runtime-ktx:$workRuntimeVersion" - kapt "com.google.dagger:hilt-android-compiler:$hiltVersion" - kapt "androidx.hilt:hilt-compiler:1.0.0" + ksp "com.google.dagger:hilt-android-compiler:$hiltVersion" + ksp "androidx.hilt:hilt-compiler:$hiltCompilerVersion" // UI implementation "com.google.android.material:material:$materialVersion" diff --git a/integrations/crowdnode/src/main/res/values-id/strings-crowdnode.xml b/integrations/crowdnode/src/main/res/values-id/strings-crowdnode.xml index 3b362d31ab..e63a7812a1 100644 --- a/integrations/crowdnode/src/main/res/values-id/strings-crowdnode.xml +++ b/integrations/crowdnode/src/main/res/values-id/strings-crowdnode.xml @@ -143,6 +143,8 @@ Karena persyaratan layanan CrowdNode, pengguna dapat menarik tidak lebih dari: Penarikan tanpa batas dengan akun online di situs web CrowdNode. Kesalahan ini kemungkinan besar karena melebihi batas penarikan CrowdNode. Coba lagi nanti. + Harap tunggu sebelum memulai penarikan berikutnya + Anda harus menunggu 5 menit sebelum memulai penarikan lainnya per transaksi per jam per 24 jam diff --git a/integrations/crowdnode/src/main/res/values-ko/strings-crowdnode.xml b/integrations/crowdnode/src/main/res/values-ko/strings-crowdnode.xml index 115b4dcf20..7689e3f518 100644 --- a/integrations/crowdnode/src/main/res/values-ko/strings-crowdnode.xml +++ b/integrations/crowdnode/src/main/res/values-ko/strings-crowdnode.xml @@ -143,6 +143,8 @@ 크라우드노드의 서비스 이용 약관에 따라, 사용자들은 다음의 금액 이상을 출금할 수 없습니다: 제한 없이 출금하려면 크라우드노드 웹사이트의 온라인 계정을 활용하십시오. 크라우드노드 출금 한도를 초과하여 에러가 발생한 것으로 보입니다. 잠시 후 다시 시도하십시오. + 다음 출금을 요청하기 전 기다려 주십시오 + 다음 출금을 요청하시기 전 5분을 대기하여야 합니다 거래당 시간당 24시간당 diff --git a/integrations/crowdnode/src/main/res/values-tr/strings-crowdnode.xml b/integrations/crowdnode/src/main/res/values-tr/strings-crowdnode.xml index 47fe945cac..3dd2661b2d 100644 --- a/integrations/crowdnode/src/main/res/values-tr/strings-crowdnode.xml +++ b/integrations/crowdnode/src/main/res/values-tr/strings-crowdnode.xml @@ -143,6 +143,8 @@ CrowdNode\'un hizmet koşulları nedeniyle, kullanıcılar aşağıdakilerden daha fazlasını çekemez: CrowdNode web sitesinde çevrimiçi bir hesapla sınırsız para çekin. Bu hatanın nedeni büyük olasılıkla CrowdNode para çekme limitlerinin aşılmasıdır. Daha sonra tekrar deneyin. + Bir sonraki para çekme işlemini başlatmadan önce lütfen bekleyin + Başka bir para çekme işlemine başlamadan önce 5 dakika beklemeniz gerekir işlem başına saat başına 24 saatte diff --git a/integrations/uphold/ERRORS.md b/integrations/uphold/ERRORS.md index 52debf51c2..c6423f53e8 100644 --- a/integrations/uphold/ERRORS.md +++ b/integrations/uphold/ERRORS.md @@ -154,7 +154,7 @@ Error: 403 ```json { "error": { - "capability": "withdrawals", + "capability": "crypto_withdrawals", "code": "forbidden", "message": "Quote not allowed due to capability constraints", "requirements": [], diff --git a/integrations/uphold/build.gradle b/integrations/uphold/build.gradle index 76d3225d84..48a7fe08e4 100644 --- a/integrations/uphold/build.gradle +++ b/integrations/uphold/build.gradle @@ -1,7 +1,7 @@ plugins { id 'com.android.library' id 'kotlin-android' - id 'kotlin-kapt' + id 'com.google.devtools.ksp' id 'dagger.hilt.android.plugin' id 'org.jlleitschuh.gradle.ktlint' } @@ -10,7 +10,7 @@ android { defaultConfig { compileSdk 33 - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -37,10 +37,6 @@ android { namespace 'org.dash.wallet.integrations.uphold' } -kapt { - correctErrorTypes true -} - hilt { enableAggregatingTask = true } @@ -65,7 +61,7 @@ dependencies { // DI implementation "com.google.dagger:hilt-android:$hiltVersion" - kapt "com.google.dagger:hilt-compiler:$hiltVersion" + ksp "com.google.dagger:hilt-compiler:$hiltVersion" // JWT implementation 'io.jsonwebtoken:jjwt-api:0.11.5' diff --git a/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/TopperClient.kt b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/TopperClient.kt index ce6b8f9f7b..c95c72c711 100644 --- a/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/TopperClient.kt +++ b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/TopperClient.kt @@ -17,6 +17,7 @@ package org.dash.wallet.integrations.uphold.api +import androidx.annotation.VisibleForTesting import com.google.gson.Gson import io.jsonwebtoken.Jwts import io.jsonwebtoken.SignatureAlgorithm @@ -26,6 +27,8 @@ import org.bitcoinj.core.Address import org.dash.wallet.common.util.Constants import org.dash.wallet.common.util.get import org.dash.wallet.integrations.uphold.data.SupportedTopperAssets +import org.dash.wallet.integrations.uphold.data.SupportedTopperPaymentMethods +import org.dash.wallet.integrations.uphold.data.TopperPaymentMethod import org.slf4j.LoggerFactory import org.spongycastle.asn1.ASN1Sequence import org.spongycastle.asn1.pkcs.PrivateKeyInfo @@ -35,6 +38,9 @@ import org.spongycastle.asn1.x9.X9ObjectIdentifiers import org.spongycastle.jce.provider.BouncyCastleProvider import org.spongycastle.openssl.jcajce.JcaPEMKeyConverter import java.lang.Exception +import java.math.BigDecimal +import java.math.MathContext +import java.math.RoundingMode import java.util.Date import java.util.SortedSet import java.util.UUID @@ -44,9 +50,11 @@ class TopperClient @Inject constructor( private val httpClient: OkHttpClient ) { companion object { + private const val DEFAULT_FIAT_AMOUNT = 100 // Target 100 USD, or similar in other currencies private const val BASE_URL = "https://app.topperpay.com/" private const val SANDBOX_URL = "https://app.sandbox.topperpay.com/" private const val SUPPORTED_ASSETS_URL = "https://api.topperpay.com/assets/crypto-onramp" + const val SUPPORTED_PAYMENT_METHODS_URL = "https://api.topperpay.com/payment-methods/crypto-onramp" private val log = LoggerFactory.getLogger(TopperClient::class.java) } @@ -55,6 +63,7 @@ class TopperClient @Inject constructor( private lateinit var privateKey: String private var isSandbox: Boolean = false private var supportedAssets: SortedSet = sortedSetOf() + private var supportedPaymentMethods: List = listOf() val hasValidCredentials: Boolean get() = keyId.isNotEmpty() && widgetId.isNotEmpty() && privateKey.isNotEmpty() @@ -98,7 +107,18 @@ class TopperClient @Inject constructor( } } - fun isSupportedAsset(asset: String): Boolean { + suspend fun refreshPaymentMethods() { + supportedPaymentMethods = try { + val response = httpClient.get(SUPPORTED_PAYMENT_METHODS_URL) + val root = Gson().fromJson(response.body?.string(), SupportedTopperPaymentMethods::class.java) + root.paymentMethods.filter { it.type == "credit-card" && it.billingAsset == "USD" } + } catch (ex: Exception) { + log.error("Failed to get supported assets from Topper", ex) + listOf() + } + } + + private fun isSupportedAsset(asset: String): Boolean { return supportedAssets.contains(asset) } @@ -115,6 +135,8 @@ class TopperClient @Inject constructor( setProvider(BouncyCastleProvider()) }.getPrivateKey(PrivateKeyInfo(algId, pKey)) + val defaultValue = getDefaultValue(sourceAsset, supportedPaymentMethods) + // docs: https://docs.topperpay.com/flows/crypto-onramp return Jwts.builder() .setHeaderParam("kid", keyId) .setHeaderParam("typ", "JWT") @@ -123,13 +145,15 @@ class TopperClient @Inject constructor( .setIssuedAt(Date()) .claim( "source", - mapOf("asset" to sourceAsset) + mapOf( + "asset" to sourceAsset, + "amount" to defaultValue.toString() + ) ) .claim( "target", mapOf( "address" to receiverAddress.toString(), - "amount" to "1", "asset" to "DASH", "network" to "dash", "priority" to "fast", @@ -139,4 +163,38 @@ class TopperClient @Inject constructor( .signWith(key, SignatureAlgorithm.ES256) .compact() } + + @VisibleForTesting + fun getDefaultValue(sourceAsset: String, paymentMethods: List): Int { + if (paymentMethods.isNotEmpty()) { + val paymentMethod = paymentMethods.find { it.type == "credit-card" && it.billingAsset == "USD" } + + val minimumUSD = paymentMethod?.limits?.find { + it.asset == "USD" + }?.minimum?.toBigDecimal() + val minimumMultplier = minimumUSD?.let { BigDecimal(DEFAULT_FIAT_AMOUNT).setScale(3) / minimumUSD } + + if (minimumMultplier != null) { + val minimum = paymentMethod.limits.find { it.asset == sourceAsset }?.minimum?.toBigDecimal() + // check if the minimum is greater than the default amount + val amount = if (minimumMultplier > BigDecimal.ONE) { + minimum?.multiply(minimumMultplier) + } else { + minimum?.round(MathContext(2, RoundingMode.CEILING)) + } + if (minimum != null && amount != null) { + // This section will round the amount up + // 1. amounts below 100 will be rounded up with a precision of 1: 92 becomes 90 + // 2. amounts above 100 will be rounded up with a precision of 2: 15070 becomes 15000 + val precision = if (amount > BigDecimal.valueOf(100)) { + 2 + } else { + 1 + } + return amount.round(MathContext(precision, RoundingMode.HALF_UP)).toInt() + } + } + } + return DEFAULT_FIAT_AMOUNT + } } diff --git a/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/UpholdClientExt.kt b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/UpholdClientExt.kt index fd41820bdd..a727a8a997 100644 --- a/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/UpholdClientExt.kt +++ b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/api/UpholdClientExt.kt @@ -150,7 +150,7 @@ suspend fun UpholdClient.revokeAccessToken() { } suspend fun UpholdClient.checkCapabilities() { - val operation = "withdrawals" + val operation = "crypto_withdrawals" val response = service.getCapabilities(operation) response.ensureSuccessful() val capability = response.body() @@ -207,7 +207,7 @@ fun UpholdClient.createDashAddress(cardId: String) { } fun UpholdClient.getWithdrawalRequirements(): List { - val result = requirements["withdrawals"] + val result = requirements["crypto_withdrawals"] return result ?: emptyList() } diff --git a/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/data/SupportedTopperPaymentMethods.kt b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/data/SupportedTopperPaymentMethods.kt new file mode 100644 index 0000000000..6eaa2b534d --- /dev/null +++ b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/data/SupportedTopperPaymentMethods.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2024 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.uphold.data + +data class SupportedTopperPaymentMethods( + val paymentMethods: List +) + +data class TopperPaymentMethod( + val billingAsset: String, + val countries: List, + val limits: List, + val network: String, + val type: String + +) + +data class PaymentMethodLimit( + val asset: String, + val maximum: String, + val minimum: String +) diff --git a/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/data/UpholdApiException.java b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/data/UpholdApiException.java index c541d9c304..97d0b3d8e8 100644 --- a/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/data/UpholdApiException.java +++ b/integrations/uphold/src/main/java/org/dash/wallet/integrations/uphold/data/UpholdApiException.java @@ -236,7 +236,7 @@ private Date convertISO8601Date(String date) { // 403 // { - // "capability":"withdrawals", + // "capability":"crypto_withdrawals", // "code":"forbidden", // "message":"Quote not allowed due to capability constraints", // "requirements":[], diff --git a/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/TopperClientTest.kt b/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/TopperClientTest.kt new file mode 100644 index 0000000000..4288a54492 --- /dev/null +++ b/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/TopperClientTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024 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.uphold + +import com.google.gson.Gson +import okhttp3.OkHttpClient +import org.dash.wallet.integrations.uphold.api.TopperClient +import org.dash.wallet.integrations.uphold.data.SupportedTopperPaymentMethods +import org.dash.wallet.integrations.uphold.data.TopperPaymentMethod +import org.junit.Assert.assertEquals +import org.junit.Test +import org.mockito.kotlin.mock +import java.math.BigDecimal + +class TopperClientTest { + + private val mockHttpClient: OkHttpClient = mock() + private val topperClient = TopperClient(mockHttpClient) + private val paymentMethodsRawResult: String? + private val paymentMethods: List + private val paymentMethodsWithHigherLimits: List + init { + this::class.java.getResourceAsStream("payment-methods.json").use { stream -> + paymentMethodsRawResult = stream?.let { String(it.readBytes()) } + paymentMethods = Gson().fromJson( + paymentMethodsRawResult, + SupportedTopperPaymentMethods::class.java + )?.paymentMethods!! + paymentMethodsWithHigherLimits = scaleLimits(20) + } + } + + private fun scaleLimits(factor: Int) = paymentMethods.map { method -> + method.copy( + limits = method.limits.map { limit -> + limit.copy( + minimum = (limit.minimum.toBigDecimal() * BigDecimal(factor)).toString(), + maximum = (limit.maximum.toBigDecimal() * BigDecimal(factor)).toString() + ) + } + ) + } + + @Test + fun defaultValueTest() { + assertEquals(100, topperClient.getDefaultValue("USD", paymentMethods)) + assertEquals(90, topperClient.getDefaultValue("EUR", paymentMethods)) + assertEquals(80, topperClient.getDefaultValue("GBP", paymentMethods)) + assertEquals(15000, topperClient.getDefaultValue("JPY", paymentMethods)) + + // the limits are doubled + assertEquals(100, topperClient.getDefaultValue("USD", scaleLimits(2))) + // the limits are 9x higher + assertEquals(100, topperClient.getDefaultValue("USD", scaleLimits(9))) + // the limits are 11x higher, which means that the minimum is higher than the default + assertEquals(110, topperClient.getDefaultValue("USD", scaleLimits(11))) + + // the minimum is $200, while the default value is $100 + assertEquals(200, topperClient.getDefaultValue("USD", paymentMethodsWithHigherLimits)) + assertEquals(31000, topperClient.getDefaultValue("JPY", paymentMethodsWithHigherLimits)) + } +} diff --git a/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdClientTest.kt b/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdClientTest.kt index 2fe64121c1..268e1841fa 100644 --- a/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdClientTest.kt +++ b/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdClientTest.kt @@ -122,7 +122,7 @@ class UpholdClientTest { listOf() ) - val actualResponse = api.getCapabilities("withdrawals") + val actualResponse = api.getCapabilities("crypto_withdrawals") assertEquals(expected, actualResponse.body()) } diff --git a/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdErrorTest.kt b/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdErrorTest.kt index 2bedec5019..4f70b44ef6 100644 --- a/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdErrorTest.kt +++ b/integrations/uphold/src/test/java/org/dash/wallet/integrations/uphold/UpholdErrorTest.kt @@ -61,7 +61,7 @@ class UpholdErrorTest { val thirdError = """ { - "capability": "withdrawals", + "capability": "crypto_withdrawals", "code": "forbidden", "message": "Quote not allowed due to capability constraints", "requirements": [], diff --git a/integrations/uphold/src/test/resources/org/dash/wallet/integrations/uphold/payment-methods.json b/integrations/uphold/src/test/resources/org/dash/wallet/integrations/uphold/payment-methods.json new file mode 100644 index 0000000000..6a8130ae85 --- /dev/null +++ b/integrations/uphold/src/test/resources/org/dash/wallet/integrations/uphold/payment-methods.json @@ -0,0 +1,2070 @@ +{ + "paymentMethods": [ + { + "billingAsset": "EUR", + "countries": [ + "AT", + "BE", + "BG", + "CY", + "CZ", + "DK", + "EE", + "ES", + "FI", + "FR", + "GR", + "HR", + "HU", + "IE", + "IS", + "IT", + "LI", + "LT", + "LU", + "LV", + "MC", + "MT", + "NO", + "PL", + "PT", + "RO", + "SE", + "SI", + "SK", + "SM" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9956.98", + "minimum": "39.83" + }, + { + "asset": "ARS", + "maximum": "2280242.04", + "minimum": "9120.97" + }, + { + "asset": "AUD", + "maximum": "4142.50", + "minimum": "16.58" + }, + { + "asset": "BRL", + "maximum": "13375.19", + "minimum": "53.51" + }, + { + "asset": "CAD", + "maximum": "3668.07", + "minimum": "14.68" + }, + { + "asset": "CHF", + "maximum": "2383.33", + "minimum": "9.54" + }, + { + "asset": "CNY", + "maximum": "19533.79", + "minimum": "78.14" + }, + { + "asset": "CZK", + "maximum": "63308.55", + "minimum": "253.24" + }, + { + "asset": "DKK", + "maximum": "18634.50", + "minimum": "74.54" + }, + { + "asset": "EUR", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "GBP", + "maximum": "2139.35", + "minimum": "8.56" + }, + { + "asset": "HKD", + "maximum": "21216.41", + "minimum": "84.87" + }, + { + "asset": "HUF", + "maximum": "977134.88", + "minimum": "3908.54" + }, + { + "asset": "ILS", + "maximum": "9791.68", + "minimum": "39.17" + }, + { + "asset": "INR", + "maximum": "224811.64", + "minimum": "899.25" + }, + { + "asset": "JPY", + "maximum": "407956", + "minimum": "1632" + }, + { + "asset": "KES", + "maximum": "395788.92", + "minimum": "1583.16" + }, + { + "asset": "MXN", + "maximum": "46277.72", + "minimum": "185.12" + }, + { + "asset": "NOK", + "maximum": "28559.77", + "minimum": "114.24" + }, + { + "asset": "NZD", + "maximum": "4391.95", + "minimum": "17.57" + }, + { + "asset": "PHP", + "maximum": "152050.35", + "minimum": "608.21" + }, + { + "asset": "PLN", + "maximum": "10765.41", + "minimum": "43.07" + }, + { + "asset": "RON", + "maximum": "12423.47", + "minimum": "49.70" + }, + { + "asset": "SEK", + "maximum": "27943.90", + "minimum": "111.78" + }, + { + "asset": "SGD", + "maximum": "3643.45", + "minimum": "14.58" + }, + { + "asset": "USD", + "maximum": "2710.87", + "minimum": "10.85" + } + ], + "network": "apple-pay", + "type": "apple-pay" + }, + { + "billingAsset": "GBP", + "countries": [ + "GB" + ], + "limits": [ + { + "asset": "AED", + "maximum": "11635.49", + "minimum": "46.55" + }, + { + "asset": "ARS", + "maximum": "2664635.72", + "minimum": "10658.55" + }, + { + "asset": "AUD", + "maximum": "4840.83", + "minimum": "19.37" + }, + { + "asset": "BRL", + "maximum": "15629.92", + "minimum": "62.52" + }, + { + "asset": "CAD", + "maximum": "4286.42", + "minimum": "17.15" + }, + { + "asset": "CHF", + "maximum": "2785.10", + "minimum": "11.15" + }, + { + "asset": "CNY", + "maximum": "22826.72", + "minimum": "91.31" + }, + { + "asset": "CZK", + "maximum": "73980.84", + "minimum": "295.93" + }, + { + "asset": "DKK", + "maximum": "21775.82", + "minimum": "87.11" + }, + { + "asset": "EUR", + "maximum": "2921.43", + "minimum": "11.69" + }, + { + "asset": "GBP", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "HKD", + "maximum": "24792.98", + "minimum": "99.18" + }, + { + "asset": "HUF", + "maximum": "1141856.20", + "minimum": "4567.43" + }, + { + "asset": "ILS", + "maximum": "11442.32", + "minimum": "45.77" + }, + { + "asset": "INR", + "maximum": "262709.44", + "minimum": "1050.84" + }, + { + "asset": "JPY", + "maximum": "476728", + "minimum": "1907" + }, + { + "asset": "KES", + "maximum": "462509.36", + "minimum": "1850.04" + }, + { + "asset": "MXN", + "maximum": "54079.03", + "minimum": "216.32" + }, + { + "asset": "NOK", + "maximum": "33374.25", + "minimum": "133.50" + }, + { + "asset": "NZD", + "maximum": "5132.33", + "minimum": "20.53" + }, + { + "asset": "PHP", + "maximum": "177682.37", + "minimum": "710.73" + }, + { + "asset": "PLN", + "maximum": "12580.20", + "minimum": "50.33" + }, + { + "asset": "RON", + "maximum": "14517.77", + "minimum": "58.08" + }, + { + "asset": "SEK", + "maximum": "32654.56", + "minimum": "130.62" + }, + { + "asset": "SGD", + "maximum": "4257.65", + "minimum": "17.04" + }, + { + "asset": "USD", + "maximum": "3167.86", + "minimum": "12.68" + } + ], + "network": "apple-pay", + "type": "apple-pay" + }, + { + "billingAsset": "USD", + "countries": [ + "AE", + "AR", + "AU", + "BH", + "BR", + "CA", + "CH", + "CR", + "FO", + "GG", + "GL", + "HK", + "IL", + "IM", + "JE", + "JP", + "MO", + "MX", + "NZ", + "PE", + "QA", + "SA", + "SG", + "US", + "VA", + "ZA" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9182.44", + "minimum": "36.73" + }, + { + "asset": "ARS", + "maximum": "2102865.38", + "minimum": "8411.47" + }, + { + "asset": "AUD", + "maximum": "3820.26", + "minimum": "15.29" + }, + { + "asset": "BRL", + "maximum": "12334.75", + "minimum": "49.34" + }, + { + "asset": "CAD", + "maximum": "3382.73", + "minimum": "13.54" + }, + { + "asset": "CHF", + "maximum": "2197.93", + "minimum": "8.80" + }, + { + "asset": "CNY", + "maximum": "18014.29", + "minimum": "72.06" + }, + { + "asset": "CZK", + "maximum": "58383.87", + "minimum": "233.54" + }, + { + "asset": "DKK", + "maximum": "17184.95", + "minimum": "68.74" + }, + { + "asset": "EUR", + "maximum": "2305.52", + "minimum": "9.23" + }, + { + "asset": "GBP", + "maximum": "1972.93", + "minimum": "7.90" + }, + { + "asset": "HKD", + "maximum": "19566.01", + "minimum": "78.27" + }, + { + "asset": "HUF", + "maximum": "901125.00", + "minimum": "3604.51" + }, + { + "asset": "ILS", + "maximum": "9030.00", + "minimum": "36.13" + }, + { + "asset": "INR", + "maximum": "207323.87", + "minimum": "829.30" + }, + { + "asset": "JPY", + "maximum": "376222", + "minimum": "1505" + }, + { + "asset": "KES", + "maximum": "365001.08", + "minimum": "1460.01" + }, + { + "asset": "MXN", + "maximum": "42677.85", + "minimum": "170.72" + }, + { + "asset": "NOK", + "maximum": "26338.15", + "minimum": "105.36" + }, + { + "asset": "NZD", + "maximum": "4050.31", + "minimum": "16.21" + }, + { + "asset": "PHP", + "maximum": "140222.58", + "minimum": "560.90" + }, + { + "asset": "PLN", + "maximum": "9927.98", + "minimum": "39.72" + }, + { + "asset": "RON", + "maximum": "11457.07", + "minimum": "45.83" + }, + { + "asset": "SEK", + "maximum": "25770.18", + "minimum": "103.09" + }, + { + "asset": "SGD", + "maximum": "3360.03", + "minimum": "13.45" + }, + { + "asset": "USD", + "maximum": "2500.00", + "minimum": "10.00" + } + ], + "network": "apple-pay", + "type": "apple-pay" + }, + { + "billingAsset": "EUR", + "countries": [ + "AD", + "AT", + "AX", + "BE", + "BG", + "BQ", + "BQ", + "CY", + "CZ", + "DK", + "EE", + "ES", + "FI", + "FR", + "GF", + "GP", + "GR", + "HR", + "HU", + "IE", + "IS", + "IT", + "LI", + "LT", + "LU", + "LV", + "MC", + "MF", + "MQ", + "MT", + "NO", + "PL", + "PT", + "RE", + "RO", + "SE", + "SI", + "SK", + "SM", + "YT" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9956.98", + "minimum": "39.83" + }, + { + "asset": "ARS", + "maximum": "2280242.04", + "minimum": "9120.97" + }, + { + "asset": "AUD", + "maximum": "4142.50", + "minimum": "16.58" + }, + { + "asset": "BRL", + "maximum": "13375.19", + "minimum": "53.51" + }, + { + "asset": "CAD", + "maximum": "3668.07", + "minimum": "14.68" + }, + { + "asset": "CHF", + "maximum": "2383.33", + "minimum": "9.54" + }, + { + "asset": "CNY", + "maximum": "19533.79", + "minimum": "78.14" + }, + { + "asset": "CZK", + "maximum": "63308.55", + "minimum": "253.24" + }, + { + "asset": "DKK", + "maximum": "18634.50", + "minimum": "74.54" + }, + { + "asset": "EUR", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "GBP", + "maximum": "2139.35", + "minimum": "8.56" + }, + { + "asset": "HKD", + "maximum": "21216.41", + "minimum": "84.87" + }, + { + "asset": "HUF", + "maximum": "977134.88", + "minimum": "3908.54" + }, + { + "asset": "ILS", + "maximum": "9791.68", + "minimum": "39.17" + }, + { + "asset": "INR", + "maximum": "224811.64", + "minimum": "899.25" + }, + { + "asset": "JPY", + "maximum": "407956", + "minimum": "1632" + }, + { + "asset": "KES", + "maximum": "395788.92", + "minimum": "1583.16" + }, + { + "asset": "MXN", + "maximum": "46277.72", + "minimum": "185.12" + }, + { + "asset": "NOK", + "maximum": "28559.77", + "minimum": "114.24" + }, + { + "asset": "NZD", + "maximum": "4391.95", + "minimum": "17.57" + }, + { + "asset": "PHP", + "maximum": "152050.35", + "minimum": "608.21" + }, + { + "asset": "PLN", + "maximum": "10765.41", + "minimum": "43.07" + }, + { + "asset": "RON", + "maximum": "12423.47", + "minimum": "49.70" + }, + { + "asset": "SEK", + "maximum": "27943.90", + "minimum": "111.78" + }, + { + "asset": "SGD", + "maximum": "3643.45", + "minimum": "14.58" + }, + { + "asset": "USD", + "maximum": "2710.87", + "minimum": "10.85" + } + ], + "network": "card", + "type": "credit-card" + }, + { + "billingAsset": "GBP", + "countries": [ + "GB" + ], + "limits": [ + { + "asset": "AED", + "maximum": "11635.49", + "minimum": "46.55" + }, + { + "asset": "ARS", + "maximum": "2664635.72", + "minimum": "10658.55" + }, + { + "asset": "AUD", + "maximum": "4840.83", + "minimum": "19.37" + }, + { + "asset": "BRL", + "maximum": "15629.92", + "minimum": "62.52" + }, + { + "asset": "CAD", + "maximum": "4286.42", + "minimum": "17.15" + }, + { + "asset": "CHF", + "maximum": "2785.10", + "minimum": "11.15" + }, + { + "asset": "CNY", + "maximum": "22826.72", + "minimum": "91.31" + }, + { + "asset": "CZK", + "maximum": "73980.84", + "minimum": "295.93" + }, + { + "asset": "DKK", + "maximum": "21775.82", + "minimum": "87.11" + }, + { + "asset": "EUR", + "maximum": "2921.43", + "minimum": "11.69" + }, + { + "asset": "GBP", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "HKD", + "maximum": "24792.98", + "minimum": "99.18" + }, + { + "asset": "HUF", + "maximum": "1141856.20", + "minimum": "4567.43" + }, + { + "asset": "ILS", + "maximum": "11442.32", + "minimum": "45.77" + }, + { + "asset": "INR", + "maximum": "262709.44", + "minimum": "1050.84" + }, + { + "asset": "JPY", + "maximum": "476728", + "minimum": "1907" + }, + { + "asset": "KES", + "maximum": "462509.36", + "minimum": "1850.04" + }, + { + "asset": "MXN", + "maximum": "54079.03", + "minimum": "216.32" + }, + { + "asset": "NOK", + "maximum": "33374.25", + "minimum": "133.50" + }, + { + "asset": "NZD", + "maximum": "5132.33", + "minimum": "20.53" + }, + { + "asset": "PHP", + "maximum": "177682.37", + "minimum": "710.73" + }, + { + "asset": "PLN", + "maximum": "12580.20", + "minimum": "50.33" + }, + { + "asset": "RON", + "maximum": "14517.77", + "minimum": "58.08" + }, + { + "asset": "SEK", + "maximum": "32654.56", + "minimum": "130.62" + }, + { + "asset": "SGD", + "maximum": "4257.65", + "minimum": "17.04" + }, + { + "asset": "USD", + "maximum": "3167.86", + "minimum": "12.68" + } + ], + "network": "card", + "type": "credit-card" + }, + { + "billingAsset": "USD", + "countries": [ + "AE", + "AG", + "AR", + "AU", + "BH", + "BL", + "BM", + "BN", + "BO", + "BR", + "BS", + "BT", + "BW", + "BZ", + "CA", + "CC", + "CH", + "CK", + "CL", + "CR", + "CV", + "CW", + "CX", + "DJ", + "DM", + "EH", + "FK", + "FM", + "FO", + "GA", + "GD", + "GG", + "GL", + "GM", + "GS", + "GU", + "GY", + "HK", + "HN", + "IL", + "IM", + "IO", + "JE", + "JP", + "KI", + "KM", + "KN", + "KR", + "KW", + "KY", + "LC", + "LS", + "MG", + "MH", + "MO", + "MP", + "MS", + "MU", + "MV", + "MW", + "MX", + "MY", + "MZ", + "NA", + "NC", + "NE", + "NF", + "NR", + "NU", + "NZ", + "OM", + "PE", + "PF", + "PG", + "PH", + "PM", + "PN", + "PR", + "PY", + "QA", + "SA", + "SB", + "SC", + "SG", + "SH", + "SJ", + "SN", + "ST", + "SV", + "SX", + "SZ", + "TC", + "TH", + "TK", + "TL", + "TO", + "TV", + "US", + "UY", + "VA", + "VC", + "VG", + "WF", + "ZA", + "ZM" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9182.44", + "minimum": "36.73" + }, + { + "asset": "ARS", + "maximum": "2102865.38", + "minimum": "8411.47" + }, + { + "asset": "AUD", + "maximum": "3820.26", + "minimum": "15.29" + }, + { + "asset": "BRL", + "maximum": "12334.75", + "minimum": "49.34" + }, + { + "asset": "CAD", + "maximum": "3382.73", + "minimum": "13.54" + }, + { + "asset": "CHF", + "maximum": "2197.93", + "minimum": "8.80" + }, + { + "asset": "CNY", + "maximum": "18014.29", + "minimum": "72.06" + }, + { + "asset": "CZK", + "maximum": "58383.87", + "minimum": "233.54" + }, + { + "asset": "DKK", + "maximum": "17184.95", + "minimum": "68.74" + }, + { + "asset": "EUR", + "maximum": "2305.52", + "minimum": "9.23" + }, + { + "asset": "GBP", + "maximum": "1972.93", + "minimum": "7.90" + }, + { + "asset": "HKD", + "maximum": "19566.01", + "minimum": "78.27" + }, + { + "asset": "HUF", + "maximum": "901125.00", + "minimum": "3604.51" + }, + { + "asset": "ILS", + "maximum": "9030.00", + "minimum": "36.13" + }, + { + "asset": "INR", + "maximum": "207323.87", + "minimum": "829.30" + }, + { + "asset": "JPY", + "maximum": "376222", + "minimum": "1505" + }, + { + "asset": "KES", + "maximum": "365001.08", + "minimum": "1460.01" + }, + { + "asset": "MXN", + "maximum": "42677.85", + "minimum": "170.72" + }, + { + "asset": "NOK", + "maximum": "26338.15", + "minimum": "105.36" + }, + { + "asset": "NZD", + "maximum": "4050.31", + "minimum": "16.21" + }, + { + "asset": "PHP", + "maximum": "140222.58", + "minimum": "560.90" + }, + { + "asset": "PLN", + "maximum": "9927.98", + "minimum": "39.72" + }, + { + "asset": "RON", + "maximum": "11457.07", + "minimum": "45.83" + }, + { + "asset": "SEK", + "maximum": "25770.18", + "minimum": "103.09" + }, + { + "asset": "SGD", + "maximum": "3360.03", + "minimum": "13.45" + }, + { + "asset": "USD", + "maximum": "2500.00", + "minimum": "10.00" + } + ], + "network": "card", + "type": "credit-card" + }, + { + "billingAsset": "EUR", + "countries": [ + "AD", + "AT", + "AX", + "BE", + "BG", + "BQ", + "CY", + "CZ", + "DK", + "EE", + "ES", + "FI", + "FR", + "GF", + "GP", + "GR", + "HR", + "HU", + "IE", + "IS", + "IT", + "LI", + "LT", + "LU", + "LV", + "MC", + "MF", + "MQ", + "MT", + "NO", + "PL", + "PT", + "RE", + "RO", + "SE", + "SI", + "SK", + "SM", + "YT" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9956.98", + "minimum": "39.83" + }, + { + "asset": "ARS", + "maximum": "2280242.04", + "minimum": "9120.97" + }, + { + "asset": "AUD", + "maximum": "4142.50", + "minimum": "16.58" + }, + { + "asset": "BRL", + "maximum": "13375.19", + "minimum": "53.51" + }, + { + "asset": "CAD", + "maximum": "3668.07", + "minimum": "14.68" + }, + { + "asset": "CHF", + "maximum": "2383.33", + "minimum": "9.54" + }, + { + "asset": "CNY", + "maximum": "19533.79", + "minimum": "78.14" + }, + { + "asset": "CZK", + "maximum": "63308.55", + "minimum": "253.24" + }, + { + "asset": "DKK", + "maximum": "18634.50", + "minimum": "74.54" + }, + { + "asset": "EUR", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "GBP", + "maximum": "2139.35", + "minimum": "8.56" + }, + { + "asset": "HKD", + "maximum": "21216.41", + "minimum": "84.87" + }, + { + "asset": "HUF", + "maximum": "977134.88", + "minimum": "3908.54" + }, + { + "asset": "ILS", + "maximum": "9791.68", + "minimum": "39.17" + }, + { + "asset": "INR", + "maximum": "224811.64", + "minimum": "899.25" + }, + { + "asset": "JPY", + "maximum": "407956", + "minimum": "1632" + }, + { + "asset": "KES", + "maximum": "395788.92", + "minimum": "1583.16" + }, + { + "asset": "MXN", + "maximum": "46277.72", + "minimum": "185.12" + }, + { + "asset": "NOK", + "maximum": "28559.77", + "minimum": "114.24" + }, + { + "asset": "NZD", + "maximum": "4391.95", + "minimum": "17.57" + }, + { + "asset": "PHP", + "maximum": "152050.35", + "minimum": "608.21" + }, + { + "asset": "PLN", + "maximum": "10765.41", + "minimum": "43.07" + }, + { + "asset": "RON", + "maximum": "12423.47", + "minimum": "49.70" + }, + { + "asset": "SEK", + "maximum": "27943.90", + "minimum": "111.78" + }, + { + "asset": "SGD", + "maximum": "3643.45", + "minimum": "14.58" + }, + { + "asset": "USD", + "maximum": "2710.87", + "minimum": "10.85" + } + ], + "network": "card", + "type": "debit-card" + }, + { + "billingAsset": "GBP", + "countries": [ + "GB" + ], + "limits": [ + { + "asset": "AED", + "maximum": "11635.49", + "minimum": "46.55" + }, + { + "asset": "ARS", + "maximum": "2664635.72", + "minimum": "10658.55" + }, + { + "asset": "AUD", + "maximum": "4840.83", + "minimum": "19.37" + }, + { + "asset": "BRL", + "maximum": "15629.92", + "minimum": "62.52" + }, + { + "asset": "CAD", + "maximum": "4286.42", + "minimum": "17.15" + }, + { + "asset": "CHF", + "maximum": "2785.10", + "minimum": "11.15" + }, + { + "asset": "CNY", + "maximum": "22826.72", + "minimum": "91.31" + }, + { + "asset": "CZK", + "maximum": "73980.84", + "minimum": "295.93" + }, + { + "asset": "DKK", + "maximum": "21775.82", + "minimum": "87.11" + }, + { + "asset": "EUR", + "maximum": "2921.43", + "minimum": "11.69" + }, + { + "asset": "GBP", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "HKD", + "maximum": "24792.98", + "minimum": "99.18" + }, + { + "asset": "HUF", + "maximum": "1141856.20", + "minimum": "4567.43" + }, + { + "asset": "ILS", + "maximum": "11442.32", + "minimum": "45.77" + }, + { + "asset": "INR", + "maximum": "262709.44", + "minimum": "1050.84" + }, + { + "asset": "JPY", + "maximum": "476728", + "minimum": "1907" + }, + { + "asset": "KES", + "maximum": "462509.36", + "minimum": "1850.04" + }, + { + "asset": "MXN", + "maximum": "54079.03", + "minimum": "216.32" + }, + { + "asset": "NOK", + "maximum": "33374.25", + "minimum": "133.50" + }, + { + "asset": "NZD", + "maximum": "5132.33", + "minimum": "20.53" + }, + { + "asset": "PHP", + "maximum": "177682.37", + "minimum": "710.73" + }, + { + "asset": "PLN", + "maximum": "12580.20", + "minimum": "50.33" + }, + { + "asset": "RON", + "maximum": "14517.77", + "minimum": "58.08" + }, + { + "asset": "SEK", + "maximum": "32654.56", + "minimum": "130.62" + }, + { + "asset": "SGD", + "maximum": "4257.65", + "minimum": "17.04" + }, + { + "asset": "USD", + "maximum": "3167.86", + "minimum": "12.68" + } + ], + "network": "card", + "type": "debit-card" + }, + { + "billingAsset": "USD", + "countries": [ + "AE", + "AG", + "AR", + "AU", + "BH", + "BL", + "BM", + "BN", + "BO", + "BR", + "BS", + "BT", + "BW", + "BZ", + "CA", + "CC", + "CH", + "CK", + "CL", + "CR", + "CV", + "CW", + "CX", + "DJ", + "DM", + "EH", + "FK", + "FM", + "FO", + "GA", + "GD", + "GG", + "GL", + "GM", + "GS", + "GU", + "GY", + "HK", + "HN", + "IL", + "IM", + "IO", + "JE", + "JP", + "KI", + "KM", + "KN", + "KR", + "KW", + "KY", + "LC", + "LS", + "MG", + "MH", + "MO", + "MP", + "MS", + "MU", + "MV", + "MW", + "MX", + "MY", + "MZ", + "NA", + "NC", + "NE", + "NF", + "NR", + "NU", + "NZ", + "OM", + "PE", + "PF", + "PG", + "PH", + "PM", + "PN", + "PR", + "PY", + "QA", + "SA", + "SB", + "SC", + "SG", + "SH", + "SJ", + "SN", + "ST", + "SV", + "SX", + "SZ", + "TC", + "TH", + "TK", + "TL", + "TO", + "TV", + "US", + "UY", + "VA", + "VC", + "VG", + "WF", + "ZA", + "ZM" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9182.44", + "minimum": "36.73" + }, + { + "asset": "ARS", + "maximum": "2102865.38", + "minimum": "8411.47" + }, + { + "asset": "AUD", + "maximum": "3820.26", + "minimum": "15.29" + }, + { + "asset": "BRL", + "maximum": "12334.75", + "minimum": "49.34" + }, + { + "asset": "CAD", + "maximum": "3382.73", + "minimum": "13.54" + }, + { + "asset": "CHF", + "maximum": "2197.93", + "minimum": "8.80" + }, + { + "asset": "CNY", + "maximum": "18014.29", + "minimum": "72.06" + }, + { + "asset": "CZK", + "maximum": "58383.87", + "minimum": "233.54" + }, + { + "asset": "DKK", + "maximum": "17184.95", + "minimum": "68.74" + }, + { + "asset": "EUR", + "maximum": "2305.52", + "minimum": "9.23" + }, + { + "asset": "GBP", + "maximum": "1972.93", + "minimum": "7.90" + }, + { + "asset": "HKD", + "maximum": "19566.01", + "minimum": "78.27" + }, + { + "asset": "HUF", + "maximum": "901125.00", + "minimum": "3604.51" + }, + { + "asset": "ILS", + "maximum": "9030.00", + "minimum": "36.13" + }, + { + "asset": "INR", + "maximum": "207323.87", + "minimum": "829.30" + }, + { + "asset": "JPY", + "maximum": "376222", + "minimum": "1505" + }, + { + "asset": "KES", + "maximum": "365001.08", + "minimum": "1460.01" + }, + { + "asset": "MXN", + "maximum": "42677.85", + "minimum": "170.72" + }, + { + "asset": "NOK", + "maximum": "26338.15", + "minimum": "105.36" + }, + { + "asset": "NZD", + "maximum": "4050.31", + "minimum": "16.21" + }, + { + "asset": "PHP", + "maximum": "140222.58", + "minimum": "560.90" + }, + { + "asset": "PLN", + "maximum": "9927.98", + "minimum": "39.72" + }, + { + "asset": "RON", + "maximum": "11457.07", + "minimum": "45.83" + }, + { + "asset": "SEK", + "maximum": "25770.18", + "minimum": "103.09" + }, + { + "asset": "SGD", + "maximum": "3360.03", + "minimum": "13.45" + }, + { + "asset": "USD", + "maximum": "2500.00", + "minimum": "10.00" + } + ], + "network": "card", + "type": "debit-card" + }, + { + "billingAsset": "EUR", + "countries": [ + "AT", + "BE", + "BG", + "CZ", + "DK", + "EE", + "ES", + "FI", + "FR", + "GR", + "HR", + "HU", + "IE", + "IT", + "LT", + "LU", + "LV", + "NO", + "PL", + "PT", + "RO", + "SE", + "SK" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9956.98", + "minimum": "39.83" + }, + { + "asset": "ARS", + "maximum": "2280242.04", + "minimum": "9120.97" + }, + { + "asset": "AUD", + "maximum": "4142.50", + "minimum": "16.58" + }, + { + "asset": "BRL", + "maximum": "13375.19", + "minimum": "53.51" + }, + { + "asset": "CAD", + "maximum": "3668.07", + "minimum": "14.68" + }, + { + "asset": "CHF", + "maximum": "2383.33", + "minimum": "9.54" + }, + { + "asset": "CNY", + "maximum": "19533.79", + "minimum": "78.14" + }, + { + "asset": "CZK", + "maximum": "63308.55", + "minimum": "253.24" + }, + { + "asset": "DKK", + "maximum": "18634.50", + "minimum": "74.54" + }, + { + "asset": "EUR", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "GBP", + "maximum": "2139.35", + "minimum": "8.56" + }, + { + "asset": "HKD", + "maximum": "21216.41", + "minimum": "84.87" + }, + { + "asset": "HUF", + "maximum": "977134.88", + "minimum": "3908.54" + }, + { + "asset": "ILS", + "maximum": "9791.68", + "minimum": "39.17" + }, + { + "asset": "INR", + "maximum": "224811.64", + "minimum": "899.25" + }, + { + "asset": "JPY", + "maximum": "407956", + "minimum": "1632" + }, + { + "asset": "KES", + "maximum": "395788.92", + "minimum": "1583.16" + }, + { + "asset": "MXN", + "maximum": "46277.72", + "minimum": "185.12" + }, + { + "asset": "NOK", + "maximum": "28559.77", + "minimum": "114.24" + }, + { + "asset": "NZD", + "maximum": "4391.95", + "minimum": "17.57" + }, + { + "asset": "PHP", + "maximum": "152050.35", + "minimum": "608.21" + }, + { + "asset": "PLN", + "maximum": "10765.41", + "minimum": "43.07" + }, + { + "asset": "RON", + "maximum": "12423.47", + "minimum": "49.70" + }, + { + "asset": "SEK", + "maximum": "27943.90", + "minimum": "111.78" + }, + { + "asset": "SGD", + "maximum": "3643.45", + "minimum": "14.58" + }, + { + "asset": "USD", + "maximum": "2710.87", + "minimum": "10.85" + } + ], + "network": "google-pay", + "type": "google-pay" + }, + { + "billingAsset": "GBP", + "countries": [ + "GB" + ], + "limits": [ + { + "asset": "AED", + "maximum": "11635.49", + "minimum": "46.55" + }, + { + "asset": "ARS", + "maximum": "2664635.72", + "minimum": "10658.55" + }, + { + "asset": "AUD", + "maximum": "4840.83", + "minimum": "19.37" + }, + { + "asset": "BRL", + "maximum": "15629.92", + "minimum": "62.52" + }, + { + "asset": "CAD", + "maximum": "4286.42", + "minimum": "17.15" + }, + { + "asset": "CHF", + "maximum": "2785.10", + "minimum": "11.15" + }, + { + "asset": "CNY", + "maximum": "22826.72", + "minimum": "91.31" + }, + { + "asset": "CZK", + "maximum": "73980.84", + "minimum": "295.93" + }, + { + "asset": "DKK", + "maximum": "21775.82", + "minimum": "87.11" + }, + { + "asset": "EUR", + "maximum": "2921.43", + "minimum": "11.69" + }, + { + "asset": "GBP", + "maximum": "2500.00", + "minimum": "10.00" + }, + { + "asset": "HKD", + "maximum": "24792.98", + "minimum": "99.18" + }, + { + "asset": "HUF", + "maximum": "1141856.20", + "minimum": "4567.43" + }, + { + "asset": "ILS", + "maximum": "11442.32", + "minimum": "45.77" + }, + { + "asset": "INR", + "maximum": "262709.44", + "minimum": "1050.84" + }, + { + "asset": "JPY", + "maximum": "476728", + "minimum": "1907" + }, + { + "asset": "KES", + "maximum": "462509.36", + "minimum": "1850.04" + }, + { + "asset": "MXN", + "maximum": "54079.03", + "minimum": "216.32" + }, + { + "asset": "NOK", + "maximum": "33374.25", + "minimum": "133.50" + }, + { + "asset": "NZD", + "maximum": "5132.33", + "minimum": "20.53" + }, + { + "asset": "PHP", + "maximum": "177682.37", + "minimum": "710.73" + }, + { + "asset": "PLN", + "maximum": "12580.20", + "minimum": "50.33" + }, + { + "asset": "RON", + "maximum": "14517.77", + "minimum": "58.08" + }, + { + "asset": "SEK", + "maximum": "32654.56", + "minimum": "130.62" + }, + { + "asset": "SGD", + "maximum": "4257.65", + "minimum": "17.04" + }, + { + "asset": "USD", + "maximum": "3167.86", + "minimum": "12.68" + } + ], + "network": "google-pay", + "type": "google-pay" + }, + { + "billingAsset": "USD", + "countries": [ + "AE", + "AG", + "AR", + "AU", + "BH", + "BR", + "CA", + "CH", + "CL", + "HK", + "IL", + "JP", + "KW", + "MX", + "MY", + "NZ", + "OM", + "PE", + "PH", + "QA", + "SA", + "SG", + "TH", + "US", + "UY", + "ZA" + ], + "limits": [ + { + "asset": "AED", + "maximum": "9182.44", + "minimum": "36.73" + }, + { + "asset": "ARS", + "maximum": "2102865.38", + "minimum": "8411.47" + }, + { + "asset": "AUD", + "maximum": "3820.26", + "minimum": "15.29" + }, + { + "asset": "BRL", + "maximum": "12334.75", + "minimum": "49.34" + }, + { + "asset": "CAD", + "maximum": "3382.73", + "minimum": "13.54" + }, + { + "asset": "CHF", + "maximum": "2197.93", + "minimum": "8.80" + }, + { + "asset": "CNY", + "maximum": "18014.29", + "minimum": "72.06" + }, + { + "asset": "CZK", + "maximum": "58383.87", + "minimum": "233.54" + }, + { + "asset": "DKK", + "maximum": "17184.95", + "minimum": "68.74" + }, + { + "asset": "EUR", + "maximum": "2305.52", + "minimum": "9.23" + }, + { + "asset": "GBP", + "maximum": "1972.93", + "minimum": "7.90" + }, + { + "asset": "HKD", + "maximum": "19566.01", + "minimum": "78.27" + }, + { + "asset": "HUF", + "maximum": "901125.00", + "minimum": "3604.51" + }, + { + "asset": "ILS", + "maximum": "9030.00", + "minimum": "36.13" + }, + { + "asset": "INR", + "maximum": "207323.87", + "minimum": "829.30" + }, + { + "asset": "JPY", + "maximum": "376222", + "minimum": "1505" + }, + { + "asset": "KES", + "maximum": "365001.08", + "minimum": "1460.01" + }, + { + "asset": "MXN", + "maximum": "42677.85", + "minimum": "170.72" + }, + { + "asset": "NOK", + "maximum": "26338.15", + "minimum": "105.36" + }, + { + "asset": "NZD", + "maximum": "4050.31", + "minimum": "16.21" + }, + { + "asset": "PHP", + "maximum": "140222.58", + "minimum": "560.90" + }, + { + "asset": "PLN", + "maximum": "9927.98", + "minimum": "39.72" + }, + { + "asset": "RON", + "maximum": "11457.07", + "minimum": "45.83" + }, + { + "asset": "SEK", + "maximum": "25770.18", + "minimum": "103.09" + }, + { + "asset": "SGD", + "maximum": "3360.03", + "minimum": "13.45" + }, + { + "asset": "USD", + "maximum": "2500.00", + "minimum": "10.00" + } + ], + "network": "google-pay", + "type": "google-pay" + } + ] +} \ No newline at end of file diff --git a/sample-integration-android/build.gradle b/sample-integration-android/build.gradle index a6ae756877..26723b640f 100644 --- a/sample-integration-android/build.gradle +++ b/sample-integration-android/build.gradle @@ -10,6 +10,7 @@ dependencies { } implementation "org.dashj:dashj-core:$dashjVersion" implementation 'androidx.multidex:multidex:2.0.1' + implementation 'com.google.protobuf:protobuf-javalite:3.17.3' } android { @@ -38,7 +39,7 @@ android { defaultConfig { compileSdk 33 - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 33 multiDexEnabled true } diff --git a/wallet/AndroidManifest.xml b/wallet/AndroidManifest.xml index a0b40fd831..9cfcfb0998 100644 --- a/wallet/AndroidManifest.xml +++ b/wallet/AndroidManifest.xml @@ -41,7 +41,7 @@ android:required="true" /> + android:required="false" /> diff --git a/wallet/CHANGES b/wallet/CHANGES index e584bffebe..46f5903981 100644 --- a/wallet/CHANGES +++ b/wallet/CHANGES @@ -1,4 +1,20 @@ Dash Wallet +v10.1.0 +* Add ZenLedger support +* Coinbase: upgrade payment methods to v3 +* Update to dashj 20.0.4 (track deadlocks) + +v10.0.3 +* Deprecate Android 6 support, Android 7 now required +* Coinbase: use custom tab for linking accounts +* Topper: set $100 as default amount +* Uphold: update API endpoint for withdraw capabilities +* Fix some memory leaks in Coinbase and Explore modules +* Add bootstrap files for less bandwidth initial sync +* Fix rare blockchain rescan crashes +* Fix rare internal wallet restoration crash +* update to dashj 20.0.3 + v10.0.2 * Fix Syncing... issue * Fix CrowdNode and Masternode APY calculation diff --git a/wallet/README.recover.md b/wallet/README.recover.md index 34b30268c5..f4c3c32960 100644 --- a/wallet/README.recover.md +++ b/wallet/README.recover.md @@ -97,7 +97,7 @@ password. We need wallet-tool from dashj. First, in a working directory, let's get dashj: - git clone -b release-19 https://github.com/HashEngineering/dashj.git + git clone -b master https://github.com/HashEngineering/dashj.git Make sure everything is compiled and ready to go by using once: @@ -135,6 +135,42 @@ Then to get the private keys use: Look for `priv WIF=<...>`, where `<...>` will be your private keys in wallet import format. Be careful where you put them, as anybody getting access to them will be able to steal your coins. Consider securely deleting the decrypted wallet once you get your private keys. + +## RECOVERING FROM A RECOVERY PHRASE (BIP39 MNEMONIC) + +There are two accounts on the BIP39 mnemonic phrase. One follows the BIP44 standard with path +m/44'/5'/0' and the second follows the BIP32 standard with path m/0'. Dash Wallet 5.18 (May 2018) +and higher use the BIP44 path as the default but maintain the BIP32 path for backward compatibility. + +We need wallet-tool from dashj. First, in a working directory, let's get dashj: + + git clone -b release-20 https://github.com/HashEngineering/dashj.git + +Make sure everything is compiled and ready to go by using once: + + cd dashj/tools + ./wallet-tool + +Now use wallet-tool to create a wallet using your recovery phrase: + + ./wallet-tool create --seed="" --wallet=recoveredwallet + +Now use wallet-tool to sync the wallet from your backup: + + ./wallet-tool reset --wallet=recoveredwallet + ./wallet-tool sync --wallet=recoveredwallet --debuglog + +The sync process will take anywhere from a few minutes to hours. Wallet-tool will return to the +shell prompt if its finished syncing. Have a look at the wallet: + + ./wallet-tool dump --wallet=recoveredwallet + +Does the balance look right? You can see all transactions that ever touched your wallet. Now empty +your entire wallet to the desired destination wallet: + + ./wallet-tool send --wallet=recoveredwallet --output=:ALL + + ## RECOVERING FROM BASE58 KEY FORMAT Have a deeper look at the backup file (these files were produced by Darkcoin Wallet): @@ -145,8 +181,7 @@ You'll see each line contains a key in WIF (wallet import format), technically B datetime string after each key is the birthdate of that key which you can ignore for the purpose of this one-time recovery. -Another option is importing each individual key into one of [Electrum Dash] (https://electrum.dash.org/#download) -or [Dash Core] (https://www.dash.org/downloads/). +Another option is importing each individual key into [Dash Core] (https://www.dash.org/downloads/). As soon as you see your whole balance again, empty your entire wallet to the desired destination wallet. Please do not continue to use the imported wallet. Remember you just operated on diff --git a/wallet/assets/mnlistdiff.dat b/wallet/assets/mnlistdiff.dat new file mode 100644 index 0000000000..d6bab2dcb3 Binary files /dev/null and b/wallet/assets/mnlistdiff.dat differ diff --git a/wallet/assets/qrinfo.dat b/wallet/assets/qrinfo.dat new file mode 100644 index 0000000000..2134d358dd Binary files /dev/null and b/wallet/assets/qrinfo.dat differ diff --git a/wallet/build.gradle b/wallet/build.gradle index a8616f9e98..7c382246e8 100644 --- a/wallet/build.gradle +++ b/wallet/build.gradle @@ -3,7 +3,7 @@ plugins { id 'project-report' id 'kotlin-android' id 'kotlin-parcelize' - id 'kotlin-kapt' + id 'com.google.devtools.ksp' id 'dagger.hilt.android.plugin' id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' id 'androidx.navigation.safeargs.kotlin' @@ -49,6 +49,7 @@ dependencies { implementation "androidx.work:work-runtime-ktx:$workRuntimeVersion" implementation "androidx.biometric:biometric:1.1.0" + implementation 'org.bouncycastle:bcprov-jdk15to18:1.74' implementation "org.dashj:dashj-core:$dashjVersion" implementation 'org.dashj.platform:dashpay:0.24-MOCK-SNAPSHOT' implementation 'org.dashj.platform:platform-core:0.24-MOCK-SNAPSHOT' @@ -89,9 +90,15 @@ dependencies { implementation 'com.google.android.flexbox:flexbox:3.0.0' implementation "androidx.constraintlayout:constraintlayout:$constrainLayoutVersion" implementation "com.github.bumptech.glide:glide:$glideVersion" - kapt "com.github.bumptech.glide:compiler:$glideVersion" - implementation 'com.github.MikeOrtiz:TouchImageView:3.0.3' + ksp "com.github.bumptech.glide:compiler:$glideVersion" + implementation 'com.github.MikeOrtiz:TouchImageView:3.0.6' implementation "io.coil-kt:coil:$coilVersion" + implementation(platform("androidx.compose:compose-bom:2023.03.00")) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.compose.material3:material3") + debugImplementation 'androidx.compose.ui:ui-tooling:1.0.0' + implementation("androidx.activity:activity-compose:1.7.2") // Navigation implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" @@ -100,7 +107,7 @@ dependencies { // Database implementation "androidx.room:room-ktx:$roomVersion" implementation "androidx.room:room-runtime:$roomVersion" - kapt "androidx.room:room-compiler:$roomVersion" + ksp "androidx.room:room-compiler:$roomVersion" // Prefs implementation "androidx.preference:preference-ktx:$preferenceVersion" @@ -141,9 +148,9 @@ dependencies { // DI implementation "com.google.dagger:hilt-android:$hiltVersion" - kapt "com.google.dagger:hilt-compiler:$hiltVersion" + ksp "com.google.dagger:hilt-compiler:$hiltVersion" implementation "androidx.hilt:hilt-work:$hiltWorkVersion" - kapt 'androidx.hilt:hilt-compiler:1.0.0' + ksp "androidx.hilt:hilt-compiler:$hiltCompilerVersion" // Unit tests testImplementation "junit:junit:$junitVersion" @@ -169,15 +176,11 @@ dependencies { // DI for instrumentation tests androidTestImplementation "com.google.dagger:hilt-android-testing:$hiltVersion" - kaptAndroidTest "com.google.dagger:hilt-compiler:$hiltVersion" + kspAndroidTest "com.google.dagger:hilt-compiler:$hiltVersion" // DI for local unit tests testImplementation "com.google.dagger:hilt-android-testing:$hiltVersion" - kaptTest "com.google.dagger:hilt-compiler:$hiltVersion" -} - -kapt { - correctErrorTypes true + kspTest "com.google.dagger:hilt-compiler:$hiltVersion" } hilt { @@ -188,14 +191,6 @@ ext { archivesBaseName = 'dash-wallet' } -kapt { - correctErrorTypes true -} - -hilt { - enableAggregatingTask = true -} - def props = new Properties() if (file("../service.properties").exists()) { file("../service.properties").withInputStream { props.load(it) } @@ -211,7 +206,7 @@ android { defaultConfig { compileSdk 33 - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 33 versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 90002 versionName project.hasProperty('versionName') ? project.property('versionName') : "5.4-dashpay" @@ -324,6 +319,11 @@ android { buildConfigField("String", "TOPPER_KEY_ID", topperKeyId) buildConfigField("String", "TOPPER_WIDGET_ID", topperWidgetId) buildConfigField("String", "TOPPER_PRIVATE_KEY", topperPrivateKey) + + def zenLedgerClientId = props.getProperty("ZENLEDGER_CLIENT_ID", "\"\"") + def zenLedgerClientSecret = props.getProperty("ZENLEDGER_CLIENT_SECRET", "\"\"") + buildConfigField("String", "ZENLEDGER_CLIENT_ID", zenLedgerClientId) + buildConfigField("String", "ZENLEDGER_CLIENT_SECRET", zenLedgerClientSecret) } _testNet3 { applicationId = "hashengineering.darkcoin.wallet_test" @@ -339,6 +339,8 @@ android { buildConfigField("String", "TOPPER_KEY_ID", topperKeyId) buildConfigField("String", "TOPPER_WIDGET_ID", topperWidgetId) buildConfigField("String", "TOPPER_PRIVATE_KEY", topperPrivateKey) + buildConfigField("String", "ZENLEDGER_CLIENT_ID", "\"\"") + buildConfigField("String", "ZENLEDGER_CLIENT_SECRET", "\"\"") def dashDirectClientId = props.getProperty("DASHDIRECT_CLIENT_ID") if (dashDirectClientId == null) { dashDirectClientId = "\"DASHDIRECT_CLIENT_ID\"" @@ -363,7 +365,8 @@ android { buildConfigField("String", "TOPPER_KEY_ID", topperKeyId) buildConfigField("String", "TOPPER_WIDGET_ID", topperWidgetId) buildConfigField("String", "TOPPER_PRIVATE_KEY", topperPrivateKey) - + buildConfigField("String", "ZENLEDGER_CLIENT_ID", "\"\"") + buildConfigField("String", "ZENLEDGER_CLIENT_SECRET", "\"\"") def dashDirectClientId = props.getProperty("DASHDIRECT_CLIENT_ID") if (dashDirectClientId == null) { dashDirectClientId = "\"DASHDIRECT_CLIENT_ID\"" @@ -383,6 +386,8 @@ android { applicationId = "org.dash.wallet.devnet" buildConfigField("String", "UPHOLD_CLIENT_ID", "\"UPHOLD_CLIENT_ID\"") buildConfigField("String", "UPHOLD_CLIENT_SECRET", "\"UPHOLD_CLIENT_SECRET\"") + buildConfigField("String", "ZENLEDGER_CLIENT_ID", "\"\"") + buildConfigField("String", "ZENLEDGER_CLIENT_SECRET", "\"\"") } } @@ -416,6 +421,10 @@ android { } buildFeatures { viewBinding true + compose true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.11" } testOptions { unitTests.includeAndroidResources = true diff --git a/wallet/proguard.cfg b/wallet/proguard.cfg index 662b1b5726..12d537bb65 100644 --- a/wallet/proguard.cfg +++ b/wallet/proguard.cfg @@ -134,4 +134,8 @@ -keep class org.bouncycastle** { *; } -keepnames class org.bouncycastle** { *; } --dontwarn org.bouncycastle.** \ No newline at end of file +-dontwarn org.bouncycastle.** + +-keepclassmembers class * { + @com.google.gson.annotations.SerializedName ; +} \ No newline at end of file diff --git a/wallet/res/drawable-hdpi/ic_zenledger_dash.png b/wallet/res/drawable-hdpi/ic_zenledger_dash.png new file mode 100644 index 0000000000..d22dab9c8a Binary files /dev/null and b/wallet/res/drawable-hdpi/ic_zenledger_dash.png differ diff --git a/wallet/res/drawable-mdpi/ic_zenledger_dash.png b/wallet/res/drawable-mdpi/ic_zenledger_dash.png new file mode 100644 index 0000000000..a0ad87a4dc Binary files /dev/null and b/wallet/res/drawable-mdpi/ic_zenledger_dash.png differ diff --git a/wallet/res/drawable-xhdpi/ic_zenledger_dash.png b/wallet/res/drawable-xhdpi/ic_zenledger_dash.png new file mode 100644 index 0000000000..27bc564a6a Binary files /dev/null and b/wallet/res/drawable-xhdpi/ic_zenledger_dash.png differ diff --git a/wallet/res/drawable-xxhdpi/ic_zenledger_dash.png b/wallet/res/drawable-xxhdpi/ic_zenledger_dash.png new file mode 100644 index 0000000000..8be408a279 Binary files /dev/null and b/wallet/res/drawable-xxhdpi/ic_zenledger_dash.png differ diff --git a/wallet/res/drawable-xxxhdpi/ic_zenledger_dash.png b/wallet/res/drawable-xxxhdpi/ic_zenledger_dash.png new file mode 100644 index 0000000000..7b65bda0dc Binary files /dev/null and b/wallet/res/drawable-xxxhdpi/ic_zenledger_dash.png differ diff --git a/wallet/res/drawable/ic_zenledger_icon.xml b/wallet/res/drawable/ic_zenledger_icon.xml new file mode 100644 index 0000000000..184baf10c1 --- /dev/null +++ b/wallet/res/drawable/ic_zenledger_icon.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/wallet/res/layout/dialog_zenledger.xml b/wallet/res/layout/dialog_zenledger.xml new file mode 100644 index 0000000000..c0168705ba --- /dev/null +++ b/wallet/res/layout/dialog_zenledger.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + +