[#776] Enable ZIP 317 fees support
* [#776] Enable ZIP 317 fees support * Deprecate ZcashSdk.MINERS_FEE * Replace MINERS_FEE with Proposal API in Demo app * Changelog update * Bump SDK to 2.0.8 to produce snapshot version
This commit is contained in:
parent
5b04cbc579
commit
078e76a941
|
@ -6,6 +6,10 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The SDK uses ZIP-317 fee system internally
|
||||||
|
- `ZcashSdk.MINERS_FEE` has been deprecated, and will be removed in 2.1.0
|
||||||
|
|
||||||
## [2.0.7] - 2024-03-08
|
## [2.0.7] - 2024-03-08
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -376,7 +376,7 @@ class RustBackend private constructor(
|
||||||
*/
|
*/
|
||||||
companion object {
|
companion object {
|
||||||
internal val rustLibraryLoader = NativeLibraryLoader("zcashwalletsdk")
|
internal val rustLibraryLoader = NativeLibraryLoader("zcashwalletsdk")
|
||||||
private const val IS_USE_ZIP_317_FEES = false
|
private const val IS_USE_ZIP_317_FEES = true
|
||||||
|
|
||||||
suspend fun loadLibrary() {
|
suspend fun loadLibrary() {
|
||||||
rustLibraryLoader.load {
|
rustLibraryLoader.load {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import cash.z.ecc.android.sdk.demoapp.ui.screen.transactions.view.Transactions
|
||||||
import cash.z.ecc.android.sdk.demoapp.util.AndroidApiVersion
|
import cash.z.ecc.android.sdk.demoapp.util.AndroidApiVersion
|
||||||
import cash.z.ecc.android.sdk.demoapp.util.fromResources
|
import cash.z.ecc.android.sdk.demoapp.util.fromResources
|
||||||
import cash.z.ecc.android.sdk.internal.Twig
|
import cash.z.ecc.android.sdk.internal.Twig
|
||||||
|
import cash.z.ecc.android.sdk.model.Proposal
|
||||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||||
import cash.z.ecc.android.sdk.type.ServerValidation
|
import cash.z.ecc.android.sdk.type.ServerValidation
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -131,6 +132,9 @@ internal fun ComposeActivity.Navigation() {
|
||||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||||
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
|
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
|
||||||
val spendingKey = walletViewModel.spendingKey.collectAsStateWithLifecycle().value
|
val spendingKey = walletViewModel.spendingKey.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
|
val sendTransactionProposal = remember { mutableStateOf<Proposal?>(null) }
|
||||||
|
|
||||||
if (null == synchronizer || null == walletSnapshot || null == spendingKey) {
|
if (null == synchronizer || null == walletSnapshot || null == spendingKey) {
|
||||||
// Display loading indicator
|
// Display loading indicator
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,10 +144,14 @@ internal fun ComposeActivity.Navigation() {
|
||||||
onSend = {
|
onSend = {
|
||||||
walletViewModel.send(it)
|
walletViewModel.send(it)
|
||||||
},
|
},
|
||||||
|
onGetProposal = {
|
||||||
|
sendTransactionProposal.value = walletViewModel.getSendProposal(it)
|
||||||
|
},
|
||||||
onBack = {
|
onBack = {
|
||||||
walletViewModel.clearSendOrShieldState()
|
walletViewModel.clearSendOrShieldState()
|
||||||
navController.popBackStackJustOnce(SEND)
|
navController.popBackStackJustOnce(SEND)
|
||||||
}
|
},
|
||||||
|
sendTransactionProposal = sendTransactionProposal.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBalanceBinding
|
||||||
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
|
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
|
||||||
import cash.z.ecc.android.sdk.demoapp.util.SyncBlockchainBenchmarkTrace
|
import cash.z.ecc.android.sdk.demoapp.util.SyncBlockchainBenchmarkTrace
|
||||||
import cash.z.ecc.android.sdk.demoapp.util.fromResources
|
import cash.z.ecc.android.sdk.demoapp.util.fromResources
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
|
||||||
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
|
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
|
||||||
import cash.z.ecc.android.sdk.internal.Twig
|
import cash.z.ecc.android.sdk.internal.Twig
|
||||||
import cash.z.ecc.android.sdk.model.Account
|
import cash.z.ecc.android.sdk.model.Account
|
||||||
|
@ -138,10 +137,10 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.shield.apply {
|
binding.shield.apply {
|
||||||
// TODO [#776]: Support variable fees
|
// This check is not entirely correct - it does not calculate the resulting fee with the new Proposal API
|
||||||
// TODO [#776]: https://github.com/zcash/zcash-android-wallet-sdk/issues/776
|
// Note that the entire fragment-based old Demo app will be removed as part of [#973]
|
||||||
visibility =
|
visibility =
|
||||||
if ((transparentBalance ?: Zatoshi(0)) > ZcashSdk.MINERS_FEE) {
|
if ((transparentBalance ?: Zatoshi(0)).value > 0L) {
|
||||||
View.VISIBLE
|
View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
View.GONE
|
View.GONE
|
||||||
|
|
|
@ -26,12 +26,11 @@ import cash.z.ecc.android.sdk.demoapp.R
|
||||||
import cash.z.ecc.android.sdk.demoapp.fixture.WalletSnapshotFixture
|
import cash.z.ecc.android.sdk.demoapp.fixture.WalletSnapshotFixture
|
||||||
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.SendState
|
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.SendState
|
||||||
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.WalletSnapshot
|
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.WalletSnapshot
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
|
||||||
import cash.z.ecc.android.sdk.model.toZecString
|
import cash.z.ecc.android.sdk.model.toZecString
|
||||||
|
|
||||||
@Preview(name = "Balance")
|
@Preview(name = "Balance")
|
||||||
@Composable
|
|
||||||
@Suppress("ktlint:standard:function-naming")
|
@Suppress("ktlint:standard:function-naming")
|
||||||
|
@Composable
|
||||||
private fun ComposablePreview() {
|
private fun ComposablePreview() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
Balance(
|
Balance(
|
||||||
|
@ -152,10 +151,8 @@ private fun BalanceMainContent(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO [#776]: Support variable fees
|
// This check is not entirely correct - it does not calculate the resulting fee with the new Proposal API
|
||||||
// TODO [#776]: https://github.com/zcash/zcash-android-wallet-sdk/issues/776
|
if (walletSnapshot.transparentBalance.value > 0L) {
|
||||||
// This check will not be correct with variable fees
|
|
||||||
if (walletSnapshot.transparentBalance > ZcashSdk.MINERS_FEE) {
|
|
||||||
// Note this implementation does not guard against multiple clicks
|
// Note this implementation does not guard against multiple clicks
|
||||||
Button(onClick = onShieldFunds) {
|
Button(onClick = onShieldFunds) {
|
||||||
Text(stringResource(id = R.string.action_shield))
|
Text(stringResource(id = R.string.action_shield))
|
||||||
|
|
|
@ -2,7 +2,6 @@ package cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.Synchronizer
|
import cash.z.ecc.android.sdk.Synchronizer
|
||||||
import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
|
import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
|
||||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||||
|
@ -16,13 +15,11 @@ data class WalletSnapshot(
|
||||||
val progress: PercentDecimal,
|
val progress: PercentDecimal,
|
||||||
val synchronizerError: SynchronizerError?
|
val synchronizerError: SynchronizerError?
|
||||||
) {
|
) {
|
||||||
// TODO [#776]: Support variable fees
|
|
||||||
// TODO [#776]: https://github.com/zcash/zcash-android-wallet-sdk/issues/776
|
|
||||||
// Note: the wallet is effectively empty if it cannot cover the miner's fee
|
// Note: the wallet is effectively empty if it cannot cover the miner's fee
|
||||||
val hasFunds =
|
// This check is not entirely correct - it does not calculate the resulting fee with the new Proposal API
|
||||||
saplingBalance.available.value >
|
val hasFunds = saplingBalance.available.value > 0L
|
||||||
(ZcashSdk.MINERS_FEE.value.toDouble() / Zatoshi.ZATOSHI_PER_ZEC) // 0.0001
|
|
||||||
val hasSaplingBalance = saplingBalance.total.value > 0
|
val hasSaplingBalance = saplingBalance.total.value > 0L
|
||||||
|
|
||||||
val isSendEnabled: Boolean get() = status == Synchronizer.Status.SYNCED && hasFunds
|
val isSendEnabled: Boolean get() = status == Synchronizer.Status.SYNCED && hasFunds
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,14 @@ import cash.z.ecc.android.sdk.model.Account
|
||||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||||
import cash.z.ecc.android.sdk.model.PersistableWallet
|
import cash.z.ecc.android.sdk.model.PersistableWallet
|
||||||
|
import cash.z.ecc.android.sdk.model.Proposal
|
||||||
import cash.z.ecc.android.sdk.model.TransactionSubmitResult
|
import cash.z.ecc.android.sdk.model.TransactionSubmitResult
|
||||||
import cash.z.ecc.android.sdk.model.WalletAddresses
|
import cash.z.ecc.android.sdk.model.WalletAddresses
|
||||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||||
import cash.z.ecc.android.sdk.model.ZecSend
|
import cash.z.ecc.android.sdk.model.ZecSend
|
||||||
|
import cash.z.ecc.android.sdk.model.proposeSend
|
||||||
import cash.z.ecc.android.sdk.model.send
|
import cash.z.ecc.android.sdk.model.send
|
||||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||||
import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
|
import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
|
||||||
|
@ -50,6 +52,7 @@ import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -57,6 +60,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
// To make this more multiplatform compatible, we need to remove the dependency on Context
|
// To make this more multiplatform compatible, we need to remove the dependency on Context
|
||||||
// for loading the preferences.
|
// for loading the preferences.
|
||||||
|
@Suppress("TooManyFunctions")
|
||||||
class WalletViewModel(application: Application) : AndroidViewModel(application) {
|
class WalletViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
private val walletCoordinator = WalletCoordinator.getInstance(application)
|
private val walletCoordinator = WalletCoordinator.getInstance(application)
|
||||||
|
|
||||||
|
@ -212,12 +216,36 @@ class WalletViewModel(application: Application) : AndroidViewModel(application)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronously provides proposal object for the given [spendingKey] and [zecSend] objects
|
||||||
|
*/
|
||||||
|
fun getSendProposal(zecSend: ZecSend): Proposal? {
|
||||||
|
if (sendState.value is SendState.Sending) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val synchronizer = synchronizer.value
|
||||||
|
|
||||||
|
return if (null != synchronizer) {
|
||||||
|
// Calling the proposal API within a blocking coroutine should be fine for the showcase purpose
|
||||||
|
runBlocking {
|
||||||
|
val spendingKey = spendingKey.filterNotNull().first()
|
||||||
|
kotlin.runCatching {
|
||||||
|
synchronizer.proposeSend(spendingKey, zecSend)
|
||||||
|
}.onFailure {
|
||||||
|
Twig.error(it) { "Failed to get transaction proposal" }
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error("Unable to send funds because synchronizer is not loaded.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously shields transparent funds. Note that two shielding operations cannot occur at the same time.
|
* Asynchronously shields transparent funds. Note that two shielding operations cannot occur at the same time.
|
||||||
*
|
*
|
||||||
* Observe the result via [sendState].
|
* Observe the result via [sendState].
|
||||||
*/
|
*/
|
||||||
@Suppress("MagicNumber")
|
|
||||||
fun shieldFunds() {
|
fun shieldFunds() {
|
||||||
if (sendState.value is SendState.Sending) {
|
if (sendState.value is SendState.Sending) {
|
||||||
return
|
return
|
||||||
|
@ -230,6 +258,7 @@ class WalletViewModel(application: Application) : AndroidViewModel(application)
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val spendingKey = spendingKey.filterNotNull().first()
|
val spendingKey = spendingKey.filterNotNull().first()
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
|
@Suppress("MagicNumber")
|
||||||
synchronizer.proposeShielding(spendingKey.account, Zatoshi(100000))?.let {
|
synchronizer.proposeShielding(spendingKey.account, Zatoshi(100000))?.let {
|
||||||
synchronizer.createProposedTransactions(
|
synchronizer.createProposedTransactions(
|
||||||
it,
|
it,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
@ -43,6 +44,7 @@ import cash.z.ecc.android.sdk.demoapp.util.fromResources
|
||||||
import cash.z.ecc.android.sdk.fixture.WalletFixture
|
import cash.z.ecc.android.sdk.fixture.WalletFixture
|
||||||
import cash.z.ecc.android.sdk.model.Memo
|
import cash.z.ecc.android.sdk.model.Memo
|
||||||
import cash.z.ecc.android.sdk.model.MonetarySeparators
|
import cash.z.ecc.android.sdk.model.MonetarySeparators
|
||||||
|
import cash.z.ecc.android.sdk.model.Proposal
|
||||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||||
import cash.z.ecc.android.sdk.model.ZecSend
|
import cash.z.ecc.android.sdk.model.ZecSend
|
||||||
import cash.z.ecc.android.sdk.model.ZecSendExt
|
import cash.z.ecc.android.sdk.model.ZecSendExt
|
||||||
|
@ -60,18 +62,22 @@ private fun ComposablePreview() {
|
||||||
walletSnapshot = WalletSnapshotFixture.new(),
|
walletSnapshot = WalletSnapshotFixture.new(),
|
||||||
sendState = SendState.None,
|
sendState = SendState.None,
|
||||||
onSend = {},
|
onSend = {},
|
||||||
onBack = {}
|
onGetProposal = {},
|
||||||
|
onBack = {},
|
||||||
|
sendTransactionProposal = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("ktlint:standard:function-naming")
|
@Suppress("ktlint:standard:function-naming", "LongParameterList")
|
||||||
fun Send(
|
fun Send(
|
||||||
walletSnapshot: WalletSnapshot,
|
walletSnapshot: WalletSnapshot,
|
||||||
sendState: SendState,
|
sendState: SendState,
|
||||||
onSend: (ZecSend) -> Unit,
|
onSend: (ZecSend) -> Unit,
|
||||||
onBack: () -> Unit
|
onGetProposal: (ZecSend) -> Unit,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
sendTransactionProposal: Proposal?,
|
||||||
) {
|
) {
|
||||||
Scaffold(topBar = {
|
Scaffold(topBar = {
|
||||||
SendTopAppBar(onBack)
|
SendTopAppBar(onBack)
|
||||||
|
@ -80,7 +86,9 @@ fun Send(
|
||||||
paddingValues = paddingValues,
|
paddingValues = paddingValues,
|
||||||
walletSnapshot = walletSnapshot,
|
walletSnapshot = walletSnapshot,
|
||||||
sendState = sendState,
|
sendState = sendState,
|
||||||
onSend = onSend
|
onSend = onSend,
|
||||||
|
onGetProposal = onGetProposal,
|
||||||
|
sendTransactionProposal = sendTransactionProposal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,12 +113,14 @@ private fun SendTopAppBar(onBack: () -> Unit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod", "ktlint:standard:function-naming")
|
@Suppress("LongMethod", "ktlint:standard:function-naming", "LongParameterList")
|
||||||
private fun SendMainContent(
|
private fun SendMainContent(
|
||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
walletSnapshot: WalletSnapshot,
|
walletSnapshot: WalletSnapshot,
|
||||||
sendState: SendState,
|
sendState: SendState,
|
||||||
onSend: (ZecSend) -> Unit
|
onSend: (ZecSend) -> Unit,
|
||||||
|
onGetProposal: (ZecSend) -> Unit,
|
||||||
|
sendTransactionProposal: Proposal?,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val monetarySeparators = MonetarySeparators.current(locale = Locale.US)
|
val monetarySeparators = MonetarySeparators.current(locale = Locale.US)
|
||||||
|
@ -229,6 +239,36 @@ private fun SendMainContent(
|
||||||
Text(validation.joinToString(", "))
|
Text(validation.joinToString(", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
val zecSendValidation =
|
||||||
|
ZecSendExt.new(
|
||||||
|
context,
|
||||||
|
recipientAddressString,
|
||||||
|
amountZecString,
|
||||||
|
memoString,
|
||||||
|
monetarySeparators
|
||||||
|
)
|
||||||
|
|
||||||
|
when (zecSendValidation) {
|
||||||
|
is ZecSendExt.ZecSendValidation.Valid -> onGetProposal(zecSendValidation.zecSend)
|
||||||
|
is ZecSendExt.ZecSendValidation.Invalid -> validation = zecSendValidation.validationErrors
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Needs actual validation
|
||||||
|
enabled = amountZecString.isNotBlank() && recipientAddressString.isNotBlank()
|
||||||
|
) {
|
||||||
|
Text(stringResource(id = R.string.send_proposal_button))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendTransactionProposal != null) {
|
||||||
|
Text(stringResource(id = R.string.send_proposal_status, sendTransactionProposal.toPrettyString()))
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
val zecSendValidation =
|
val zecSendValidation =
|
||||||
|
|
|
@ -73,6 +73,9 @@
|
||||||
<string name="send_memo">Memo</string>
|
<string name="send_memo">Memo</string>
|
||||||
<string name="send_button">Send</string>
|
<string name="send_button">Send</string>
|
||||||
|
|
||||||
|
<string name="send_proposal_button">Get Proposal</string>
|
||||||
|
<string name="send_proposal_status">Proposal:\n<xliff:g id="proposal" example="Fee:0.001...">%1$s</xliff:g></string>
|
||||||
|
|
||||||
<string name="server_textfield_value"><xliff:g id="host" example="example.com">%1$s</xliff:g>:<xliff:g id="port" example="508">%2$d</xliff:g></string>
|
<string name="server_textfield_value"><xliff:g id="host" example="example.com">%1$s</xliff:g>:<xliff:g id="port" example="508">%2$d</xliff:g></string>
|
||||||
<string name="server_textfield_hint"><host>:<port></string>
|
<string name="server_textfield_hint"><host>:<port></string>
|
||||||
<string name="server_textfield_error">Invalid server</string>
|
<string name="server_textfield_error">Invalid server</string>
|
||||||
|
|
|
@ -26,7 +26,7 @@ ZCASH_ASCII_GPG_KEY=
|
||||||
# Configures whether release is an unstable snapshot, therefore published to the snapshot repository.
|
# Configures whether release is an unstable snapshot, therefore published to the snapshot repository.
|
||||||
IS_SNAPSHOT=true
|
IS_SNAPSHOT=true
|
||||||
|
|
||||||
LIBRARY_VERSION=2.0.7
|
LIBRARY_VERSION=2.0.8
|
||||||
|
|
||||||
# Kotlin compiler warnings can be considered errors, failing the build.
|
# Kotlin compiler warnings can be considered errors, failing the build.
|
||||||
ZCASH_IS_TREAT_WARNINGS_AS_ERRORS=true
|
ZCASH_IS_TREAT_WARNINGS_AS_ERRORS=true
|
||||||
|
|
|
@ -18,3 +18,13 @@ suspend fun Synchronizer.send(
|
||||||
),
|
),
|
||||||
spendingKey
|
spendingKey
|
||||||
)
|
)
|
||||||
|
|
||||||
|
suspend fun Synchronizer.proposeSend(
|
||||||
|
spendingKey: UnifiedSpendingKey,
|
||||||
|
send: ZecSend
|
||||||
|
) = proposeTransfer(
|
||||||
|
spendingKey.account,
|
||||||
|
send.destination.address,
|
||||||
|
send.amount,
|
||||||
|
send.memo.value
|
||||||
|
)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import cash.z.ecc.android.sdk.Synchronizer
|
import cash.z.ecc.android.sdk.Synchronizer
|
||||||
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
|
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
|
||||||
import cash.z.ecc.android.sdk.WalletInitMode
|
import cash.z.ecc.android.sdk.WalletInitMode
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
|
||||||
import cash.z.ecc.android.sdk.ext.onFirst
|
import cash.z.ecc.android.sdk.ext.onFirst
|
||||||
import cash.z.ecc.android.sdk.internal.Twig
|
import cash.z.ecc.android.sdk.internal.Twig
|
||||||
import cash.z.ecc.android.sdk.model.Account
|
import cash.z.ecc.android.sdk.model.Account
|
||||||
|
@ -116,7 +115,7 @@ class TestnetIntegrationTest : ScopedTest() {
|
||||||
synchronizer.proposeTransfer(
|
synchronizer.proposeTransfer(
|
||||||
spendingKey.account,
|
spendingKey.account,
|
||||||
toAddress,
|
toAddress,
|
||||||
ZcashSdk.MINERS_FEE,
|
Zatoshi(10_000L),
|
||||||
"first mainnet tx from the SDK"
|
"first mainnet tx from the SDK"
|
||||||
),
|
),
|
||||||
spendingKey
|
spendingKey
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
package cash.z.ecc.android.sdk.sample
|
package cash.z.ecc.android.sdk.sample
|
||||||
|
|
||||||
import androidx.test.filters.LargeTest
|
import androidx.test.filters.LargeTest
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
|
||||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||||
|
@ -20,7 +19,7 @@ import org.junit.Test
|
||||||
* the same data.
|
* the same data.
|
||||||
*/
|
*/
|
||||||
class TransparentRestoreSample {
|
class TransparentRestoreSample {
|
||||||
val txValue = Zatoshi(ZcashSdk.MINERS_FEE.value / 2)
|
val txValue = Zatoshi(Zatoshi(10_000L).value / 2)
|
||||||
|
|
||||||
// val walletA = SimpleWallet(SEED_PHRASE, "WalletA")
|
// val walletA = SimpleWallet(SEED_PHRASE, "WalletA")
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,19 @@ import cash.z.ecc.android.sdk.model.Zatoshi
|
||||||
* becomes easier to reduce privacy by segmenting the anonymity set of users, particularly as it
|
* becomes easier to reduce privacy by segmenting the anonymity set of users, particularly as it
|
||||||
* relates to network requests.
|
* relates to network requests.
|
||||||
*/
|
*/
|
||||||
@Suppress("MagicNumber")
|
|
||||||
object ZcashSdk {
|
object ZcashSdk {
|
||||||
/**
|
/**
|
||||||
* Miner's fee in zatoshi.
|
* Miner's fee in zatoshi.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
@Deprecated(
|
||||||
|
message = "Upcoming SDK 2.1 will create multiple transactions at once for some recipients.",
|
||||||
|
replaceWith =
|
||||||
|
ReplaceWith(
|
||||||
|
"proposeTransfer(usk.account, toAddress, amount, memo) OR " +
|
||||||
|
"proposeShielding(usk.account, shieldingThreshold, memo)"
|
||||||
|
)
|
||||||
|
)
|
||||||
val MINERS_FEE = Zatoshi(10_000L)
|
val MINERS_FEE = Zatoshi(10_000L)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package cash.z.ecc.android.sdk.fixture
|
package cash.z.ecc.android.sdk.fixture
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
|
||||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||||
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
||||||
import cash.z.ecc.android.sdk.model.TransactionOverview
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
||||||
import cash.z.ecc.android.sdk.model.TransactionState
|
import cash.z.ecc.android.sdk.model.TransactionState
|
||||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||||
|
|
||||||
|
@Suppress("MagicNumber")
|
||||||
object TransactionOverviewFixture {
|
object TransactionOverviewFixture {
|
||||||
const val ID: Long = 1
|
const val ID: Long = 1
|
||||||
val RAW_ID: FirstClassByteArray get() = FirstClassByteArray("rawId".toByteArray())
|
val RAW_ID: FirstClassByteArray get() = FirstClassByteArray("rawId".toByteArray())
|
||||||
|
@ -16,9 +16,8 @@ object TransactionOverviewFixture {
|
||||||
val RAW: FirstClassByteArray get() = FirstClassByteArray("raw".toByteArray())
|
val RAW: FirstClassByteArray get() = FirstClassByteArray("raw".toByteArray())
|
||||||
const val IS_SENT_TRANSACTION: Boolean = false
|
const val IS_SENT_TRANSACTION: Boolean = false
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
val NET_VALUE: Zatoshi = Zatoshi(10_000)
|
val NET_VALUE: Zatoshi = Zatoshi(10_000)
|
||||||
val FEE_PAID: Zatoshi = ZcashSdk.MINERS_FEE
|
val FEE_PAID: Zatoshi = Zatoshi(10_000)
|
||||||
const val IS_CHANGE: Boolean = false
|
const val IS_CHANGE: Boolean = false
|
||||||
const val RECEIVED_NOTE_COUNT: Int = 1
|
const val RECEIVED_NOTE_COUNT: Int = 1
|
||||||
const val SENT_NOTE_COUNT: Int = 0
|
const val SENT_NOTE_COUNT: Int = 0
|
||||||
|
|
|
@ -51,4 +51,8 @@ class Proposal(
|
||||||
fun totalFeeRequired(): Zatoshi {
|
fun totalFeeRequired(): Zatoshi {
|
||||||
return Zatoshi(inner.totalFeeRequired())
|
return Zatoshi(inner.totalFeeRequired())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toPrettyString(): String {
|
||||||
|
return "Transaction count: ${transactionCount()}, Total fee required: ${totalFeeRequired()}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue