disable exchange for lightning + save before translation updates

This commit is contained in:
Matthew Fosse 2024-03-07 09:26:23 -08:00
parent 29fc88d841
commit 32350b8ab2
8 changed files with 267 additions and 272 deletions

View file

@ -8,10 +8,17 @@ import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart';
double lightningDoubleToBitcoinDouble({required double amount}) {
return amount / 100000000;
}
double bitcoinDoubleToLightningDouble({required double amount}) {
return amount * 100000000;
}
class TrocadorExchangeProvider extends ExchangeProvider {
TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}})
: _lastUsedRateId = '',
@ -100,8 +107,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
// trocador treats btcln as just bitcoin amounts:
if (from == CryptoCurrency.btcln) {
return Limits(
min: lightning!.bitcoinDoubleToLightningDouble(amount: (coinJson['minimum'] as double)),
max: lightning!.bitcoinDoubleToLightningDouble(amount: (coinJson['maximum'] as double)),
min: bitcoinDoubleToLightningDouble(amount: (coinJson['minimum'] as double)),
max: bitcoinDoubleToLightningDouble(amount: (coinJson['maximum'] as double)),
);
}
@ -124,7 +131,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
double amt = amount;
if (from == CryptoCurrency.btcln) {
amt = lightning!.lightningDoubleToBitcoinDouble(amount: amount);
amt = lightningDoubleToBitcoinDouble(amount: amount);
}
final params = <String, String>{
@ -164,10 +171,10 @@ class TrocadorExchangeProvider extends ExchangeProvider {
double fromAmt = double.parse(request.fromAmount);
double toAmt = double.parse(request.toAmount);
if (request.fromCurrency == CryptoCurrency.btcln) {
fromAmt = lightning!.lightningDoubleToBitcoinDouble(amount: fromAmt);
fromAmt = lightningDoubleToBitcoinDouble(amount: fromAmt);
}
if (request.toCurrency == CryptoCurrency.btcln) {
toAmt = lightning!.lightningDoubleToBitcoinDouble(amount: toAmt);
toAmt = lightningDoubleToBitcoinDouble(amount: toAmt);
}
final params = {
@ -187,7 +194,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
double amt = double.tryParse(request.toAmount) ?? 0;
if (request.fromCurrency == CryptoCurrency.btcln) {
amt = lightning!.lightningDoubleToBitcoinDouble(amount: amt);
amt = lightningDoubleToBitcoinDouble(amount: amt);
}
if (isFixedRateMode) {
@ -242,9 +249,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
String? responseAmount = responseJSON['amount_from']?.toString();
if (request.fromCurrency == CryptoCurrency.btcln && responseAmount != null) {
responseAmount = lightning!
.bitcoinDoubleToLightningDouble(amount: double.parse(responseAmount))
.toString();
responseAmount =
bitcoinDoubleToLightningDouble(amount: double.parse(responseAmount)).toString();
}
responseAmount ??= fromAmt.toString();

View file

@ -604,6 +604,7 @@ class ExchangePage extends BasePage {
initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
isAmountEstimated: false,
hasRefundAddress: true,
hasAddress: exchangeViewModel.hasAddress,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.depositCurrencies,
onCurrencySelected: (currency) {

View file

@ -32,6 +32,7 @@ class ExchangeCard extends StatefulWidget {
this.title = '',
this.initialIsAddressEditable = true,
this.hasRefundAddress = false,
this.hasAddress = true,
this.isMoneroWallet = false,
this.currencyButtonColor = Colors.transparent,
this.addressButtonsColor = Colors.transparent,
@ -55,6 +56,7 @@ class ExchangeCard extends StatefulWidget {
final bool initialIsAddressEditable;
final bool isAmountEstimated;
final bool hasRefundAddress;
final bool hasAddress;
final bool isMoneroWallet;
final Image imageArrow;
final Color currencyButtonColor;
@ -76,15 +78,15 @@ class ExchangeCard extends StatefulWidget {
class ExchangeCardState extends State<ExchangeCard> {
ExchangeCardState()
: _title = '',
_min = '',
_max = '',
_isAmountEditable = false,
_isAddressEditable = false,
_walletName = '',
_selectedCurrency = CryptoCurrency.btc,
_isAmountEstimated = false,
_isMoneroWallet = false;
: _title = '',
_min = '',
_max = '',
_isAmountEditable = false,
_isAddressEditable = false,
_walletName = '',
_selectedCurrency = CryptoCurrency.btc,
_isAmountEstimated = false,
_isMoneroWallet = false;
final addressController = TextEditingController();
final amountController = TextEditingController();
@ -168,8 +170,7 @@ class ExchangeCardState extends State<ExchangeCard> {
return Container(
width: double.infinity,
color: Colors.transparent,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <
Widget>[
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
@ -202,40 +203,41 @@ class ExchangeCardState extends State<ExchangeCard> {
),
Text(_selectedCurrency.toString(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.white))
fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white))
]),
),
),
_selectedCurrency.tag != null ? Padding(
padding: const EdgeInsets.only(right:3.0),
child: Container(
height: 32,
decoration: BoxDecoration(
color: widget.addressButtonsColor ??
Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Center(
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(_selectedCurrency.tag!,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor)),
),
),
),
) : Container(),
_selectedCurrency.tag != null
? Padding(
padding: const EdgeInsets.only(right: 3.0),
child: Container(
height: 32,
decoration: BoxDecoration(
color: widget.addressButtonsColor ??
Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonColor,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: Center(
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(_selectedCurrency.tag!,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor)),
),
),
),
)
: Container(),
Padding(
padding: const EdgeInsets.only(right: 4.0),
child: Text(':',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.white)),
fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white)),
),
Expanded(
child: Row(
@ -249,26 +251,23 @@ class ExchangeCardState extends State<ExchangeCard> {
controller: amountController,
enabled: _isAmountEditable,
textAlign: TextAlign.left,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
keyboardType:
TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [
FilteringTextInputFormatter.deny(
RegExp('[\\-|\\ ]'))
FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]'))
],
hintText: '0.0000',
borderColor: Colors.transparent,
//widget.borderColor,
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
validator: _isAmountEditable
? widget.currencyValueValidator
: null),
color: Theme.of(context)
.extension<ExchangePageTheme>()!
.hintTextColor),
validator: _isAmountEditable ? widget.currencyValueValidator : null),
),
),
if (widget.hasAllAmount)
@ -276,9 +275,10 @@ class ExchangeCardState extends State<ExchangeCard> {
height: 32,
width: 32,
decoration: BoxDecoration(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonColor,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: InkWell(
onTap: () => widget.allAmount?.call(),
child: Center(
@ -287,7 +287,9 @@ class ExchangeCardState extends State<ExchangeCard> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor)),
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor)),
),
),
)
@ -296,191 +298,168 @@ class ExchangeCardState extends State<ExchangeCard> {
),
],
)),
Divider(
height: 1,
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
Divider(height: 1, color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor),
Padding(
padding: EdgeInsets.only(top: 5),
child: Container(
height: 15,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
_min != null
? Text(
S
.of(context)
.min_value(_min ?? '', _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
)
: Offstage(),
_min != null ? SizedBox(width: 10) : Offstage(),
_max != null
? Text(
S
.of(context)
.max_value(_max ?? '', _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor))
: Offstage(),
])),
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[
_min != null
? Text(
S.of(context).min_value(_min ?? '', _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
)
: Offstage(),
_min != null ? SizedBox(width: 10) : Offstage(),
_max != null
? Text(S.of(context).max_value(_max ?? '', _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor))
: Offstage(),
])),
),
!_isAddressEditable && widget.hasRefundAddress
? Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
S.of(context).refund_address,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
))
: Offstage(),
_isAddressEditable
? FocusTraversalOrder(
order: NumericFocusOrder(2),
child: Padding(
if (widget.hasAddress) ...[
!_isAddressEditable && widget.hasRefundAddress
? Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
focusNode: widget.addressFocusNode,
controller: addressController,
onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri);
addressController.text = paymentRequest.address;
if (amountController.text.isNotEmpty) {
_showAmountPopup(context, paymentRequest);
return;
}
widget.amountFocusNode?.requestFocus();
amountController.text = paymentRequest.amount;
},
placeholder: widget.hasRefundAddress
? S.of(context).refund_address
: null,
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
],
isBorderExist: false,
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
hintStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency
),
),
)
: Padding(
padding: EdgeInsets.only(top: 10),
child: Builder(
builder: (context) => Stack(children: <Widget>[
FocusTraversalOrder(
order: NumericFocusOrder(3),
child: BaseTextFormField(
controller: addressController,
borderColor: Colors.transparent,
suffixIcon:
SizedBox(width: _isMoneroWallet ? 80 : 36),
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
validator: widget.addressTextFieldValidator),
),
Positioned(
top: 2,
right: 0,
child: SizedBox(
width: _isMoneroWallet ? 80 : 36,
child: Row(children: <Widget>[
if (_isMoneroWallet)
Padding(
padding: EdgeInsets.only(left: 10),
child: Container(
width: 34,
height: 34,
padding: EdgeInsets.only(top: 0),
child: Semantics(
label: S.of(context).address_book,
child: InkWell(
onTap: () async {
final contact =
await Navigator.of(context)
.pushNamed(
Routes.pickerAddressBook,
arguments: widget.initialCurrency,
);
child: Text(
S.of(context).refund_address,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
))
: Offstage(),
_isAddressEditable
? FocusTraversalOrder(
order: NumericFocusOrder(2),
child: Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
focusNode: widget.addressFocusNode,
controller: addressController,
onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri);
addressController.text = paymentRequest.address;
if (contact is ContactBase &&
contact.address != null) {
setState(() =>
addressController.text =
contact.address);
widget.onPushAddressBookButton
?.call(context);
}
},
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: widget
.addressButtonsColor,
borderRadius:
BorderRadius.all(
Radius.circular(
6))),
child: Image.asset(
'assets/images/open_book.png',
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
)),
),
)),
),
Padding(
padding: EdgeInsets.only(left: 2),
child: Container(
width: 34,
height: 34,
padding: EdgeInsets.only(top: 0),
child: Semantics(
label: S.of(context).copy_address,
child: InkWell(
onTap: () {
Clipboard.setData(ClipboardData(
text: addressController
.text));
showBar<void>(
context,
S
.of(context)
.copied_to_clipboard);
},
child: Container(
padding: EdgeInsets.fromLTRB(
8, 8, 0, 8),
color: Colors.transparent,
child: copyImage),
),
)))
])))
])),
),
if (amountController.text.isNotEmpty) {
_showAmountPopup(context, paymentRequest);
return;
}
widget.amountFocusNode?.requestFocus();
amountController.text = paymentRequest.amount;
},
placeholder: widget.hasRefundAddress ? S.of(context).refund_address : null,
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
],
isBorderExist: false,
textStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
hintStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ExchangePageTheme>()!.hintTextColor),
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency),
),
)
: Padding(
padding: EdgeInsets.only(top: 10),
child: Builder(
builder: (context) => Stack(children: <Widget>[
FocusTraversalOrder(
order: NumericFocusOrder(3),
child: BaseTextFormField(
controller: addressController,
borderColor: Colors.transparent,
suffixIcon: SizedBox(width: _isMoneroWallet ? 80 : 36),
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
validator: widget.addressTextFieldValidator),
),
Positioned(
top: 2,
right: 0,
child: SizedBox(
width: _isMoneroWallet ? 80 : 36,
child: Row(children: <Widget>[
if (_isMoneroWallet)
Padding(
padding: EdgeInsets.only(left: 10),
child: Container(
width: 34,
height: 34,
padding: EdgeInsets.only(top: 0),
child: Semantics(
label: S.of(context).address_book,
child: InkWell(
onTap: () async {
final contact =
await Navigator.of(context).pushNamed(
Routes.pickerAddressBook,
arguments: widget.initialCurrency,
);
if (contact is ContactBase &&
contact.address != null) {
setState(() =>
addressController.text = contact.address);
widget.onPushAddressBookButton?.call(context);
}
},
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: widget.addressButtonsColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png',
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor,
)),
),
)),
),
Padding(
padding: EdgeInsets.only(left: 2),
child: Container(
width: 34,
height: 34,
padding: EdgeInsets.only(top: 0),
child: Semantics(
label: S.of(context).copy_address,
child: InkWell(
onTap: () {
Clipboard.setData(ClipboardData(
text: addressController.text));
showBar<void>(
context, S.of(context).copied_to_clipboard);
},
child: Container(
padding: EdgeInsets.fromLTRB(8, 8, 0, 8),
color: Colors.transparent,
child: copyImage),
),
)))
])))
])),
),
],
]),
);
}
@ -514,7 +493,6 @@ class ExchangeCardState extends State<ExchangeCard> {
Navigator.of(context).pop();
},
actionLeftButton: () => Navigator.of(dialogContext).pop());
}
);
});
}
}

