Skip to content

Commit 93a5473

Browse files
authored
Merge pull request #255 from synonymdev/fix/transfer-spending-limit
Fix LSP balance calculation
2 parents 44e9b4d + c4379bf commit 93a5473

File tree

2 files changed

+60
-32
lines changed

2 files changed

+60
-32
lines changed

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

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.ui.Modifier
2323
import androidx.compose.ui.platform.LocalContext
2424
import androidx.compose.ui.res.stringResource
2525
import androidx.compose.ui.unit.dp
26+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2627
import kotlinx.coroutines.launch
2728
import to.bitkit.R
2829
import to.bitkit.models.Toast
@@ -60,6 +61,7 @@ fun SpendingAmountScreen(
6061
val blocktank = blocktankViewModel ?: return
6162
val currencies = LocalCurrencies.current
6263
val resources = LocalContext.current.resources
64+
val transferValues by viewModel.transferValues.collectAsStateWithLifecycle()
6365

6466
ScreenColumn {
6567
AppTopBar(
@@ -78,41 +80,39 @@ fun SpendingAmountScreen(
7880
var isLoading by remember { mutableStateOf(false) }
7981

8082
val availableAmount = LocalBalances.current.totalOnchainSats - 512u // default tx fee
81-
82-
var maxClientBalance by remember { mutableStateOf(0uL) }
83-
var maxLspBalance by remember { mutableStateOf(0uL) }
8483
var maxLspFee by remember { mutableStateOf(0uL) }
8584

8685
val feeMaximum = max(0, availableAmount.toLong() - maxLspFee.toLong())
87-
val maximum = min(maxClientBalance.toLong(), feeMaximum)
86+
val maximum = min(
87+
transferValues.maxClientBalance.toLong(),
88+
feeMaximum
89+
) // TODO USE MAX AVAILABLE TO TRANSFER INSTEAD OF MAX ONCHAIN BALANCE
8890

8991
// Update maxClientBalance Effect
9092
LaunchedEffect(satsAmount) {
91-
val transferValues = viewModel.calculateTransferValues(satsAmount.toULong())
92-
maxClientBalance = transferValues.maxClientBalance
93-
Logger.debug("maxClientBalance: $maxClientBalance", context = "SpendingAmountScreen")
94-
}
95-
96-
// Update maxLspBalance Effect
97-
LaunchedEffect(availableAmount) {
98-
val transferValues = viewModel.calculateTransferValues(availableAmount)
99-
maxLspBalance = transferValues.defaultLspBalance
100-
Logger.debug("maxLspBalance: $maxLspBalance", context = "SpendingAmountScreen")
93+
viewModel.updateTransferValues(satsAmount.toULong())
94+
Logger.debug(
95+
"satsAmount changed - maxClientBalance: ${transferValues.maxClientBalance}",
96+
context = "SpendingAmountScreen"
97+
)
10198
}
10299

103100
// Update maxLspFee Effect
104-
LaunchedEffect(availableAmount, maxLspBalance) {
101+
LaunchedEffect(availableAmount, transferValues.maxLspBalance) { // TODO MOVE TO VIEWMODEL
105102
runCatching {
106103
val estimate = blocktank.estimateOrderFee(
107104
spendingBalanceSats = availableAmount,
108-
receivingBalanceSats = maxLspBalance,
105+
receivingBalanceSats = transferValues.maxLspBalance,
109106
)
110107
maxLspFee = estimate.feeSat
111108
}
112109
}
113110

114111
Spacer(modifier = Modifier.height(32.dp))
115-
Display(text = stringResource(R.string.lightning__spending_amount__title).withAccent(accentColor = Colors.Purple))
112+
Display(
113+
text = stringResource(R.string.lightning__spending_amount__title)
114+
.withAccent(accentColor = Colors.Purple)
115+
)
116116
Spacer(modifier = Modifier.height(32.dp))
117117

118118
AmountInput(
@@ -167,11 +167,13 @@ fun SpendingAmountScreen(
167167
PrimaryButton(
168168
text = stringResource(R.string.common__continue),
169169
onClick = {
170-
if (maxLspBalance == 0uL) {
170+
if (transferValues.maxLspBalance == 0uL) {
171171
app.toast(
172172
type = Toast.ToastType.ERROR,
173173
title = resources.getString(R.string.lightning__spending_amount__error_max__title),
174-
description = resources.getString(R.string.lightning__spending_amount__error_max__description_zero),
174+
description = resources.getString(
175+
R.string.lightning__spending_amount__error_max__description_zero
176+
),
175177
)
176178
return@PrimaryButton
177179
}

app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,40 @@ class TransferViewModel @Inject constructor(
194194

195195
// The maximum channel size the user can open including existing channels
196196
val maxChannelSize2 = if (maxChannelSize1 > channelsSize) maxChannelSize1 - channelsSize else 0u
197-
val maxChannelSize = min(maxChannelSize1, maxChannelSize2)
197+
val maxChannelSizeAvailableToIncrease = min(maxChannelSize1, maxChannelSize2)
198198

199199
val minLspBalance = getMinLspBalance(clientBalanceSat, minChannelSizeSat)
200-
val maxLspBalance = if (maxChannelSize > clientBalanceSat) maxChannelSize - clientBalanceSat else 0u
200+
val maxLspBalance = if (maxChannelSizeAvailableToIncrease > clientBalanceSat) {
201+
maxChannelSizeAvailableToIncrease - clientBalanceSat
202+
} else {
203+
0u
204+
}
201205
val defaultLspBalance = getDefaultLspBalance(clientBalanceSat, maxLspBalance)
202-
val maxClientBalance = getMaxClientBalance(maxChannelSize)
206+
val maxClientBalance = getMaxClientBalance(maxChannelSizeAvailableToIncrease)
207+
208+
if (maxChannelSizeAvailableToIncrease < clientBalanceSat) { // TODO DISPLAY ERROR
209+
Logger.warn(
210+
"Amount clientBalanceSat:$clientBalanceSat too large, max possible: $maxChannelSizeAvailableToIncrease",
211+
context = TAG
212+
)
213+
}
214+
215+
if (defaultLspBalance < minLspBalance || defaultLspBalance > maxLspBalance) {
216+
Logger.warn(
217+
"Invalid defaultLspBalance:$defaultLspBalance " +
218+
"min possible:$maxLspBalance, " +
219+
"max possible: $minLspBalance",
220+
context = TAG
221+
)
222+
}
223+
224+
if (maxChannelSizeAvailableToIncrease <= 0u) {
225+
Logger.warn("Max channel size reached. current size: $channelsSize sats", context = TAG)
226+
}
227+
228+
if (maxClientBalance <= 0u) {
229+
Logger.warn("No liquidity available to purchase $maxClientBalance", context = TAG)
230+
}
203231

204232
return TransferValues(
205233
defaultLspBalance = defaultLspBalance,
@@ -225,16 +253,14 @@ class TransferViewModel @Inject constructor(
225253
throw ServiceError.CurrencyRateUnavailable
226254
}
227255

228-
// Safely calculate lspBalance to avoid arithmetic overflow
229-
var lspBalance: ULong = 0u
230-
if (defaultLspBalanceSats > clientBalanceSat) {
231-
lspBalance = defaultLspBalanceSats - clientBalanceSat
232-
}
233-
if (lspBalance > threshold1) {
234-
lspBalance = clientBalanceSat
235-
}
236-
if (lspBalance > threshold2) {
237-
lspBalance = maxLspBalance
256+
val lspBalance = if (clientBalanceSat < threshold1) { // 0-225€: LSP balance = 450€ - client balance
257+
defaultLspBalanceSats - clientBalanceSat
258+
} else if (clientBalanceSat < threshold2) { // 225-495€: LSP balance = client balance
259+
clientBalanceSat
260+
} else if (clientBalanceSat < maxLspBalance) { // 495-950€: LSP balance = max - client balance
261+
maxLspBalance - clientBalanceSat
262+
} else {
263+
maxLspBalance
238264
}
239265

240266
return min(lspBalance, maxLspBalance)

0 commit comments

Comments
 (0)