Skip to content

Commit df12c8c

Browse files
committed
Merge branch 'master' into feat/save-tags
2 parents 9289b4d + 5f2dbd6 commit df12c8c

File tree

11 files changed

+247
-189
lines changed

11 files changed

+247
-189
lines changed

app/src/androidTest/java/to/bitkit/services/BlocktankTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class BlocktankTest {
199199
announceChannel = true,
200200
)
201201

202-
val estimate: IBtEstimateFeeResponse2 = service.blocktank.newOrderFeeEstimate(
202+
val estimate: IBtEstimateFeeResponse2 = service.blocktank.estimateFee(
203203
lspBalanceSat = lspBalanceSat,
204204
channelExpiryWeeks = channelExpiryWeeks,
205205
options = options

app/src/main/java/to/bitkit/services/CoreService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,10 +415,10 @@ class BlocktankService(
415415
}
416416
}
417417

418-
suspend fun newOrderFeeEstimate(
418+
suspend fun estimateFee(
419419
lspBalanceSat: ULong,
420420
channelExpiryWeeks: UInt,
421-
options: CreateOrderOptions,
421+
options: CreateOrderOptions? = null,
422422
): IBtEstimateFeeResponse2 {
423423
return ServiceQueue.CORE.background {
424424
estimateOrderFeeFull(

app/src/main/java/to/bitkit/services/CurrencyService.kt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import to.bitkit.models.ConvertedAmount
77
import to.bitkit.models.FxRate
88
import to.bitkit.utils.AppError
99
import java.math.BigDecimal
10+
import java.math.RoundingMode
1011
import java.text.DecimalFormat
1112
import java.text.DecimalFormatSymbols
1213
import java.util.Locale
@@ -18,6 +19,8 @@ import kotlin.math.pow
1819
class CurrencyService @Inject constructor(
1920
private val blocktankHttpClient: BlocktankHttpClient,
2021
) {
22+
private var cachedRates: List<FxRate>? = null
23+
2124
private val maxRetries = 3
2225

2326
suspend fun fetchLatestRates(): List<FxRate> {
@@ -26,7 +29,12 @@ class CurrencyService @Inject constructor(
2629
for (attempt in 0 until maxRetries) {
2730
try {
2831
val response = ServiceQueue.FOREX.background { blocktankHttpClient.fetchLatestRates() }
29-
return response.tickers
32+
val rates = response.tickers
33+
34+
// TODO Cache to disk
35+
cachedRates = rates
36+
37+
return rates
3038
} catch (e: Exception) {
3139
lastError = e
3240
if (attempt < maxRetries - 1) {
@@ -40,9 +48,14 @@ class CurrencyService @Inject constructor(
4048
throw lastError ?: CurrencyError.Unknown
4149
}
4250

51+
fun loadCachedRates(): List<FxRate>? {
52+
// TODO load from disk
53+
return cachedRates
54+
}
55+
4356
fun convert(sats: Long, rate: FxRate): ConvertedAmount? {
4457
val btcAmount = BigDecimal(sats).divide(BigDecimal(100_000_000))
45-
val value: BigDecimal = btcAmount.multiply(BigDecimal(rate.rate))
58+
val value: BigDecimal = btcAmount.multiply(BigDecimal.valueOf(rate.rate))
4659

4760
val symbols = DecimalFormatSymbols(Locale.getDefault()).apply {
4861
decimalSeparator = '.'
@@ -64,6 +77,15 @@ class CurrencyService @Inject constructor(
6477
)
6578
}
6679

80+
fun convertFiatToSats(fiatValue: BigDecimal, rate: FxRate): ULong {
81+
val btcAmount = fiatValue.divide(BigDecimal.valueOf(rate.rate), 8, RoundingMode.HALF_UP)
82+
val satsDecimal = btcAmount.multiply(BigDecimal(100_000_000))
83+
84+
val roundedNumber = satsDecimal.setScale(0, RoundingMode.HALF_UP)
85+
86+
return roundedNumber.toLong().toULong()
87+
}
88+
6789
fun getAvailableCurrencies(rates: List<FxRate>): List<String> {
6890
return rates.map { it.quote }
6991
}

app/src/main/java/to/bitkit/services/LightningService.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import org.lightningdevkit.ldknode.Builder
1818
import org.lightningdevkit.ldknode.ChannelDetails
1919
import org.lightningdevkit.ldknode.EsploraSyncConfig
2020
import org.lightningdevkit.ldknode.Event
21-
import org.lightningdevkit.ldknode.LightningBalance
2221
import org.lightningdevkit.ldknode.LogLevel
2322
import org.lightningdevkit.ldknode.Network
2423
import org.lightningdevkit.ldknode.Node

app/src/main/java/to/bitkit/ui/components/TransferAmount.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ fun TransferAmount(
5454

5555
// Handle overrideSats changes
5656
LaunchedEffect(overrideSats) {
57-
overrideSats?.let { sats ->
58-
satsAmount = satsAmount.copy(sats.toString(), TextRange(sats.toString().length))
59-
onSatsChange(sats)
57+
overrideSats?.let { exactSats ->
58+
satsAmount = satsAmount.copy(exactSats.toString(), TextRange(exactSats.toString().length))
59+
onSatsChange(exactSats)
6060

6161
// Update fiat amount if needed
62-
currency.convert(sats)?.let { converted ->
62+
currency.convert(exactSats)?.let { converted ->
6363
fiatAmount = satsAmount.copy(converted.formatted, TextRange(converted.formatted.length))
6464
}
6565
}

app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAdvancedScreen.kt

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import androidx.compose.material3.Icon
1818
import androidx.compose.material3.IconButton
1919
import androidx.compose.runtime.Composable
2020
import androidx.compose.runtime.LaunchedEffect
21+
import androidx.compose.runtime.collectAsState
2122
import androidx.compose.runtime.getValue
2223
import androidx.compose.runtime.mutableLongStateOf
2324
import androidx.compose.runtime.mutableStateOf
@@ -44,7 +45,6 @@ import to.bitkit.ui.components.TransferAmount
4445
import to.bitkit.ui.scaffold.AppTopBar
4546
import to.bitkit.ui.scaffold.ScreenColumn
4647
import to.bitkit.ui.theme.Colors
47-
import to.bitkit.ui.utils.useTransfer
4848
import to.bitkit.ui.utils.withAccent
4949
import to.bitkit.viewmodels.TransferViewModel
5050

@@ -81,31 +81,35 @@ fun SpendingAdvancedScreen(
8181
.fillMaxSize()
8282
.imePadding()
8383
) {
84-
85-
var lspBalance by rememberSaveable { mutableLongStateOf(0) }
84+
var receivingSatsAmount by rememberSaveable { mutableLongStateOf(0) }
8685
var overrideSats: Long? by remember { mutableStateOf(null) }
8786

8887
val clientBalance = order.clientBalanceSat
89-
var fee: Long? by remember { mutableStateOf(null) }
88+
var feeEstimate: Long? by remember { mutableStateOf(null) }
9089
var isLoading by remember { mutableStateOf(false) }
9190

92-
val transferValues = useTransfer(clientBalance.toLong())
91+
val transferValues by viewModel.transferValues.collectAsState()
9392

94-
val isValid = lspBalance >= transferValues.minLspBalance &&
95-
lspBalance <= transferValues.maxLspBalance
93+
LaunchedEffect(order.clientBalanceSat) {
94+
viewModel.updateTransferValues(clientBalance, blocktank.info)
95+
}
9696

97-
// Fetch LSP Fee estimate
98-
LaunchedEffect(lspBalance) {
99-
fee = null
100-
if (lspBalance < transferValues.minLspBalance) {
101-
return@LaunchedEffect
102-
}
97+
val isValid = transferValues.let {
98+
val isAboveMin = receivingSatsAmount.toULong() >= it.minLspBalance
99+
val isBelowMax = receivingSatsAmount.toULong() <= it.maxLspBalance
100+
isAboveMin && isBelowMax
101+
}
102+
103+
// Update feeEstimate
104+
LaunchedEffect(receivingSatsAmount, transferValues) {
105+
feeEstimate = null
106+
if (!isValid) return@LaunchedEffect
103107
runCatching {
104108
val estimate = blocktank.estimateOrderFee(
105109
spendingBalanceSats = clientBalance,
106-
receivingBalanceSats = lspBalance.toULong(),
110+
receivingBalanceSats = receivingSatsAmount.toULong(),
107111
)
108-
fee = estimate.feeSat.toLong()
112+
feeEstimate = estimate.feeSat.toLong()
109113
}
110114
}
111115

@@ -120,7 +124,7 @@ fun SpendingAdvancedScreen(
120124
primaryDisplay = currencies.primaryDisplay,
121125
overrideSats = overrideSats,
122126
onSatsChange = { sats ->
123-
lspBalance = sats
127+
receivingSatsAmount = sats
124128
overrideSats = null
125129
},
126130
)
@@ -135,7 +139,7 @@ fun SpendingAdvancedScreen(
135139
color = Colors.White64,
136140
)
137141
Spacer(modifier = Modifier.width(4.dp))
138-
fee?.let {
142+
feeEstimate?.let {
139143
MoneySSB(it)
140144
} ?: run {
141145
Caption13Up(text = "", color = Colors.White64)
@@ -157,23 +161,23 @@ fun SpendingAdvancedScreen(
157161
text = stringResource(R.string.common__min),
158162
color = Colors.Purple,
159163
onClick = {
160-
overrideSats = transferValues.minLspBalance
164+
overrideSats = transferValues.minLspBalance.toLong()
161165
},
162166
)
163167
// Default Button
164168
NumberPadActionButton(
165169
text = stringResource(R.string.common__default),
166170
color = Colors.Purple,
167171
onClick = {
168-
overrideSats = order.lspBalanceSat.toLong()
172+
overrideSats = transferValues.defaultLspBalance.toLong()
169173
},
170174
)
171175
// Max Button
172176
NumberPadActionButton(
173177
text = stringResource(R.string.common__max),
174178
color = Colors.Purple,
175179
onClick = {
176-
overrideSats = transferValues.maxLspBalance
180+
overrideSats = transferValues.maxLspBalance.toLong()
177181
},
178182
)
179183
}
@@ -188,7 +192,7 @@ fun SpendingAdvancedScreen(
188192
try {
189193
val newOrder = blocktank.createOrder(
190194
spendingBalanceSats = clientBalance,
191-
receivingBalanceSats = lspBalance.toULong(),
195+
receivingBalanceSats = receivingSatsAmount.toULong(),
192196
)
193197
viewModel.onAdvancedOrderCreated(newOrder)
194198
onOrderCreated()

app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ import to.bitkit.ui.components.UnitButton
4444
import to.bitkit.ui.scaffold.AppTopBar
4545
import to.bitkit.ui.scaffold.ScreenColumn
4646
import to.bitkit.ui.theme.Colors
47-
import to.bitkit.ui.utils.useTransfer
4847
import to.bitkit.ui.utils.withAccent
48+
import to.bitkit.utils.Logger
4949
import to.bitkit.viewmodels.TransferViewModel
5050
import kotlin.math.max
5151
import kotlin.math.min
@@ -83,27 +83,39 @@ fun SpendingAmountScreen(
8383
.fillMaxSize()
8484
.imePadding()
8585
) {
86-
var spendingBalanceSats by rememberSaveable { mutableLongStateOf(0) }
86+
var satsAmount by rememberSaveable { mutableLongStateOf(0) }
8787
var overrideSats: Long? by remember { mutableStateOf(null) }
8888
var isLoading by remember { mutableStateOf(false) }
8989

90-
val balances = LocalBalances.current
90+
val availableAmount = LocalBalances.current.totalOnchainSats - 512u // default tx fee
9191

92-
val availableAmount = balances.totalOnchainSats - 512u // default tx fee
92+
var maxClientBalance by remember { mutableStateOf(0uL) }
93+
var maxLspBalance by remember { mutableStateOf(0uL) }
94+
var maxLspFee by remember { mutableStateOf(0uL) }
9395

94-
val maxClientBalance = useTransfer(spendingBalanceSats).maxClientBalance
95-
val maxLspBalance = useTransfer(availableAmount.toLong()).defaultLspBalance
96+
val feeMaximum = max(0, availableAmount.toLong() - maxLspFee.toLong())
97+
val maximum = min(maxClientBalance.toLong(), feeMaximum)
9698

97-
var maxLspFee by remember { mutableStateOf(0uL) }
99+
// Update maxClientBalance Effect
100+
LaunchedEffect(satsAmount) {
101+
val transferValues = viewModel.calculateTransferValues(satsAmount.toULong(), blocktank.info)
102+
maxClientBalance = transferValues.maxClientBalance
103+
Logger.debug("maxClientBalance: $maxClientBalance", context = "SpendingAmountScreen")
104+
}
98105

99-
val feeMaximum = max(0, (availableAmount - maxLspFee).toLong())
100-
val maximum = min(maxClientBalance, feeMaximum)
106+
// Update maxLspBalance Effect
107+
LaunchedEffect(availableAmount) {
108+
val transferValues = viewModel.calculateTransferValues(availableAmount, blocktank.info)
109+
maxLspBalance = transferValues.defaultLspBalance
110+
Logger.debug("maxLspBalance: $maxLspBalance", context = "SpendingAmountScreen")
111+
}
101112

113+
// Update maxLspFee Effect
102114
LaunchedEffect(availableAmount, maxLspBalance) {
103115
runCatching {
104116
val estimate = blocktank.estimateOrderFee(
105117
spendingBalanceSats = availableAmount,
106-
receivingBalanceSats = maxLspBalance.toULong(),
118+
receivingBalanceSats = maxLspBalance,
107119
)
108120
maxLspFee = estimate.feeSat
109121
}
@@ -117,7 +129,7 @@ fun SpendingAmountScreen(
117129
primaryDisplay = currencies.primaryDisplay,
118130
overrideSats = overrideSats,
119131
onSatsChange = { sats ->
120-
spendingBalanceSats = sats
132+
satsAmount = sats
121133
overrideSats = null
122134
},
123135
)
@@ -165,7 +177,7 @@ fun SpendingAmountScreen(
165177
PrimaryButton(
166178
text = stringResource(R.string.common__continue),
167179
onClick = {
168-
if (maxLspBalance == 0L) {
180+
if (maxLspBalance == 0uL) {
169181
app.toast(
170182
type = Toast.ToastType.ERROR,
171183
title = resources.getString(R.string.lightning__spending_amount__error_max__title),
@@ -177,7 +189,7 @@ fun SpendingAmountScreen(
177189
isLoading = true
178190
scope.launch {
179191
try {
180-
val order = blocktank.createOrder(spendingBalanceSats.toULong())
192+
val order = blocktank.createOrder(satsAmount.toULong())
181193
viewModel.onOrderCreated(order)
182194
onOrderCreated()
183195
} catch (e: Throwable) {
@@ -187,7 +199,7 @@ fun SpendingAmountScreen(
187199
}
188200
}
189201
},
190-
enabled = !isLoading && spendingBalanceSats != 0L,
202+
enabled = !isLoading && satsAmount != 0L,
191203
isLoading = isLoading,
192204
)
193205

app/src/main/java/to/bitkit/ui/shared/Channels.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ internal fun Channels(
7575
channelId = it.channelId,
7676
outbound = outbound.toLong(),
7777
inbound = inbound.toLong(),
78+
confirmationsText = "Confirmations: ${it.confirmations ?: 0u}/${it.confirmationsRequired ?: 0u}",
7879
onClose = { onChannelCloseTap(it) },
7980
)
8081
}
@@ -99,6 +100,7 @@ private fun ChannelItem(
99100
channelId: String,
100101
outbound: Long,
101102
inbound: Long,
103+
confirmationsText: String,
102104
onClose: () -> Unit,
103105
) {
104106
Column(
@@ -151,16 +153,18 @@ private fun ChannelItem(
151153
}
152154
Column {
153155
val style = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.Normal)
156+
Text("Usable: ${if (isUsable) "" else ""}", style = style)
154157
Text("Announced: $isAnnounced", style = style)
155-
Text("Usable: $isUsable", style = style)
156158
Text("Inbound htlc max: " + moneyString(inboundHtlcMax), style = style)
157159
Text("Inbound htlc min: " + moneyString(inboundHtlcMin), style = style)
158160
Text("Next outbound htlc limit: " + moneyString(nextOutboundHtlcLimit), style = style)
159161
Text("Next outbound htlc min: " + moneyString(nextOutboundHtlcMin), style = style)
162+
Text(confirmationsText, style = style)
160163
}
161164
}
162165
}
163166

167+
@Suppress("SpellCheckingInspection")
164168
@Preview(showBackground = true)
165169
@Composable
166170
private fun ChannelItemPreview() {
@@ -177,6 +181,7 @@ private fun ChannelItemPreview() {
177181
inboundHtlcMin = 246L,
178182
nextOutboundHtlcLimit = 531L,
179183
nextOutboundHtlcMin = 762L,
184+
confirmationsText = "Confirmations: 1/2",
180185
)
181186
}
182187
}

0 commit comments

Comments
 (0)