View file

@ -171,14 +171,14 @@ class LightningInvoicePage extends BasePage {
late String finalText;
InvoiceSoftLimitsResult limits =
snapshot.data as InvoiceSoftLimitsResult;
if (limits.balance == 0) {
if (limits.inboundLiquidity == 0) {
finalText = S
.of(context)
.lightning_invoice_min(lightning!.satsToLightningString(limits.min));
.lightning_invoice_min(lightning!.satsToLightningString(limits.minFee));
} else {
finalText = S.of(context).lightning_invoice_min_max(
lightning!.satsToLightningString(limits.min),
lightning!.satsToLightningString(limits.max),
lightning!.satsToLightningString(limits.minFee),
lightning!.satsToLightningString(limits.inboundLiquidity),
);
}

View file

@ -333,7 +333,12 @@ abstract class DashboardViewModelBase with Store {
void furtherShowYatPopup(bool shouldShow) => settingsStore.shouldShowYatPopup = shouldShow;
@computed
bool get isEnabledExchangeAction => settingsStore.exchangeStatus != ExchangeApiMode.disabled;
bool get isEnabledExchangeAction {
if (wallet.type == WalletType.lightning) {
return false;
}
return settingsStore.exchangeStatus != ExchangeApiMode.disabled;
}
@observable
bool hasExchangeAction;

View file

@ -271,6 +271,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
bool get isMoneroWallet => wallet.type == WalletType.monero;
// lightning doesn't have the same concept of "addresses" (since it uses invoices)
bool get hasAddress => wallet.type != WalletType.lightning;
bool get isLowFee {
switch (wallet.type) {
case WalletType.monero:

View file

@ -110,8 +110,8 @@ abstract class LightningInvoicePageViewModelBase with Store {
Future<void> _fetchLimits() async {
final limits = await lightningViewModel.invoiceSoftLimitsSats();
minimum = limits.min.toDouble();
maximum = limits.max.toDouble();
minimum = limits.minFee.toDouble();
maximum = limits.inboundLiquidity.toDouble();
}
@action

View file

@ -44,29 +44,30 @@ abstract class LightningViewModelBase with Store {
}
Future<InvoiceSoftLimitsResult> invoiceSoftLimitsSats() async {
final sdk = await BreezSDK();
BZG.ReceivePaymentRequest? req = null;
req = BZG.ReceivePaymentRequest(
amountMsat: 10000 * 1000, // 10000 sats
description: "limits",
);
final res = await sdk.receivePayment(req: req);
int min = (res.openingFeeMsat ?? (2500 * 1000)) ~/ 1000;
int max = 1000000000 * 1000 * 10; // 10 BTC
double feePercent = 0.4;
int minFee = (2500 * 1000) ~/ 1000; // 2500 sats
int inboundLiquidity = 1000000000 * 1000 * 10; // 10 BTC
int balance = 0;
final sdk = await BreezSDK();
try {
final nodeState = (await sdk.nodeInfo())!;
max = nodeState.inboundLiquidityMsats ~/ 1000;
balance = nodeState.channelsBalanceMsat ~/ 1000;
if (balance > 0) {
min = 0;
inboundLiquidity = nodeState.inboundLiquidityMsats ~/ 1000;
final openingFees = await sdk.openChannelFee(
req: BZG.OpenChannelFeeRequest(amountMsat: inboundLiquidity + 1));
if (openingFees.usedFeeParams != null) {
feePercent = (openingFees.usedFeeParams!.proportional * 100) / 1000000;
minFee = openingFees.usedFeeParams!.minMsat ~/ 1000;
}
balance = nodeState.channelsBalanceMsat ~/ 1000;
} catch (_) {}
return InvoiceSoftLimitsResult(
min: min,
max: max,
minFee: minFee,
inboundLiquidity: inboundLiquidity,
feePercent: feePercent,
balance: balance,
);
}
@ -82,7 +83,6 @@ abstract class LightningViewModelBase with Store {
}
}
class ReceiveOnchainResult {
final String bitcoinAddress;
final int minAllowedDeposit;
@ -98,13 +98,15 @@ class ReceiveOnchainResult {
}
class InvoiceSoftLimitsResult {
final int min;
final int max;
final double feePercent;
final int minFee;
final int inboundLiquidity;
final int balance;
InvoiceSoftLimitsResult({
required this.min,
required this.max,
required this.inboundLiquidity,
required this.feePercent,
required this.minFee,
required this.balance,
});
}
}