From 79faeb98afe3395f0a98dbd58e3fa28d291c2508 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 20 May 2025 21:25:00 +0300 Subject: [PATCH 01/30] add cake relay server --- cw_bitcoin/lib/payjoin/manager.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_bitcoin/lib/payjoin/manager.dart b/cw_bitcoin/lib/payjoin/manager.dart index b80fa777c..7ba3ceb9b 100644 --- a/cw_bitcoin/lib/payjoin/manager.dart +++ b/cw_bitcoin/lib/payjoin/manager.dart @@ -28,6 +28,7 @@ class PayjoinManager { static const List ohttpRelayUrls = [ 'https://pj.bobspacebkk.com', 'https://ohttp.achow101.com', + 'https://ohttp.cakewallet.com', ]; static Future randomOhttpRelayUrl() => PayjoinUri.Url.fromStr( From 43808992ec852b28969d6f45d11a8259c0d26fe2 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Wed, 21 May 2025 00:24:30 +0200 Subject: [PATCH 02/30] misc-seed-page-fixes (#2280) * feat: Trim pasted seed * fix: Fix Seed Type selection --- .../wallet_restore_from_seed_form.dart | 35 +++++++++++++------ lib/src/widgets/seed_widget.dart | 6 ++-- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index 431574b38..deffa0748 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -82,8 +82,11 @@ class WalletRestoreFromSeedFormState extends State { @override void initState() { - // _setSeedType(widget.seedTypeViewModel.moneroSeedType); - _setSeedType(MoneroSeedType.defaultSeedType); + if (widget.type == WalletType.monero) { + _setSeedType(widget.seedSettingsViewModel.moneroSeedType); + } else { + _setSeedType(MoneroSeedType.defaultSeedType); + } _setLanguageLabel(language); if (passwordTextEditingController != null) { @@ -118,7 +121,8 @@ class WalletRestoreFromSeedFormState extends State { } if (repeatedPasswordListener != null) { - repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); + repeatedPasswordTextEditingController + ?.removeListener(repeatedPasswordListener!); } super.dispose(); @@ -158,11 +162,13 @@ class WalletRestoreFromSeedFormState extends State { alignment: Alignment.centerRight, children: [ BaseTextFormField( - key: ValueKey('wallet_restore_from_seed_wallet_name_textfield_key'), + key: ValueKey( + 'wallet_restore_from_seed_wallet_name_textfield_key'), controller: nameTextEditingController, hintText: S.of(context).wallet_name, suffixIcon: IconButton( - key: ValueKey('wallet_restore_from_seed_wallet_name_refresh_button_key'), + key: ValueKey( + 'wallet_restore_from_seed_wallet_name_refresh_button_key'), onPressed: () async { final rName = await generateName(); FocusManager.instance.primaryFocus?.unfocus(); @@ -202,12 +208,15 @@ class WalletRestoreFromSeedFormState extends State { language: language, type: widget.type, onSeedChange: onSeedChange, - seedTextFieldKey: ValueKey('wallet_restore_from_seed_wallet_seeds_textfield_key'), - pasteButtonKey: ValueKey('wallet_restore_from_seed_wallet_seeds_paste_button_key'), + seedTextFieldKey: + ValueKey('wallet_restore_from_seed_wallet_seeds_textfield_key'), + pasteButtonKey: ValueKey( + 'wallet_restore_from_seed_wallet_seeds_paste_button_key'), ), if ([WalletType.monero, WalletType.wownero].contains(widget.type)) GestureDetector( - key: ValueKey('wallet_restore_from_seed_seedtype_picker_button_key'), + key: ValueKey( + 'wallet_restore_from_seed_seedtype_picker_button_key'), onTap: () async { await showPopUp( context: context, @@ -215,7 +224,9 @@ class WalletRestoreFromSeedFormState extends State { items: _getItems(), selectedAtIndex: isPolyseed ? 1 - : (seedTypeController.value.text.contains("14") && widget.type == WalletType.wownero) || isBip39 + : (seedTypeController.value.text.contains("14") && + widget.type == WalletType.wownero) || + isBip39 ? 2 : 0, mainAxisAlignment: MainAxisAlignment.start, @@ -249,7 +260,8 @@ class WalletRestoreFromSeedFormState extends State { obscureText: true) ], if (widget.displayLanguageSelector) - if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector) + if (!seedTypeController.value.text.contains("14") && + widget.displayLanguageSelector) GestureDetector( onTap: () async { await showPopUp( @@ -279,7 +291,8 @@ class WalletRestoreFromSeedFormState extends State { BlockchainHeightWidget( focusNode: widget.blockHeightFocusNode, key: blockchainHeightKey, - blockHeightTextFieldKey: ValueKey('wallet_restore_from_seed_blockheight_textfield_key'), + blockHeightTextFieldKey: ValueKey( + 'wallet_restore_from_seed_blockheight_textfield_key'), onHeightOrDateEntered: widget.onHeightOrDateEntered, hasDatePicker: [WalletType.monero, WalletType.wownero].contains(widget.type), diff --git a/lib/src/widgets/seed_widget.dart b/lib/src/widgets/seed_widget.dart index 9b3726afb..5a43a6808 100644 --- a/lib/src/widgets/seed_widget.dart +++ b/lib/src/widgets/seed_widget.dart @@ -146,12 +146,12 @@ class SeedWidgetState extends State { } Future _pasteText() async { - final value = await Clipboard.getData('text/plain'); + final value = (await Clipboard.getData('text/plain'))?.text?.trim(); - if (value?.text?.isNotEmpty ?? false) { + if (value?.isNotEmpty ?? false) { setState(() { _showPlaceholder = false; - controller.text = value!.text!; + controller.text = value!; }); } } From b872b43b75953755ed3a8f377831e8acef93f0a1 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 21 May 2025 05:19:23 +0300 Subject: [PATCH 03/30] fix hive error --- cw_bitcoin/lib/litecoin_wallet.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 662d70d67..08c56c600 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -464,7 +464,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final oldBox = await CakeHive.openBox(oldBoxName); mwebUtxosBox = await CakeHive.openBox(newBoxName); for (final key in oldBox.keys) { - await mwebUtxosBox.put(key, oldBox.get(key)!); + final value = oldBox.get(key); + await oldBox.delete(key); + await mwebUtxosBox.put(key, value!); } oldBox.deleteFromDisk(); From d356d5bfcbe6c9a453d214695af506d15e5b2975 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 22 May 2025 15:01:51 +0300 Subject: [PATCH 04/30] change exchange dock item to buy --- lib/entities/main_actions.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/entities/main_actions.dart b/lib/entities/main_actions.dart index 0cb2e4058..76db7fa77 100644 --- a/lib/entities/main_actions.dart +++ b/lib/entities/main_actions.dart @@ -70,7 +70,7 @@ class MainActions { ); static MainActions tradeAction = MainActions._( - name: (context) => S.of(context).exchange, + name: (context) => S.of(context).buy, image: 'assets/images/buy_sell.png', isEnabled: (viewModel) => viewModel.isEnabledTradeAction, canShow: (viewModel) => viewModel.hasTradeAction, From 1aac17676de86b68f4c00b1a4ba05b58b65c1dff Mon Sep 17 00:00:00 2001 From: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Date: Thu, 22 May 2025 22:32:18 +0100 Subject: [PATCH 05/30] feat: Migrate to EtherScan v2 API for supported EVM chains (#2264) --- cw_ethereum/lib/ethereum_client.dart | 6 ++++-- cw_polygon/lib/polygon_client.dart | 10 ++++++---- .../dashboard/home_settings_view_model.dart | 14 ++++++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart index 9d50fdd5b..1298d54f5 100644 --- a/cw_ethereum/lib/ethereum_client.dart +++ b/cw_ethereum/lib/ethereum_client.dart @@ -19,7 +19,8 @@ class EthereumClient extends EVMChainClient { Future> fetchTransactions(String address, {String? contractAddress}) async { try { - final response = await httpClient.get(Uri.https("api.etherscan.io", "/api", { + final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", { + "chainid": "$chainId", "module": "account", "action": contractAddress != null ? "tokentx" : "txlist", if (contractAddress != null) "contractaddress": contractAddress, @@ -50,7 +51,8 @@ class EthereumClient extends EVMChainClient { @override Future> fetchInternalTransactions(String address) async { try { - final response = await httpClient.get(Uri.https("api.etherscan.io", "/api", { + final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", { + "chainid": "$chainId", "module": "account", "action": "txlistinternal", "address": address, diff --git a/cw_polygon/lib/polygon_client.dart b/cw_polygon/lib/polygon_client.dart index cb8331977..7e9e882fa 100644 --- a/cw_polygon/lib/polygon_client.dart +++ b/cw_polygon/lib/polygon_client.dart @@ -40,12 +40,13 @@ class PolygonClient extends EVMChainClient { Future> fetchTransactions(String address, {String? contractAddress}) async { try { - final response = await httpClient.get(Uri.https("api.polygonscan.com", "/api", { + final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", { + "chainid": "$chainId", "module": "account", "action": contractAddress != null ? "tokentx" : "txlist", if (contractAddress != null) "contractaddress": contractAddress, "address": address, - "apikey": secrets.polygonScanApiKey, + "apikey": secrets.etherScanApiKey, })); final jsonResponse = json.decode(response.body) as Map; @@ -67,11 +68,12 @@ class PolygonClient extends EVMChainClient { @override Future> fetchInternalTransactions(String address) async { try { - final response = await httpClient.get(Uri.https("api.polygonscan.io", "/api", { + final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", { + "chainid": "$chainId", "module": "account", "action": "txlistinternal", "address": address, - "apikey": secrets.polygonScanApiKey, + "apikey": secrets.etherScanApiKey, })); final jsonResponse = json.decode(response.body) as Map; diff --git a/lib/view_model/dashboard/home_settings_view_model.dart b/lib/view_model/dashboard/home_settings_view_model.dart index 7bba04b21..4794746c4 100644 --- a/lib/view_model/dashboard/home_settings_view_model.dart +++ b/lib/view_model/dashboard/home_settings_view_model.dart @@ -297,13 +297,14 @@ abstract class HomeSettingsViewModelBase with Store { required bool isEthereum, }) async { final uri = Uri.https( - isEthereum ? "api.etherscan.io" : "api.polygonscan.com", - "/api", + "api.etherscan.io", + "/v2/api", { + "chainid": isEthereum ? "1" : "137", "module": "token", "action": "tokeninfo", "contractaddress": contractAddress, - "apikey": isEthereum ? secrets.etherScanApiKey : secrets.polygonScanApiKey, + "apikey": secrets.etherScanApiKey, }, ); @@ -338,13 +339,14 @@ abstract class HomeSettingsViewModelBase with Store { required bool isEthereum, }) async { final uri = Uri.https( - isEthereum ? "api.etherscan.io" : "api.polygonscan.com", - "/api", + "api.etherscan.io", + "/v2/api", { + "chainid": isEthereum ? "1" : "137", "module": "contract", "action": "getsourcecode", "address": contractAddress, - "apikey": isEthereum ? secrets.etherScanApiKey : secrets.polygonScanApiKey, + "apikey": secrets.etherScanApiKey, }, ); From 939e5d9279604272559f6df911bc326ed7677eab Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Sat, 24 May 2025 03:24:06 +0200 Subject: [PATCH 06/30] CW-1076-payjoin-ux-enhancements (#2282) * feat: Add Payjoin Unavailable bottom sheet * feat: Add Payjoin Info Card to Dashboard * feat: Open payjoin docs on learn more --- lib/entities/preferences_key.dart | 1 + .../screens/dashboard/pages/address_page.dart | 14 +- .../pages/balance/crypto_balance_widget.dart | 48 ++++ lib/src/screens/receive/receive_page.dart | 93 ++++---- .../screens/receive/widgets/qr_widget.dart | 211 +++++++++++++----- lib/store/settings_store.dart | 11 + .../dashboard/dashboard_view_model.dart | 19 +- .../wallet_address_list_view_model.dart | 6 + res/values/strings_ar.arb | 8 +- res/values/strings_bg.arb | 8 +- res/values/strings_cs.arb | 8 +- res/values/strings_de.arb | 8 +- res/values/strings_en.arb | 8 +- res/values/strings_es.arb | 8 +- res/values/strings_fr.arb | 8 +- res/values/strings_ha.arb | 8 +- res/values/strings_hi.arb | 10 +- res/values/strings_hr.arb | 8 +- res/values/strings_hy.arb | 8 +- res/values/strings_id.arb | 8 +- res/values/strings_it.arb | 8 +- res/values/strings_ja.arb | 8 +- res/values/strings_ko.arb | 8 +- res/values/strings_my.arb | 8 +- res/values/strings_nl.arb | 8 +- res/values/strings_pl.arb | 8 +- res/values/strings_pt.arb | 8 +- res/values/strings_ru.arb | 8 +- res/values/strings_th.arb | 8 +- res/values/strings_tl.arb | 8 +- res/values/strings_tr.arb | 8 +- res/values/strings_uk.arb | 8 +- res/values/strings_ur.arb | 8 +- res/values/strings_vi.arb | 8 +- res/values/strings_yo.arb | 8 +- res/values/strings_zh.arb | 8 +- 36 files changed, 480 insertions(+), 149 deletions(-) diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 494888a86..2a8c2d019 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -83,6 +83,7 @@ class PreferencesKey { static const lookupsENS = 'looks_up_ens'; static const lookupsWellKnown = 'looks_up_well_known'; static const usePayjoin = 'use_payjoin'; + static const showPayjoinCard = 'show_payjoin_card'; static const showCameraConsent = 'show_camera_consent'; static const showDecredInfoCard = 'show_decred_info_card'; diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index a9c265e58..136fcb084 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -150,13 +150,13 @@ class AddressPage extends BasePage { Expanded( child: Observer( builder: (_) => QRWidget( - formKey: _formKey, - addressListViewModel: addressListViewModel, - amountTextFieldFocusNode: _cryptoAmountFocus, - amountController: _amountController, - isLight: dashboardViewModel.settingsStore.currentTheme.type == - ThemeType.light, - ))), + formKey: _formKey, + addressListViewModel: addressListViewModel, + amountTextFieldFocusNode: _cryptoAmountFocus, + amountController: _amountController, + currentTheme: + dashboardViewModel.settingsStore.currentTheme, + ))), SizedBox(height: 16), Observer(builder: (_) { if (addressListViewModel.hasAddressList) { diff --git a/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart b/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart index d453a698f..d723c377d 100644 --- a/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart +++ b/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart @@ -325,6 +325,39 @@ class CryptoBalanceWidget extends StatelessWidget { ), ), ], + if (dashboardViewModel.showPayjoinCard) ...[ + SizedBox(height: 10), + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), + child: InfoCard( + title: "Payjoin", + description: S.of(context).payjoin_card_content, + hintWidget: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => launchUrl( + Uri.parse("https://docs.cakewallet.com/cryptos/bitcoin/#payjoin"), + mode: LaunchMode.externalApplication, + ), + child: Text( + S.of(context).learn_more, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context).extension()!.labelTextColor, + height: 1, + ), + softWrap: true, + ), + ), + image: 'assets/images/payjoin.png', + leftButtonTitle: S.of(context).litecoin_mweb_dismiss, + rightButtonTitle: S.of(context).enable, + leftButtonAction: () => dashboardViewModel.dismissPayjoin(), + rightButtonAction: () => _enablePayjoin(context), + ), + ), + ], ], ); }), @@ -363,6 +396,21 @@ class CryptoBalanceWidget extends StatelessWidget { } return dashboardViewModel.setSilentPaymentsScanning(newValue); + } + + void _enablePayjoin(BuildContext context) { + showPopUp( + context: context, + builder: (BuildContext context) => AlertWithOneAction( + alertTitle: S.of(context).payjoin_enabling_popup_title, + alertContent: S.of(context).payjoin_enabling_popup_content, + buttonText: S.of(context).ok, + buttonAction: () { + Navigator.of(context).pop(); + }, + )); + + dashboardViewModel.enablePayjoin(); } Future _enableMweb(BuildContext context) async { diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 2a18f4d08..e919f7e92 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -1,29 +1,14 @@ -import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/receive/widgets/address_list.dart'; +import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart'; +import 'package:cake_wallet/src/widgets/gradient_background.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; -import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; -import 'package:cake_wallet/src/widgets/gradient_background.dart'; -import 'package:cake_wallet/src/widgets/section_divider.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/share_util.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart'; -import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart'; -import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart'; -import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart'; -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart'; -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; -import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart'; +import 'package:flutter/material.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; class ReceivePage extends BasePage { @@ -55,24 +40,23 @@ class ReceivePage extends BasePage { final FocusNode _cryptoAmountFocus; @override - Widget middle(BuildContext context) { - return Text( - title, - style: TextStyle( + Widget middle(BuildContext context) => Text( + title, + style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', - color: pageIconColor(context)), - ); - } + color: pageIconColor(context), + ), + ); @override Widget Function(BuildContext, Widget) get rootWrapper => - (BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold); + (BuildContext context, Widget scaffold) => + GradientBackground(scaffold: scaffold); @override - Widget trailing(BuildContext context) { - return Material( + Widget trailing(BuildContext context) => Material( color: Colors.transparent, child: Semantics( label: S.of(context).share, @@ -82,27 +66,25 @@ class ReceivePage extends BasePage { highlightColor: Colors.transparent, splashColor: Colors.transparent, iconSize: 25, - onPressed: () { - ShareUtil.share( - text: addressListViewModel.uri.toString(), - context: context, - ); - }, + onPressed: () => ShareUtil.share( + text: addressListViewModel.uri.toString(), + context: context, + ), icon: Icon( Icons.share, size: 20, color: pageIconColor(context), ), ), - )); - } + ), + ); @override - Widget body(BuildContext context) { - return KeyboardActions( + Widget body(BuildContext context) => KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: Theme.of(context).extension()!.keyboardBarColor, + keyboardBarColor: + Theme.of(context).extension()!.keyboardBarColor, nextFocus: false, actions: [ KeyboardActionsItem( @@ -121,23 +103,28 @@ class ReceivePage extends BasePage { heroTag: _heroTag, amountTextFieldFocusNode: _cryptoAmountFocus, amountController: _amountController, - isLight: currentTheme.type == ThemeType.light, - ), + currentTheme: currentTheme), ), - AddressList(addressListViewModel: addressListViewModel, currentTheme: currentTheme), + AddressList( + addressListViewModel: addressListViewModel, + currentTheme: currentTheme), Padding( padding: EdgeInsets.fromLTRB(24, 24, 24, 32), child: Text( - addressListViewModel.isSilentPayments - ? S.of(context).silent_payments_disclaimer - : S.of(context).electrum_address_disclaimer, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 15, - color: Theme.of(context).extension()!.labelTextColor)), + addressListViewModel.isSilentPayments + ? S.of(context).silent_payments_disclaimer + : S.of(context).electrum_address_disclaimer, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + color: Theme.of(context) + .extension()! + .labelTextColor, + ), + ), ), ], ), - )); - } + ), + ); } diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index 2df1c7b90..6fb61a893 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -1,30 +1,32 @@ import 'package:cake_wallet/entities/qr_view_data.dart'; -import 'package:cake_wallet/src/widgets/primary_button.dart'; -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:cake_wallet/themes/extensions/picker_theme.dart'; -import 'package:cake_wallet/themes/extensions/qr_code_theme.dart'; +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart'; import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart'; +import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart'; +import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/picker_theme.dart'; +import 'package:cake_wallet/themes/extensions/qr_code_theme.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/address_formatter.dart'; import 'package:cake_wallet/utils/brightness_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart'; -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; -import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:url_launcher/url_launcher.dart'; class QRWidget extends StatelessWidget { QRWidget({ required this.addressListViewModel, - required this.isLight, + required this.currentTheme, this.qrVersion, this.heroTag, required this.amountController, @@ -36,7 +38,7 @@ class QRWidget extends StatelessWidget { final TextEditingController amountController; final FocusNode? amountTextFieldFocusNode; final GlobalKey formKey; - final bool isLight; + final ThemeBase currentTheme; final int? qrVersion; final String? heroTag; @@ -47,11 +49,14 @@ class QRWidget extends StatelessWidget { @override Widget build(BuildContext context) { final copyImage = Image.asset('assets/images/copy_address.png', - color: Theme.of(context).extension()!.qrWidgetCopyButtonColor); + color: Theme.of(context) + .extension()! + .qrWidgetCopyButtonColor); // This magic number for wider screen sets the text input focus at center of the inputfield - final _width = - responsiveLayoutUtil.shouldRenderMobileUI ? MediaQuery.of(context).size.width : 500; + final _width = responsiveLayoutUtil.shouldRenderMobileUI + ? MediaQuery.of(context).size.width + : 500; return Center( child: SingleChildScrollView( @@ -69,7 +74,9 @@ class QRWidget extends StatelessWidget { style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.textColor), + color: Theme.of(context) + .extension()! + .textColor), ), ), Row( @@ -82,7 +89,8 @@ class QRWidget extends StatelessWidget { onTap: () { BrightnessUtil.changeBrightnessForFunction( () async { - await Navigator.pushNamed(context, Routes.fullscreenQR, + await Navigator.pushNamed( + context, Routes.fullscreenQR, arguments: QrViewData( data: addressUri.toString(), heroTag: heroTag, @@ -97,7 +105,8 @@ class QRWidget extends StatelessWidget { padding: EdgeInsets.zero, decoration: BoxDecoration( border: Border(top: BorderSide.none), - borderRadius: BorderRadius.all(Radius.circular(5)), + borderRadius: + BorderRadius.all(Radius.circular(5)), color: Colors.white, ), child: Column( @@ -111,10 +120,64 @@ class QRWidget extends StatelessWidget { ), ), ), - if (addressListViewModel.payjoinEndpoint.isNotEmpty && - !addressListViewModel.isSilentPayments) ...[ + if (addressListViewModel + .isPayjoinUnavailable && + !addressListViewModel + .isSilentPayments) ...[ + GestureDetector( + onTap: () => + _onPayjoinInactivePressed(context), + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only( + top: 4, + bottom: 4, + right: 4, + ), + child: Image.asset( + 'assets/images/payjoin.png', + width: 20, + ), + ), + Text( + S.of(context).payjoin_unavailable, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color.fromARGB( + 255, 139, 137, 139), + ), + ), + Padding( + padding: EdgeInsets.only( + top: 4, + bottom: 4, + left: 4, + ), + child: CircleAvatar( + radius: 7, + backgroundColor: Colors.black, + child: Icon( + Icons.question_mark, + size: 10, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ], + if (addressListViewModel + .payjoinEndpoint.isNotEmpty && + !addressListViewModel + .isSilentPayments) ...[ Row( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, children: [ Padding( padding: EdgeInsets.only( @@ -151,37 +214,43 @@ class QRWidget extends StatelessWidget { ), ], ), - Observer(builder: (_) { - return Padding( - padding: EdgeInsets.only(top: 10), - child: Row( - children: [ - Expanded( - child: Form( - key: formKey, - child: CurrencyAmountTextField( - selectedCurrency: _currencyName, - amountFocusNode: amountTextFieldFocusNode, - amountController: amountController, - padding: EdgeInsets.only(top: 20, left: _width / 4), - currentTheme: isLight ? ThemeType.light : ThemeType.dark, - isAmountEditable: true, - tag: addressListViewModel.selectedCurrency.tag, - onTapPicker: () => _presentPicker(context), - isPickerEnable: true)), - ), - ], - ), - ); - }), - Divider(height: 1, color: Theme.of(context).extension()!.dividerColor), + Observer( + builder: (_) => Padding( + padding: EdgeInsets.only(top: 10), + child: Row( + children: [ + Expanded( + child: Form( + key: formKey, + child: CurrencyAmountTextField( + selectedCurrency: _currencyName, + amountFocusNode: amountTextFieldFocusNode, + amountController: amountController, + padding: + EdgeInsets.only(top: 20, left: _width / 4), + currentTheme: currentTheme.type, + isAmountEditable: true, + tag: addressListViewModel.selectedCurrency.tag, + onTapPicker: () => _presentPicker(context), + isPickerEnable: true, + ), + ), + ), + ], + ), + )), + Divider( + height: 1, + color: + Theme.of(context).extension()!.dividerColor), Padding( padding: EdgeInsets.only(top: 20, bottom: 8), child: Builder( builder: (context) => Observer( builder: (context) => GestureDetector( onTap: () { - Clipboard.setData(ClipboardData(text: addressUri.address)); + Clipboard.setData( + ClipboardData(text: addressUri.address)); showBar(context, S.of(context).copied_to_clipboard); }, child: Row( @@ -189,17 +258,16 @@ class QRWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: AddressFormatter.buildSegmentedAddress( - address: addressUri.address, - walletType: addressListViewModel.type, - textAlign: TextAlign.center, - evenTextStyle: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.textColor, - ), - ), - ), + child: AddressFormatter.buildSegmentedAddress( + address: addressUri.address, + walletType: addressListViewModel.type, + textAlign: TextAlign.center, + evenTextStyle: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .extension()! + .textColor))), Padding( padding: EdgeInsets.only(left: 12), child: copyImage, @@ -212,12 +280,14 @@ class QRWidget extends StatelessWidget { ), Observer( builder: (_) => Offstage( - offstage: addressListViewModel.payjoinEndpoint.isEmpty || addressListViewModel.isSilentPayments, + offstage: addressListViewModel.payjoinEndpoint.isEmpty || + addressListViewModel.isSilentPayments, child: Padding( padding: EdgeInsets.only(top: 12), child: PrimaryImageButton( onPressed: () { - Clipboard.setData(ClipboardData(text: addressUri.toString())); + Clipboard.setData( + ClipboardData(text: addressUri.toString())); showBar(context, S.of(context).copied_to_clipboard); }, image: Image.asset( @@ -226,7 +296,9 @@ class QRWidget extends StatelessWidget { ), text: S.of(context).copy_payjoin_address, color: Theme.of(context).cardColor, - textColor: Theme.of(context).extension()!.buttonTextColor, + textColor: Theme.of(context) + .extension()! + .buttonTextColor, ), ), ), @@ -239,7 +311,9 @@ class QRWidget extends StatelessWidget { String get _currencyName { if (addressListViewModel.selectedCurrency is CryptoCurrency) { - return (addressListViewModel.selectedCurrency as CryptoCurrency).title.toUpperCase(); + return (addressListViewModel.selectedCurrency as CryptoCurrency) + .title + .toUpperCase(); } return addressListViewModel.selectedCurrency.name.toUpperCase(); } @@ -257,4 +331,23 @@ class QRWidget extends StatelessWidget { // update amount if currency changed addressListViewModel.changeAmount(amountController.text); } + + void _onPayjoinInactivePressed(BuildContext context) { + showModalBottomSheet( + context: context, + builder: (context) => InfoBottomSheet( + titleText: S.of(context).payjoin_unavailable_sheet_title, + content: S.of(context).payjoin_unavailable_sheet_content, + currentTheme: currentTheme, + isTwoAction: true, + leftButtonText: S.of(context).learn_more, + actionLeftButton: () => launchUrl( + Uri.parse("https://docs.cakewallet.com/cryptos/bitcoin/#payjoin"), + mode: LaunchMode.externalApplication, + ), + rightButtonText: S.of(context).ok, + actionRightButton: () => Navigator.of(context).pop(), + ), + ); + } } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 6a9525f5c..c8193a79f 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -122,6 +122,7 @@ abstract class SettingsStoreBase with Store { required this.lookupsENS, required this.lookupsWellKnown, required this.usePayjoin, + required this.showPayjoinCard, required this.customBitcoinFeeRate, required this.silentPaymentsCardDisplay, required this.silentPaymentsAlwaysScan, @@ -489,6 +490,11 @@ abstract class SettingsStoreBase with Store { (bool usePayjoin) => _sharedPreferences.setBool(PreferencesKey.usePayjoin, usePayjoin)); + reaction( + (_) => showPayjoinCard, + (bool showPayjoinCard) => _sharedPreferences.setBool( + PreferencesKey.showPayjoinCard, showPayjoinCard)); + // secure storage keys: reaction( (_) => allowBiometricalAuthentication, @@ -811,6 +817,9 @@ abstract class SettingsStoreBase with Store { @observable bool usePayjoin; + @observable + bool showPayjoinCard; + @observable SyncMode currentSyncMode; @@ -1019,6 +1028,7 @@ abstract class SettingsStoreBase with Store { final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true; final lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true; final usePayjoin = sharedPreferences.getBool(PreferencesKey.usePayjoin) ?? false; + final showPayjoinCard = sharedPreferences.getBool(PreferencesKey.showPayjoinCard) ?? true; final customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1; final silentPaymentsCardDisplay = sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; @@ -1322,6 +1332,7 @@ abstract class SettingsStoreBase with Store { lookupsENS: lookupsENS, lookupsWellKnown: lookupsWellKnown, usePayjoin: usePayjoin, + showPayjoinCard: showPayjoinCard, customBitcoinFeeRate: customBitcoinFeeRate, silentPaymentsCardDisplay: silentPaymentsCardDisplay, silentPaymentsAlwaysScan: silentPaymentsAlwaysScan, diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 058fb25f9..b88f6e88e 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -33,7 +33,6 @@ import 'package:cake_wallet/view_model/dashboard/payjoin_transaction_list_item.d import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart'; -import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wownero/wownero.dart' as wow; import 'package:cryptography/cryptography.dart'; import 'package:cw_core/balance.dart'; @@ -559,6 +558,12 @@ abstract class DashboardViewModelBase with Store { @observable late bool showDecredInfoCard; + @computed + bool get showPayjoinCard => + wallet.type == WalletType.bitcoin && + settingsStore.showPayjoinCard && + !settingsStore.usePayjoin; + @observable bool backgroundSyncEnabled = false; @@ -764,6 +769,18 @@ abstract class DashboardViewModelBase with Store { sharedPreferences.setBool(PreferencesKey.showDecredInfoCard, false); } + @action + void dismissPayjoin() { + settingsStore.showPayjoinCard = false; + } + + @action + void enablePayjoin() { + settingsStore.usePayjoin = true; + settingsStore.showPayjoinCard = false; + bitcoin!.updatePayjoinState(wallet, true); + } + BalanceViewModel balanceViewModel; AppStore appStore; diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 2052a79a4..910f081ee 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -310,6 +310,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo ? bitcoin!.getPayjoinEndpoint(wallet) : ""; + @computed + bool get isPayjoinUnavailable => + wallet.type == WalletType.bitcoin && + _settingsStore.usePayjoin && + payjoinEndpoint.isEmpty; + @computed PaymentURI get uri { switch (wallet.type) { diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 7caf3a126..5489321a0 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -548,10 +548,16 @@ "password": "كلمة المرور", "paste": "لصق", "pause_wallet_creation": ".ﺎﻴًﻟﺎﺣ ﺎﺘًﻗﺆﻣ ﺔﻔﻗﻮﺘﻣ Haven Wallet ءﺎﺸﻧﺇ ﻰﻠﻋ ﺓﺭﺪﻘﻟﺍ", + "payjoin_card_content": "Payjoin هي ميزة جديدة للحفاظ على الخصوصية وتوفير الرسوم في Bitcoin.", "payjoin_details": "Payjoin تفاصيل", "payjoin_enabled": "Payjoin تمكين", + "payjoin_enabling_popup_content": "يمكنك التبديل Payjoin ضمن القائمة → الخصوصية ← استخدام Payjoin لتمكينه أو تعطيله لاحقًا.", + "payjoin_enabling_popup_title": "تمكين Payjoin", "payjoin_request_awaiting_tx": "في انتظار المعاملة", "payjoin_request_in_progress": "في تَقَدم", + "payjoin_unavailable": "Payjoin غير متوفر", + "payjoin_unavailable_sheet_content": "يتطلب استلام معاملة Payjoin أن تكون Bitcoin جاهزًا للاستخدام.", + "payjoin_unavailable_sheet_title": "لماذا Payjoin غير متوفر؟", "payment_id": "معرف الدفع:", "payment_was_received": "تم استلام الدفع الخاص بك.", "pending": " (في الإنتظار)", @@ -1071,4 +1077,4 @@ "you_will_send": "تحويل من", "youCanGoBackToYourDapp": "يمكنك العودة إلى DAPP الخاص بك الآن", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index ae08c4ac4..b44eb0ef4 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -548,10 +548,16 @@ "password": "Парола", "paste": "Поставяне", "pause_wallet_creation": "Възможността за създаване на Haven Wallet в момента е на пауза.", + "payjoin_card_content": "Payjoin е нова функция за запазване на поверителност и пестене на такси в Bitcoin.", "payjoin_details": "Payjoin подробности", "payjoin_enabled": "Payjoin enabled", + "payjoin_enabling_popup_content": "Можете да превключвате Payjoin под меню → поверителност → Използвайте Payjoin, за да го активирате или деактивирате по -късно.", + "payjoin_enabling_popup_title": "Активиране Payjoin", "payjoin_request_awaiting_tx": "В очакване на транзакция", "payjoin_request_in_progress": "В ход", + "payjoin_unavailable": "Payjoin недостъпно", + "payjoin_unavailable_sheet_content": "Получаването на Payjoin транзакция изисква да имате готови за биткойн. \\ N \\ nas скоро, когато портфейлът ви има средства, Payjoin ще бъде активиран автоматично.", + "payjoin_unavailable_sheet_title": "Защо Payjoin не е наличен?", "payment_id": "Payment ID: ", "payment_was_received": "Плащането бе получено.", "pending": " (чакащи)", @@ -1071,4 +1077,4 @@ "you_will_send": "Обръщане от", "youCanGoBackToYourDapp": "Можете да се върнете при вашия Dapp сега", "yy": "гг" -} \ No newline at end of file +} diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index e9a5bfb55..a281a13c3 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -548,10 +548,16 @@ "password": "Heslo", "paste": "Vložit", "pause_wallet_creation": "Možnost vytvářet Haven Wallet je momentálně pozastavena.", + "payjoin_card_content": "Payjoin je nová funkce ochrany soukromí a úspory poplatků v bitcoinech.", "payjoin_details": "%%TE podrobnosti", "payjoin_enabled": "Payjoin povoleno", + "payjoin_enabling_popup_content": "Můžete přepínat Payjoin v nabídce → Ochrana osobních údajů → Použít Payjoin, abyste jej povolili později.", + "payjoin_enabling_popup_title": "Povolení Payjoin", "payjoin_request_awaiting_tx": "Čeká na transakci", "payjoin_request_in_progress": "Probíhá", + "payjoin_unavailable": "Payjoin nedostupné", + "payjoin_unavailable_sheet_content": "Přijetí transakce Payjoin vyžaduje, abyste měli bitcoin připraveni k použití. \\ N \\ NAS, jakmile má vaše peněženka finanční prostředky, Payjoin bude automaticky povoleno.", + "payjoin_unavailable_sheet_title": "Proč je Payjoin nedostupný?", "payment_id": "ID platby: ", "payment_was_received": "Vaše platba byla přijata.", "pending": " (čeká)", @@ -1071,4 +1077,4 @@ "you_will_send": "Směnit z", "youCanGoBackToYourDapp": "Nyní se můžete vrátit do svého dappu", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index e35cf08ee..7cf6dec82 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -548,10 +548,16 @@ "password": "Passwort", "paste": "Einfügen", "pause_wallet_creation": "Die Möglichkeit, Haven Wallet zu erstellen, ist derzeit pausiert.", + "payjoin_card_content": "Payjoin ist eine neue Feature für Privatsphäre und Gebühren in Bitcoin.", "payjoin_details": "Payjoin Details", "payjoin_enabled": "Payjoin aktiv", + "payjoin_enabling_popup_content": "Sie können Payjoin unter dem Menü → Privatsphäre → Payjoin verwenden, um es später zu aktivieren oder zu deaktivieren.", + "payjoin_enabling_popup_title": "Aktiviere Payjoin", "payjoin_request_awaiting_tx": "Warten auf die Transaktion", "payjoin_request_in_progress": "Im Gange", + "payjoin_unavailable": "Payjoin nicht verfügbar", + "payjoin_unavailable_sheet_content": "Wenn Sie eine Payjoin-Transaktion erhalten, müssen Sie bereits Bitcoin im Wallet haben.", + "payjoin_unavailable_sheet_title": "Warum ist Payjoin nicht verfügbar?", "payment_id": "Zahlungs-ID: ", "payment_was_received": "Ihre Zahlung ist eingegangen.", "pending": " (ausstehend)", @@ -1074,4 +1080,4 @@ "you_will_send": "Konvertieren von", "youCanGoBackToYourDapp": "Sie können jetzt zu Ihrem Dapp zurückkehren", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index a71aa5f54..c699a313a 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -549,10 +549,16 @@ "password": "Password", "paste": "Paste", "pause_wallet_creation": "Ability to create Haven Wallet is currently paused.", + "payjoin_card_content": "Payjoin is a new privacy-preserving and fee-saving feature in Bitcoin.", "payjoin_details": "Payjoin details", "payjoin_enabled": "Payjoin enabled", + "payjoin_enabling_popup_content": "You can toggle Payjoin under Menu → Privacy → Use Payjoin to enable or disable it later.", + "payjoin_enabling_popup_title": "Enabling Payjoin", "payjoin_request_awaiting_tx": "Awaiting Transaction", "payjoin_request_in_progress": "In Progress", + "payjoin_unavailable": "Payjoin unavailable", + "payjoin_unavailable_sheet_content": "Receiving a Payjoin transaction requires you to have Bitcoin ready to use.\n\nAs soon as your wallet has funds, Payjoin will be automatically enabled.", + "payjoin_unavailable_sheet_title": "Why is Payjoin unavailable?", "payment_id": "Payment ID: ", "payment_was_received": "Your payment was received.", "pending": " (pending)", @@ -1072,4 +1078,4 @@ "you_will_send": "Convert from", "youCanGoBackToYourDapp": "You can go back to your dApp now", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 23dad880e..1b4d56027 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -548,10 +548,16 @@ "password": "Contraseña", "paste": "Pegar", "pause_wallet_creation": "La capacidad para crear Haven Wallet está actualmente pausada.", + "payjoin_card_content": "Payjoin es una nueva función de preservación de la privacidad y ahorro de tarifas en Bitcoin.", "payjoin_details": "Payjoin detalles", "payjoin_enabled": "Payjoin activado", + "payjoin_enabling_popup_content": "Puede alternar Payjoin en menú → privacidad → usar Payjoin para habilitarlo o deshabilitarlo más tarde.", + "payjoin_enabling_popup_title": "Habilitando Payjoin", "payjoin_request_awaiting_tx": "Esperando transacción", "payjoin_request_in_progress": "En curso", + "payjoin_unavailable": "Payjoin no disponible", + "payjoin_unavailable_sheet_content": "Recibir una transacción Payjoin requiere que tenga Bitcoin listo para usar. \\ N \\ nas pronto como su billetera tenga fondos, Payjoin estará habilitado automáticamente.", + "payjoin_unavailable_sheet_title": "¿Por qué no está disponible Payjoin?", "payment_id": "ID de pago: ", "payment_was_received": "Su pago fue recibido.", "pending": " (pendiente)", @@ -1072,4 +1078,4 @@ "you_will_send": "Convertir de", "youCanGoBackToYourDapp": "Puedes volver a tu dapp ahora", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index d2b1c5356..805fdf3d6 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -548,10 +548,16 @@ "password": "Mot de passe", "paste": "Coller", "pause_wallet_creation": "La possibilité de créer Haven Wallet est actuellement suspendue.", + "payjoin_card_content": "Payjoin est une nouvelle fonctionnalité de préservation de la confidentialité et d'économie de frais dans Bitcoin.", "payjoin_details": "Payjoin détails", "payjoin_enabled": "Payjoin activé", + "payjoin_enabling_popup_content": "Vous pouvez basculer Payjoin sous le menu → Confidentialité → Utiliser Payjoin pour l'activer ou le désactiver plus tard.", + "payjoin_enabling_popup_title": "Activer Payjoin", "payjoin_request_awaiting_tx": "En attente de transaction", "payjoin_request_in_progress": "En cours", + "payjoin_unavailable": "Payjoin indisponible", + "payjoin_unavailable_sheet_content": "La réception d'une transaction Payjoin vous oblige à avoir un bitcoin prêt à l'emploi. \\ N \\ nas dès que votre portefeuille a des fonds, Payjoin sera automatiquement activé.", + "payjoin_unavailable_sheet_title": "Pourquoi Payjoin n'est-il pas disponible?", "payment_id": "ID de Paiement : ", "payment_was_received": "Votre paiement a été reçu.", "pending": " (en attente)", @@ -1071,4 +1077,4 @@ "you_will_send": "Convertir depuis", "youCanGoBackToYourDapp": "Vous pouvez retourner à votre Dapp maintenant", "yy": "AA" -} \ No newline at end of file +} diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index ee1fed998..b52e85184 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -550,10 +550,16 @@ "password": "Kalmar wucewa", "paste": "Manna", "pause_wallet_creation": "A halin yanzu an dakatar da ikon ƙirƙirar Haven Wallet.", + "payjoin_card_content": "Payjoin Shin sabon salo ne da adana sirri da biyan kuɗi a cikin Bitcoin.", "payjoin_details": "Payjoin LIT LOCEC LOcciya", "payjoin_enabled": "Payjoin An kunna", + "payjoin_enabling_popup_content": "Kuna iya juyawa Payjoin a ƙarƙashin menu → Sirrin Sirri → Amfani Payjoin don kunna ko musaki shi daga baya.", + "payjoin_enabling_popup_title": "Samu Payjoin", "payjoin_request_awaiting_tx": "Jiran ma'amala", "payjoin_request_in_progress": "Ana kai", + "payjoin_unavailable": "Payjoin Babu", + "payjoin_unavailable_sheet_content": "Karɓar A Payjoin Ma'amala yana buƙatar ku sami Bitcoin shirye don amfani. \\ N \\ n5 da zaran kamar walat ɗinku yana da kuɗi, Payjoin za'a kunna ta atomatik.", + "payjoin_unavailable_sheet_title": "Me yasa Payjoin rasaa?", "payment_id": "ID na biyan kuɗi:", "payment_was_received": "An karɓi kuɗin ku.", "pending": "(pending)", @@ -1073,4 +1079,4 @@ "you_will_send": "Maida daga", "youCanGoBackToYourDapp": "Kuna iya komawa zuwa DPP ɗinku yanzu", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 41c2dfd45..ad521ecd8 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -548,13 +548,19 @@ "password": "पारण शब्द", "paste": "पेस्ट करें", "pause_wallet_creation": "हेवन वॉलेट बनाने की क्षमता फिलहाल रुकी हुई है।", + "payjoin_card_content": "Payjoin बिटकॉइन में एक नई गोपनीयता-संरक्षण और शुल्क-बचत सुविधा है।", "payjoin_details": "Payjoin विवरण", "payjoin_enabled": "Payjoin सक्षम", + "payjoin_enabling_popup_content": "आप मेनू → गोपनीयता → के तहत Payjoin टॉगल कर सकते हैं → बाद में इसे सक्षम या अक्षम करने के लिए Payjoin का उपयोग करें।", + "payjoin_enabling_popup_title": "सक्षम Payjoin", "payjoin_request_awaiting_tx": "लेन -देन का इंतजार", "payjoin_request_in_progress": "प्रगति पर है", + "payjoin_unavailable": "Payjoin अनुपलब्ध", + "payjoin_unavailable_sheet_content": "Payjoin लेन -देन प्राप्त करने के लिए आपको बिटकॉइन का उपयोग करने के लिए तैयार होना आवश्यक है। जैसे ही आपके वॉलेट में धन है, \\ n \\ nas, Payjoin स्वचालित रूप से सक्षम हो जाएगा।", + "payjoin_unavailable_sheet_title": "Payjoin अनुपलब्ध क्यों है?", "payment_id": "भुगतान ID: ", - "Payment_was_received": "आपका भुगतान प्राप्त हो गया था।", "payment_was_received": "आपका भुगतान प्राप्त हुआ था।", + "Payment_was_received": "आपका भुगतान प्राप्त हो गया था।", "pending": " (अपूर्ण)", "percentageOf": "${amount} का", "pin_at_top": "शीर्ष पर ${token} पिन करें", @@ -1073,4 +1079,4 @@ "you_will_send": "से रूपांतरित करें", "youCanGoBackToYourDapp": "अब आप अपने DAPP पर वापस जा सकते हैं", "yy": "वाईवाई" -} \ No newline at end of file +} diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index d8c2a255f..6070e7908 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -548,10 +548,16 @@ "password": "Lozinka", "paste": "Zalijepi", "pause_wallet_creation": "Mogućnost stvaranja novčanika Haven trenutno je pauzirana.", + "payjoin_card_content": "Payjoin je nova značajka za čuvanje privatnosti i ušteda naknada u Bitcoin-u.", "payjoin_details": "Payjoin Pojedinosti", "payjoin_enabled": "Payjoin Omogućeno", + "payjoin_enabling_popup_content": "Možete prebaciti Payjoin u izborniku → Privatnost → Koristite Payjoin da biste ga omogućili ili onemogućili kasnije.", + "payjoin_enabling_popup_title": "Omogućavanje Payjoin", "payjoin_request_awaiting_tx": "Čekajući transakciju", "payjoin_request_in_progress": "U toku", + "payjoin_unavailable": "Payjoin nedostupno", + "payjoin_unavailable_sheet_content": "Primanje Payjoin Transakcije zahtijeva da Bitcoin bude spreman za upotrebu. \\ N \\ NAS čim vaš novčanik ima sredstva, Payjoin će biti automatski omogućen.", + "payjoin_unavailable_sheet_title": "Zašto je Payjoin nedostupno?", "payment_id": "ID plaćanja: ", "payment_was_received": "Vaša uplata je primljena.", "pending": " (u tijeku)", @@ -1071,4 +1077,4 @@ "you_will_send": "Razmijeni iz", "youCanGoBackToYourDapp": "Sada se možete vratiti na svoj dapp", "yy": "GG" -} \ No newline at end of file +} diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index b39e9092f..eb18b6e88 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -547,10 +547,16 @@ "password": "Գաղտնաբառ", "paste": "Տեղադրել", "pause_wallet_creation": "Հնարավորություն ստեղծել Haven Դրամապանակ ընթացիկ դադարեցված է", + "payjoin_card_content": "Payjoin- ն նոր գաղտնիության պահպանման եւ վճարների պահպանման առանձնահատկություն է Bitcoin- ում:", "payjoin_details": "Payjoin Մանրամասն", "payjoin_enabled": "Payjoin միացված", + "payjoin_enabling_popup_content": "Կարող եք անցնել Payjoin ընտրացանկի ներքո → Գաղտնիության → Օգտագործեք Payjoin, միացնելու կամ անջատելու այն ավելի ուշ:", + "payjoin_enabling_popup_title": "Միացնել Payjoin", "payjoin_request_awaiting_tx": "Սպասում է գործարքին", "payjoin_request_in_progress": "Ընթացքի մեջ", + "payjoin_unavailable": "Payjoin անհասանելի", + "payjoin_unavailable_sheet_content": "A Payjoin գործարք ստանալը պահանջում է, որ դուք պատրաստեք Bitcoin- ը պատրաստելու համար:", + "payjoin_unavailable_sheet_title": "Ինչու է Payjoin անհասանելի:", "payment_id": "Վճարման հերթական համար", "payment_was_received": "Վճարումը ստացված է", "pending": " (մշակվում է)", @@ -1069,4 +1075,4 @@ "you_will_send": "Փոխանակեք", "youCanGoBackToYourDapp": "Այժմ կարող եք վերադառնալ ձեր DAPP- ին", "yy": "ՏՏ" -} \ No newline at end of file +} diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index b77dc2cf0..ce4d0790e 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -550,10 +550,16 @@ "password": "Kata Sandi", "paste": "Tempel", "pause_wallet_creation": "Kemampuan untuk membuat Haven Wallet saat ini dijeda.", + "payjoin_card_content": "Payjoin adalah fitur pemeliharaan privasi dan hemat biaya baru di Bitcoin.", "payjoin_details": "Payjoin detail", "payjoin_enabled": "Payjoin diaktifkan", + "payjoin_enabling_popup_content": "Anda dapat beralih Payjoin di bawah menu → Privasi → Gunakan Payjoin untuk mengaktifkan atau menonaktifkannya nanti.", + "payjoin_enabling_popup_title": "Mengaktifkan Payjoin", "payjoin_request_awaiting_tx": "Menunggu transaksi", "payjoin_request_in_progress": "Sedang berlangsung", + "payjoin_unavailable": "Payjoin tidak tersedia", + "payjoin_unavailable_sheet_content": "Menerima Payjoin transaksi mengharuskan Anda untuk menyiapkan bitcoin. \\ N \\ nas segera setelah dompet Anda memiliki dana, Payjoin akan diaktifkan secara otomatis.", + "payjoin_unavailable_sheet_title": "Mengapa Payjoin tidak tersedia?", "payment_id": "ID Pembayaran: ", "payment_was_received": "Pembayaran Anda telah diterima.", "pending": " (pending)", @@ -1074,4 +1080,4 @@ "you_will_send": "Konversi dari", "youCanGoBackToYourDapp": "Anda dapat kembali ke dapp Anda sekarang", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index a2753277f..4eecbe8a0 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -549,10 +549,16 @@ "password": "Password", "paste": "Incolla", "pause_wallet_creation": "La possibilità di creare Wallet Haven è attualmente sospesa.", + "payjoin_card_content": "Payjoin è una nuova funzionalità di conservazione della privacy e di risparmio di tasse in Bitcoin.", "payjoin_details": "Payjoin dettagli", "payjoin_enabled": "Payjoin abilitato", + "payjoin_enabling_popup_content": "È possibile attivare Payjoin sotto menu → privacy → usa Payjoin per abilitarlo o disabilitarlo in seguito.", + "payjoin_enabling_popup_title": "Abilitazione Payjoin", "payjoin_request_awaiting_tx": "In attesa di transazione", "payjoin_request_in_progress": "In corso", + "payjoin_unavailable": "Payjoin non disponibile", + "payjoin_unavailable_sheet_content": "La ricezione di una transazione Payjoin richiede di avere Bitcoin pronto per l'uso. \\ Ns non appena il tuo portafoglio ha fondi, Payjoin verrà automaticamente abilitato.", + "payjoin_unavailable_sheet_title": "Perché Payjoin non è disponibile?", "payment_id": "ID Pagamento: ", "payment_was_received": "Il tuo pagamento è stato ricevuto.", "pending": " (non confermati)", @@ -1073,4 +1079,4 @@ "you_will_send": "Conveti da", "youCanGoBackToYourDapp": "Puoi tornare al tuo DApp ora", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index c2ab7c2be..b91e40d13 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -549,10 +549,16 @@ "password": "パスワード", "paste": "ペースト", "pause_wallet_creation": "Haven Wallet を作成する機能は現在一時停止されています。", + "payjoin_card_content": "Payjoinは、Bitcoinの新しいプライバシーを提供し、有料の機能です。", "payjoin_details": "Payjoin 詳細", "payjoin_enabled": "Payjoin enabled", + "payjoin_enabling_popup_content": "メニュー→[プライバシー]→[ Payjoinを使用して、後で有効または無効にすることもできます。", + "payjoin_enabling_popup_title": "Payjoinを有効にする", "payjoin_request_awaiting_tx": "トランザクションを待っています", "payjoin_request_in_progress": "進行中", + "payjoin_unavailable": "Payjoin利用できません", + "payjoin_unavailable_sheet_content": "Payjoinトランザクションを受信するには、ビットコインを使用できるようにする必要があります。\\ n \\ nasのウォレットに資金があるとすぐに、 Payjoinが自動的に有効になります。", + "payjoin_unavailable_sheet_title": "Payjoinが利用できないのはなぜですか?", "payment_id": "支払いID: ", "payment_was_received": "お支払いを受け取りました。", "pending": " (保留中)", @@ -1072,4 +1078,4 @@ "you_will_send": "から変換", "youCanGoBackToYourDapp": "あなたは今あなたのダップに戻ることができます", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 841cf7a37..bedd7e5f6 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -549,10 +549,16 @@ "password": "비밀번호", "paste": "붙여넣기", "pause_wallet_creation": "현재 Haven 지갑 생성 기능이 일시 중지되었습니다.", + "payjoin_card_content": "Payjoin는 Bitcoin의 새로운 개인 정보 보호 및 수수료 절약 기능입니다.", "payjoin_details": "Payjoin 세부 정보", "payjoin_enabled": "Payjoin enabled", + "payjoin_enabling_popup_content": "메뉴 → 개인 정보 → → Payjoin를 사용하여 나중에 활성화하거나 비활성화 할 수 있습니다.", + "payjoin_enabling_popup_title": "활성화 Payjoin", "payjoin_request_awaiting_tx": "거래를 기다리고 있습니다", "payjoin_request_in_progress": "진행 중", + "payjoin_unavailable": "Payjoin Aailable", + "payjoin_unavailable_sheet_content": "Payjoin 트랜잭션을 수신하려면 비트 코인을 사용할 준비가되어 있어야합니다. \\ n \\ nas 지갑이 자금이 자금이 있으면 Payjoin가 자동으로 활성화됩니다.", + "payjoin_unavailable_sheet_title": "Payjoin를 사용할 수없는 이유는 무엇입니까?", "payment_id": "결제 ID: ", "payment_was_received": "결제가 접수되었습니다.", "pending": " (대기 중)", @@ -1072,4 +1078,4 @@ "you_will_send": "전환 출처", "youCanGoBackToYourDapp": "이제 dApp으로 돌아갈 수 있습니다", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 54ad17d2b..f07d684f5 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -548,10 +548,16 @@ "password": "စကားဝှက်", "paste": "ငါးပိ", "pause_wallet_creation": "Haven Wallet ဖန်တီးနိုင်မှုကို လောလောဆယ် ခေတ္တရပ်ထားသည်။", + "payjoin_card_content": "Payjoin Bitcoin တွင် privacy ကိုထိန်းသိမ်းထားခြင်းနှင့်အခကြေးငွေသက်သာသည့်အင်္ဂါရပ်အသစ်တစ်ခုဖြစ်သည်။", "payjoin_details": "Payjoin အသေးစိတ်အချက်အလက်များ %% အသေးစိတ်အချက်အလက်များ", "payjoin_enabled": "Payjoin enabled", + "payjoin_enabling_popup_content": "menu Payjoin Payjoin Payjoin Payjoin Payjoin → privacy → enable လုပ်ရန် Payjoin Payjoin ကိုသုံးပါ။", + "payjoin_enabling_popup_title": "Payjoin Payjoin enable", "payjoin_request_awaiting_tx": "ငွေပေးငွေယူစောင့်ဆိုင်း", "payjoin_request_in_progress": "ဆောင်ရွက်ဆဲဖြစ်သည်", + "payjoin_unavailable": "Payjoin မရနိုင်သော", + "payjoin_unavailable_sheet_content": "Payjoin ငွေပေးချေမှုကိုလက်ခံရန်သင့်အား Bitcoin ကိုအသုံးပြုရန်အဆင်သင့်ဖြစ်ရန်လိုအပ်သည်။ \\ n \\ ns ပိုက်ဆံအိတ်သည်ရန်ပုံငွေများရှိပါက Payjoin သည်အလိုအလျောက်ဖွင့်ထားလိမ့်မည်။", + "payjoin_unavailable_sheet_title": "Payjoin Payjoin မရနိုင်ပါကဘာကြောင့်လဲ။", "payment_id": "ငွေပေးချေမှု ID:", "payment_was_received": "သင့်ငွေပေးချေမှုကို လက်ခံရရှိခဲ့သည်။", "pending": " (ဆိုင်းငံ့)", @@ -1071,4 +1077,4 @@ "you_will_send": "မှပြောင်းပါ။", "youCanGoBackToYourDapp": "သငျသညျယခုသင်၏ dapp ကိုပြန်သွားနိုင်ပါတယ်", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index aa489d10d..69e952113 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -548,10 +548,16 @@ "password": "Wachtwoord", "paste": "Plakken", "pause_wallet_creation": "De mogelijkheid om Haven Wallet te maken is momenteel onderbroken.", + "payjoin_card_content": "Payjoin is een nieuwe functie voor privacybehoud en fee-sparende functie in Bitcoin.", "payjoin_details": "Payjoin details", "payjoin_enabled": "Payjoin ingeschakeld", + "payjoin_enabling_popup_content": "U kunt Payjoin onder menu → privacy → Privacy → gebruiken Payjoin om het later in te schakelen of uit te schakelen.", + "payjoin_enabling_popup_title": "Payjoin Inschakelen", "payjoin_request_awaiting_tx": "In afwachting van transactie", "payjoin_request_in_progress": "In uitvoering", + "payjoin_unavailable": "Payjoin niet beschikbaar", + "payjoin_unavailable_sheet_content": "Het ontvangen van een Payjoin -transactie vereist dat u Bitcoin klaar hebt om te gebruiken. \\ N \\ NAS Zodra uw portemonnee geld heeft, wordt Payjoin automatisch ingeschakeld.", + "payjoin_unavailable_sheet_title": "Waarom is Payjoin niet beschikbaar?", "payment_id": "Betaling ID: ", "payment_was_received": "Uw betaling is ontvangen.", "pending": " (in afwachting)", @@ -1072,4 +1078,4 @@ "you_will_send": "Converteren van", "youCanGoBackToYourDapp": "U kunt nu terug naar uw DApp gaan", "yy": "JJ" -} \ No newline at end of file +} diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index b8e02aacc..189c20dd4 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -548,10 +548,16 @@ "password": "Hasło", "paste": "Wklej", "pause_wallet_creation": "Możliwość utworzenia Portfela Haven jest obecnie wstrzymana.", + "payjoin_card_content": "Payjoin to nowa funkcja wymagająca prywatności i oszczędzania opłat w Bitcoin.", "payjoin_details": "Szczegóły Payjoin", "payjoin_enabled": "Payjoin włączony", + "payjoin_enabling_popup_content": "Możesz przełączać Payjoin w menu → Prywatność → Użyj Payjoin, aby włączyć lub wyłączyć później.", + "payjoin_enabling_popup_title": "Włączanie Payjoin", "payjoin_request_awaiting_tx": "Oczekiwanie na transakcję", "payjoin_request_in_progress": "W toku", + "payjoin_unavailable": "Payjoin niedostępny", + "payjoin_unavailable_sheet_content": "Otrzymanie transakcji Payjoin wymaga przygotowania Bitcoin do użycia. \\ N \\ NAS, ponieważ portfel ma fundusze, Payjoin zostanie automatycznie włączony.", + "payjoin_unavailable_sheet_title": "Dlaczego Payjoin jest niedostępny?", "payment_id": "ID Płatności: ", "payment_was_received": "Twoja płatność została otrzymana.", "pending": " (w oczekiwaniu)", @@ -1071,4 +1077,4 @@ "you_will_send": "Konwertuj z", "youCanGoBackToYourDapp": "Możesz teraz wrócić do swojego dapp", "yy": "RR" -} \ No newline at end of file +} diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index c38a368db..610ecb585 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -550,10 +550,16 @@ "password": "Senha", "paste": "Colar", "pause_wallet_creation": "A capacidade de criar a Haven Wallet está atualmente pausada.", + "payjoin_card_content": "Payjoin é um novo recurso de preservação de privacidade e economia de taxas no Bitcoin.", "payjoin_details": "Payjoin detalhes", "payjoin_enabled": "Payjoin habilitado", + "payjoin_enabling_popup_content": "Você pode alternar Payjoin em Menu → Privacidade → Use Payjoin para habilitá -lo ou desativá -lo posteriormente.", + "payjoin_enabling_popup_title": "Habilitando Payjoin", "payjoin_request_awaiting_tx": "Aguardando transação", "payjoin_request_in_progress": "Em andamento", + "payjoin_unavailable": "Payjoin indisponível", + "payjoin_unavailable_sheet_content": "O recebimento de uma transação Payjoin exige que você tenha o Bitcoin pronto para uso. \\ N \\ nas assim que sua carteira tiver fundos, Payjoin será ativado automaticamente.", + "payjoin_unavailable_sheet_title": "Por que Payjoin não está disponível?", "payment_id": "ID de pagamento: ", "payment_was_received": "Seu pagamento foi recebido.", "pending": " (pendente)", @@ -1074,4 +1080,4 @@ "you_will_send": "Converter de", "youCanGoBackToYourDapp": "Você pode voltar para o seu dapp agora", "yy": "aa" -} \ No newline at end of file +} diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index f532783ee..409173744 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -549,10 +549,16 @@ "password": "Пароль", "paste": "Вставить", "pause_wallet_creation": "Возможность создания Haven Wallet в настоящее время приостановлена.", + "payjoin_card_content": "Payjoin-новая функция, поддерживающая конфиденциальность и сборы в биткойнах.", "payjoin_details": "Payjoin подробности", "payjoin_enabled": "Payjoin включено", + "payjoin_enabling_popup_content": "Вы можете переключить Payjoin в меню → конфиденциальность → использовать Payjoin, чтобы включить или отключить его позже.", + "payjoin_enabling_popup_title": "Включение Payjoin", "payjoin_request_awaiting_tx": "В ожидании транзакции", "payjoin_request_in_progress": "В ходе выполнения", + "payjoin_unavailable": "Payjoin недоступен", + "payjoin_unavailable_sheet_content": "Получение транзакции Payjoin требует, чтобы вы готовили биткойн для использования. \\ N \\ Nas, как только ваш кошелек получит средства, Payjoin будет автоматически включен.", + "payjoin_unavailable_sheet_title": "Почему Payjoin недоступен?", "payment_id": "ID платежа: ", "payment_was_received": "Ваш платеж получен.", "pending": " (в ожидании)", @@ -1072,4 +1078,4 @@ "you_will_send": "Конвертировать из", "youCanGoBackToYourDapp": "Вы можете вернуться к своему даппу сейчас", "yy": "ГГ" -} \ No newline at end of file +} diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 60508d827..acfba0be5 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -548,10 +548,16 @@ "password": "รหัสผ่าน", "paste": "วาง", "pause_wallet_creation": "ขณะนี้ความสามารถในการสร้าง Haven Wallet ถูกหยุดชั่วคราว", + "payjoin_card_content": "Payjoin เป็นฟีเจอร์การประหยัดความเป็นส่วนตัวและการประหยัดค่าธรรมเนียมใหม่ใน Bitcoin", "payjoin_details": "Payjoin รายละเอียด", "payjoin_enabled": "Payjoin เปิดใช้งาน", + "payjoin_enabling_popup_content": "คุณสามารถสลับ Payjoin ภายใต้เมนู→ความเป็นส่วนตัว→ใช้ Payjoin เพื่อเปิดใช้งานหรือปิดการใช้งานในภายหลัง", + "payjoin_enabling_popup_title": "เปิดใช้งาน Payjoin", "payjoin_request_awaiting_tx": "รอธุรกรรม", "payjoin_request_in_progress": "การดำเนินการ", + "payjoin_unavailable": "Payjoin ไม่พร้อมใช้งาน", + "payjoin_unavailable_sheet_content": "การรับธุรกรรม Payjoin จะต้องให้คุณมี Bitcoin พร้อมใช้งาน \\ n \\ nas เร็ว ๆ นี้เนื่องจากกระเป๋าเงินของคุณมีเงินทุน Payjoin จะเปิดใช้งานโดยอัตโนมัติ", + "payjoin_unavailable_sheet_title": "ทำไม Payjoin ไม่พร้อมใช้งาน?", "payment_id": "ID การชำระเงิน: ", "payment_was_received": "การชำระเงินของคุณได้รับการรับทราบแล้ว", "pending": " (อยู่ระหว่างดำเนินการ)", @@ -1071,4 +1077,4 @@ "you_will_send": "แปลงจาก", "youCanGoBackToYourDapp": "คุณสามารถกลับไปที่ dapp ของคุณได้ทันที", "yy": "ปี" -} \ No newline at end of file +} diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 14b8e3768..9eeb27519 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -548,10 +548,16 @@ "password": "Password", "paste": "I-paste", "pause_wallet_creation": "Kasalukuyang naka-pause ang kakayahang gumawa ng Haven Wallet.", + "payjoin_card_content": "Payjoin ay isang bagong tampok na privacy-pag-iingat at pag-save ng bayad sa Bitcoin.", "payjoin_details": "Mga detalye ng Payjoin", "payjoin_enabled": "Payjoin pinagana", + "payjoin_enabling_popup_content": "Maaari mong i -toggle Payjoin sa ilalim ng menu → privacy → gumamit ng Payjoin upang paganahin o huwag paganahin ito sa ibang pagkakataon.", + "payjoin_enabling_popup_title": "Pagpapagana Payjoin", "payjoin_request_awaiting_tx": "Naghihintay ng transaksyon", "payjoin_request_in_progress": "Sa pag -unlad", + "payjoin_unavailable": "Payjoin hindi magagamit", + "payjoin_unavailable_sheet_content": "Ang pagtanggap ng isang Payjoin transaksyon ay nangangailangan sa iyo na handa nang gamitin ang bitcoin.", + "payjoin_unavailable_sheet_title": "Bakit hindi magagamit ang Payjoin?", "payment_id": "Payment ID: ", "payment_was_received": "Natanggap ang iyong bayad.", "pending": "(hindi pa tapos)", @@ -1071,4 +1077,4 @@ "you_will_send": "I-convert mula sa", "youCanGoBackToYourDapp": "Maaari kang bumalik sa iyong dapp ngayon", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 6a9b433b3..3a15c449f 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -548,10 +548,16 @@ "password": "Parola", "paste": "Yapıştır", "pause_wallet_creation": "Haven Cüzdanı oluşturma yeteneği şu anda duraklatıldı.", + "payjoin_card_content": "Payjoin, Bitcoin'de yeni bir gizliliği koruyan ve ücret tasarrufu sağlayan bir özelliktir.", "payjoin_details": "Payjoin detaylar", "payjoin_enabled": "Payjoin etkinleştirilmiş", + "payjoin_enabling_popup_content": "Daha sonra etkinleştirmek veya devre dışı bırakmak için Menü → Gizlilik → Kullanın Payjoin kullanabilirsiniz.", + "payjoin_enabling_popup_title": "Etkinleştirme Payjoin", "payjoin_request_awaiting_tx": "İşlem bekliyor", "payjoin_request_in_progress": "Devam etmekte", + "payjoin_unavailable": "Payjoin mevcut değil", + "payjoin_unavailable_sheet_content": "Bir Payjoin işlem almak, Bitcoin kullanıma hazır olmanızı gerektirir. \\ N \\ nas Cüzdanınızın fonları olduğu anda Payjoin otomatik olarak etkinleştirilir.", + "payjoin_unavailable_sheet_title": "Payjoin Neden mevcut değil?", "payment_id": "Ödeme ID'si: ", "payment_was_received": "Ödemeniz alındı.", "pending": " (bekleyen)", @@ -1071,4 +1077,4 @@ "you_will_send": "Biçiminden dönüştür:", "youCanGoBackToYourDapp": "Şimdi Dapp'ınıza geri dönebilirsin", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 9a03526b1..66900f1f2 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -548,10 +548,16 @@ "password": "Пароль", "paste": "Вставити", "pause_wallet_creation": "Можливість створення гаманця Haven зараз призупинено.", + "payjoin_card_content": "Payjoin-це нова функція збереження конфіденційності та економії плати в Bitcoin.", "payjoin_details": "Payjoin деталей", "payjoin_enabled": "Payjoin увімкнено", + "payjoin_enabling_popup_content": "Ви можете перемикатися Payjoin в меню → конфіденційність → використовувати Payjoin, щоб увімкнути або відключити його пізніше.", + "payjoin_enabling_popup_title": "Увімкнення Payjoin", "payjoin_request_awaiting_tx": "Чекає транзакції", "payjoin_request_in_progress": "Триває", + "payjoin_unavailable": "Payjoin недоступний", + "payjoin_unavailable_sheet_content": "Отримання транзакції Payjoin вимагає, щоб ви біткойн готували до використання.", + "payjoin_unavailable_sheet_title": "Чому Payjoin недоступний?", "payment_id": "ID платежу: ", "payment_was_received": "Ваш платіж отримано.", "pending": " (в очікуванні)", @@ -1072,4 +1078,4 @@ "you_will_send": "Конвертувати з", "youCanGoBackToYourDapp": "Ви можете повернутися до свого DAPP зараз", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 65fb7db39..7ddb67d7b 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -550,10 +550,16 @@ "password": "پاس ورڈ", "paste": "چسپاں کریں۔", "pause_wallet_creation": "Haven Wallet ۔ﮯﮨ ﻑﻮﻗﻮﻣ ﻝﺎﺤﻟﺍ ﯽﻓ ﺖﯿﻠﮨﺍ ﯽﮐ ﮯﻧﺎﻨﺑ", + "payjoin_card_content": "Payjoin بٹ کوائن میں پرائیویسی کو محفوظ رکھنے اور فیس بچانے کی ایک نئی خصوصیت ہے۔", "payjoin_details": "Payjoin تفصیلات", "payjoin_enabled": "Payjoin فعال", + "payjoin_enabling_popup_content": "آپ Payjoin مینو → پرائیویسی → Payjoin Payjoin Payjoin کو بعد میں فعال کرنے یا غیر فعال کرنے کے لئے Payjoin Payjoin کا استعمال کریں۔", + "payjoin_enabling_popup_title": "Payjoin کو چالو کرنا", "payjoin_request_awaiting_tx": "لین دین کے منتظر", "payjoin_request_in_progress": "پیشرفت میں", + "payjoin_unavailable": "Payjoin دستیاب نہیں", + "payjoin_unavailable_sheet_content": "Payjoin ٹرانزیکشن وصول کرنے کے ل you آپ کو بٹ کوائن کو استعمال کرنے کے لئے تیار کرنے کی ضرورت ہوتی ہے۔ \\ n \\ nas جیسے ہی آپ کے بٹوے میں فنڈز ہوں گے ، Payjoin خود بخود فعال ہوجائیں گے۔", + "payjoin_unavailable_sheet_title": "Payjoin دستیاب کیوں نہیں ہے؟", "payment_id": "ادائیگی کی شناخت:", "payment_was_received": "آپ کی ادائیگی موصول ہو گئی۔", "pending": " (زیر التواء)", @@ -1073,4 +1079,4 @@ "you_will_send": "سے تبدیل کریں۔", "youCanGoBackToYourDapp": "اب آپ اپنے ڈی اے پی پی پر واپس جاسکتے ہیں", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index f65192fb9..06f44fd48 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -546,10 +546,16 @@ "password": "Mật khẩu", "paste": "Dán", "pause_wallet_creation": "Khả năng tạo ví Haven hiện đang bị tạm dừng.", + "payjoin_card_content": "Payjoin là một tính năng bảo tồn quyền riêng tư mới và tiết kiệm phí trong Bitcoin.", "payjoin_details": "Payjoin chi tiết", "payjoin_enabled": "Payjoin Bật", + "payjoin_enabling_popup_content": "Bạn có thể chuyển đổi Payjoin trong Menu → Quyền riêng tư → Sử dụng Payjoin để bật hoặc tắt nó sau.", + "payjoin_enabling_popup_title": "Cho phép Payjoin", "payjoin_request_awaiting_tx": "Đang chờ giao dịch", "payjoin_request_in_progress": "Trong tiến trình", + "payjoin_unavailable": "Payjoin Không có sẵn", + "payjoin_unavailable_sheet_content": "Nhận một giao dịch Payjoin yêu cầu bạn sẵn sàng sử dụng bitcoin. \\ N \\ nas sớm khi ví của bạn có tiền, Payjoin sẽ được bật tự động.", + "payjoin_unavailable_sheet_title": "Tại sao Payjoin không có sẵn?", "payment_id": "ID thanh toán: ", "payment_was_received": "Thanh toán của bạn đã được nhận.", "pending": " (đang chờ)", @@ -1068,4 +1074,4 @@ "you_will_send": "Chuyển đổi từ", "youCanGoBackToYourDapp": "Bạn có thể quay lại DAPP của mình ngay bây giờ", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index b7282aeab..1ec8d3430 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -549,10 +549,16 @@ "password": "Ọ̀rọ̀ aṣínà", "paste": "Fikún ẹ̀dà yín", "pause_wallet_creation": "Agbara lati ṣẹda Haven Wallet ti wa ni idaduro lọwọlọwọ.", + "payjoin_card_content": "Payjoin jẹ ẹya-itọju aṣiri tuntun ati ẹya fifipamọ owo sisan ni Bitcoin.", "payjoin_details": "Payjoin awọn alaye", "payjoin_enabled": "Payjoin ṣiṣẹ", + "payjoin_enabling_popup_content": "O le to Payjoin labẹ akojọ aṣayan → lo Payjoin lati ṣiṣẹ tabi mu kuro nigbamii.", + "payjoin_enabling_popup_title": "Mu ṣiṣẹ Payjoin", "payjoin_request_awaiting_tx": "O duro de idunadura", "payjoin_request_in_progress": "Ni ilọsiwaju", + "payjoin_unavailable": "Payjoin ko si", + "payjoin_unavailable_sheet_content": "Gbigba idunadura Payjoin iṣowo nbeere lati ni Bitcoin ṣetan lati lo. \\ N \\ NAS laipẹ bi apamọwọ rẹ ni owo, Payjoin yoo ṣiṣẹ laifọwọyi.", + "payjoin_unavailable_sheet_title": "Kini idi ti Payjoin ko si?", "payment_id": "Àmì ìdánimọ̀ àránṣẹ́: ", "payment_was_received": "Àránṣẹ́ yín ti dé.", "pending": " pípẹ́", @@ -1072,4 +1078,4 @@ "you_will_send": "Ṣe pàṣípààrọ̀ láti", "youCanGoBackToYourDapp": "O le pada si tapla rẹ bayi", "yy": "Ọd" -} \ No newline at end of file +} diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 09ca7cd11..ee84d8b65 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -548,10 +548,16 @@ "password": "密码", "paste": "粘贴", "pause_wallet_creation": "创建 Haven 钱包的功能当前已暂停。", + "payjoin_card_content": "Payjoin是比特币中的新的隐私权和节省费用的功能。", "payjoin_details": "Payjoin 细节", "payjoin_enabled": "Payjoin启用", + "payjoin_enabling_popup_content": "您可以在菜单下切换 Payjoin隐私→使用 Payjoin以后启用或禁用它。", + "payjoin_enabling_popup_title": "启用 Payjoin", "payjoin_request_awaiting_tx": "等待交易", "payjoin_request_in_progress": "进行中", + "payjoin_unavailable": "Payjoin不可用", + "payjoin_unavailable_sheet_content": "接收 Payjoin Transaction需要您准备使用比特币。", + "payjoin_unavailable_sheet_title": "为什么 Payjoin不可用?", "payment_id": "付款 ID: ", "payment_was_received": "您的付款已收到。", "pending": " (待定)", @@ -1071,4 +1077,4 @@ "you_will_send": "转换自", "youCanGoBackToYourDapp": "您现在可以回到DAPP", "yy": "YY" -} \ No newline at end of file +} From fd3158293eaf1364311e4ad2a14414a236bcfb9c Mon Sep 17 00:00:00 2001 From: cyan Date: Sat, 24 May 2025 21:21:49 +0200 Subject: [PATCH 07/30] fix(cw_monero): properly await transaction commit (#2284) --- cw_monero/lib/api/transaction_history.dart | 10 +++++----- cw_monero/lib/pending_monero_transaction.dart | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cw_monero/lib/api/transaction_history.dart b/cw_monero/lib/api/transaction_history.dart index 4360149d2..2fe0888f6 100644 --- a/cw_monero/lib/api/transaction_history.dart +++ b/cw_monero/lib/api/transaction_history.dart @@ -251,13 +251,13 @@ Future createTransactionMultDest( ); } -String? commitTransactionFromPointerAddress({required int address, required bool useUR}) => +Future commitTransactionFromPointerAddress({required int address, required bool useUR}) => commitTransaction(tx: MoneroPendingTransaction(Pointer.fromAddress(address)), useUR: useUR); -String? commitTransaction({required Wallet2PendingTransaction tx, required bool useUR}) { +Future commitTransaction({required Wallet2PendingTransaction tx, required bool useUR}) async { final txCommit = useUR ? tx.commitUR(120) - : Isolate.run(() { + : await Isolate.run(() { monero.PendingTransaction_commit( Pointer.fromAddress(tx.ffiAddress()), filename: '', @@ -286,9 +286,9 @@ String? commitTransaction({required Wallet2PendingTransaction tx, required bool throw CreationTransactionException(message: error); } if (useUR) { - return txCommit as String?; + return Future.value(txCommit as String?); } else { - return null; + return Future.value(null); } } diff --git a/cw_monero/lib/pending_monero_transaction.dart b/cw_monero/lib/pending_monero_transaction.dart index 4f8606a79..f29d1ccd2 100644 --- a/cw_monero/lib/pending_monero_transaction.dart +++ b/cw_monero/lib/pending_monero_transaction.dart @@ -46,7 +46,7 @@ class PendingMoneroTransaction with PendingTransaction { @override Future commit() async { try { - monero_transaction_history.commitTransactionFromPointerAddress( + await monero_transaction_history.commitTransactionFromPointerAddress( address: pendingTransactionDescription.pointerAddress, useUR: false); } catch (e) { @@ -68,7 +68,7 @@ class PendingMoneroTransaction with PendingTransaction { @override Future commitUR() async { try { - final ret = monero_transaction_history.commitTransactionFromPointerAddress( + final ret = await monero_transaction_history.commitTransactionFromPointerAddress( address: pendingTransactionDescription.pointerAddress, useUR: true); storeSync(force: true); From df20e22faeb55b6ebe456ccd3ddbb1103535001f Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Sun, 25 May 2025 03:59:18 +0300 Subject: [PATCH 08/30] minor: ignore package unreasonable error --- lib/utils/exception_handler.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index bba9d71ac..954d4f532 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -219,6 +219,7 @@ class ExceptionHandler { "invalid password", "NetworkImage._loadAsync", "SSLV3_ALERT_BAD_RECORD_MAC", + "PlatformException(already_active, File picker is already active", // Temporary ignored, More context: Flutter secure storage reads the values as null some times // probably when the device was locked and then opened on Cake // this is solved by a restart of the app From 7b8ddf9685a25d3e9d5ece681560cfc8d7d0cb9e Mon Sep 17 00:00:00 2001 From: rafael_xmr Date: Sun, 25 May 2025 16:28:08 -0300 Subject: [PATCH 09/30] FIX (#2283) * FIX! * resolve conflicts with main * undo debug changes * fix: methods * fix: methods2 * Fix Tron issue * fix: 1k limit & reaching top * fix: missing unspents * fix: missing commit --------- Co-authored-by: OmarHatem --- cw_bitcoin/lib/address_from_output.dart | 21 +- cw_bitcoin/lib/bitcoin_address_record.dart | 2 +- .../lib/bitcoin_receive_page_option.dart | 16 +- cw_bitcoin/lib/bitcoin_wallet.dart | 64 ++-- cw_bitcoin/lib/bitcoin_wallet_addresses.dart | 4 +- cw_bitcoin/lib/electrum_wallet.dart | 343 +++++++++++++++--- cw_bitcoin/lib/electrum_wallet_addresses.dart | 58 +-- cw_bitcoin/lib/electrum_wallet_snapshot.dart | 8 +- cw_bitcoin/lib/litecoin_wallet.dart | 17 +- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 10 +- cw_bitcoin/lib/payjoin/manager.dart | 28 +- cw_bitcoin/lib/psbt/signer.dart | 78 ++-- cw_bitcoin/lib/psbt/transaction_builder.dart | 34 +- cw_bitcoin/lib/psbt/utils.dart | 2 +- cw_bitcoin/pubspec.lock | 22 +- cw_bitcoin/pubspec.yaml | 6 +- cw_bitcoin_cash/pubspec.yaml | 4 +- cw_core/lib/solana_rpc_http_service.dart | 33 +- cw_core/pubspec.lock | 12 +- cw_core/pubspec.yaml | 2 +- cw_decred/pubspec.lock | 12 +- cw_monero/pubspec.lock | 12 +- cw_nano/pubspec.lock | 12 +- cw_solana/lib/solana_client.dart | 32 +- cw_solana/lib/solana_wallet.dart | 2 +- cw_solana/pubspec.yaml | 4 +- cw_tron/lib/tron_client.dart | 8 +- cw_tron/lib/tron_http_provider.dart | 49 +-- cw_tron/lib/tron_transaction_model.dart | 3 +- cw_tron/lib/tron_wallet.dart | 43 ++- cw_tron/pubspec.yaml | 4 +- cw_wownero/pubspec.lock | 12 +- cw_zano/pubspec.lock | 12 +- lib/bitcoin/cw_bitcoin.dart | 18 +- pubspec_base.yaml | 6 +- tool/download_moneroc_prebuilds.py | 76 ++++ 36 files changed, 667 insertions(+), 402 deletions(-) create mode 100755 tool/download_moneroc_prebuilds.py diff --git a/cw_bitcoin/lib/address_from_output.dart b/cw_bitcoin/lib/address_from_output.dart index 0d985b237..d6e931068 100644 --- a/cw_bitcoin/lib/address_from_output.dart +++ b/cw_bitcoin/lib/address_from_output.dart @@ -17,21 +17,16 @@ BitcoinBaseAddress addressFromScript(Script script, switch (addressType) { case P2pkhAddressType.p2pkh: - return P2pkhAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); + return P2pkhAddress.fromScriptPubkey(script: script); case P2shAddressType.p2pkhInP2sh: case P2shAddressType.p2pkInP2sh: - return P2shAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); - case SegwitAddresType.p2wpkh: - return P2wpkhAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); - case SegwitAddresType.p2wsh: - return P2wshAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); - case SegwitAddresType.p2tr: - return P2trAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); + return P2shAddress.fromScriptPubkey(script: script); + case SegwitAddressType.p2wpkh: + return P2wpkhAddress.fromScriptPubkey(script: script); + case SegwitAddressType.p2wsh: + return P2wshAddress.fromScriptPubkey(script: script); + case SegwitAddressType.p2tr: + return P2trAddress.fromScriptPubkey(script: script); } throw ArgumentError("Invalid script"); diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index 1509f913a..97b3c08f8 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -82,7 +82,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { type: decoded['type'] != null && decoded['type'] != '' ? BitcoinAddressType.values .firstWhere((type) => type.toString() == decoded['type'] as String) - : SegwitAddresType.p2wpkh, + : SegwitAddressType.p2wpkh, scriptHash: decoded['scriptHash'] as String?, network: network, ); diff --git a/cw_bitcoin/lib/bitcoin_receive_page_option.dart b/cw_bitcoin/lib/bitcoin_receive_page_option.dart index 07083e111..8331a182d 100644 --- a/cw_bitcoin/lib/bitcoin_receive_page_option.dart +++ b/cw_bitcoin/lib/bitcoin_receive_page_option.dart @@ -36,9 +36,9 @@ class BitcoinReceivePageOption implements ReceivePageOption { BitcoinAddressType toType() { switch (this) { case BitcoinReceivePageOption.p2tr: - return SegwitAddresType.p2tr; + return SegwitAddressType.p2tr; case BitcoinReceivePageOption.p2wsh: - return SegwitAddresType.p2wsh; + return SegwitAddressType.p2wsh; case BitcoinReceivePageOption.p2pkh: return P2pkhAddressType.p2pkh; case BitcoinReceivePageOption.p2sh: @@ -46,20 +46,20 @@ class BitcoinReceivePageOption implements ReceivePageOption { case BitcoinReceivePageOption.silent_payments: return SilentPaymentsAddresType.p2sp; case BitcoinReceivePageOption.mweb: - return SegwitAddresType.mweb; + return SegwitAddressType.mweb; case BitcoinReceivePageOption.p2wpkh: default: - return SegwitAddresType.p2wpkh; + return SegwitAddressType.p2wpkh; } } factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) { switch (type) { - case SegwitAddresType.p2tr: + case SegwitAddressType.p2tr: return BitcoinReceivePageOption.p2tr; - case SegwitAddresType.p2wsh: + case SegwitAddressType.p2wsh: return BitcoinReceivePageOption.p2wsh; - case SegwitAddresType.mweb: + case SegwitAddressType.mweb: return BitcoinReceivePageOption.mweb; case P2pkhAddressType.p2pkh: return BitcoinReceivePageOption.p2pkh; @@ -67,7 +67,7 @@ class BitcoinReceivePageOption implements ReceivePageOption { return BitcoinReceivePageOption.p2sh; case SilentPaymentsAddresType.p2sp: return BitcoinReceivePageOption.silent_payments; - case SegwitAddresType.p2wpkh: + case SegwitAddressType.p2wpkh: default: return BitcoinReceivePageOption.p2wpkh; } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index a23b72660..73a6ad0ea 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -73,9 +73,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialBalance: initialBalance, seedBytes: seedBytes, encryptionFileUtils: encryptionFileUtils, - currency: networkParam == BitcoinNetwork.testnet - ? CryptoCurrency.tbtc - : CryptoCurrency.btc, + currency: + networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc, alwaysScan: alwaysScan, ) { // in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here) @@ -94,14 +93,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { mainHd: hd, sideHd: accountHD.childKey(Bip32KeyIndex(1)), network: networkParam ?? network, - masterHd: - seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null, + masterHd: seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null, isHardwareWallet: walletInfo.isHardwareWallet, payjoinManager: payjoinManager); autorun((_) { - this.walletAddresses.isEnabledAutoGenerateSubaddress = - this.isEnabledAutoGenerateSubaddress; + this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; }); } @@ -136,8 +133,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { break; case DerivationType.electrum: default: - seedBytes = - await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); + seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); break; } @@ -210,10 +206,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { walletInfo.derivationInfo ??= DerivationInfo(); // set the default if not present: - walletInfo.derivationInfo!.derivationPath ??= - snp?.derivationPath ?? electrum_path; - walletInfo.derivationInfo!.derivationType ??= - snp?.derivationType ?? DerivationType.electrum; + walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path; + walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum; Uint8List? seedBytes = null; final mnemonic = keysData.mnemonic; @@ -222,8 +216,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { if (mnemonic != null) { switch (walletInfo.derivationInfo!.derivationType) { case DerivationType.electrum: - seedBytes = - await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); + seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); break; case DerivationType.bip39: default: @@ -269,8 +262,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { late final PayjoinManager payjoinManager; bool get isPayjoinAvailable => unspentCoinsInfo.values - .where((element) => - element.walletId == id && element.isSending && !element.isFrozen) + .where((element) => element.walletId == id && element.isSending && !element.isFrozen) .isNotEmpty; Future buildPsbt({ @@ -287,10 +279,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { }) async { final psbtReadyInputs = []; for (final utxo in utxos) { - final rawTx = - await electrumClient.getTransactionHex(hash: utxo.utxo.txHash); - final publicKeyAndDerivationPath = - publicKeys[utxo.ownerDetails.address.pubKeyHash()]!; + final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash); + final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!; psbtReadyInputs.add(PSBTReadyUtxoWithAddress( utxo: utxo.utxo, @@ -302,8 +292,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { )); } - return PSBTTransactionBuild( - inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF) + return PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF) .psbt; } @@ -342,8 +331,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { Future createTransaction(Object credentials) async { credentials = credentials as BitcoinTransactionCredentials; - final tx = (await super.createTransaction(credentials)) - as PendingBitcoinTransaction; + final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction; final payjoinUri = credentials.payjoinUri; if (payjoinUri == null) return tx; @@ -366,12 +354,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { publicKeys: tx.publicKeys!, masterFingerprint: Uint8List(0)); - final originalPsbt = await signPsbt( - base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys()); + final originalPsbt = + await signPsbt(base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys()); tx.commitOverride = () async { - final sender = await payjoinManager.initSender( - payjoinUri, originalPsbt, int.parse(tx.feeRate)); + final sender = + await payjoinManager.initSender(payjoinUri, originalPsbt, int.parse(tx.feeRate)); payjoinManager.spawnNewSender( sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount)); }; @@ -387,8 +375,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { Future commitPsbt(String finalizedPsbt) { final psbt = PsbtV2()..deserializeV0(base64.decode(finalizedPsbt)); - final btcTx = - BtcTransaction.fromRaw(BytesUtils.toHexString(psbt.extract())); + final btcTx = BtcTransaction.fromRaw(BytesUtils.toHexString(psbt.extract())); return PendingBitcoinTransaction( btcTx, @@ -402,12 +389,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ).commit(); } - Future signPsbt( - String preProcessedPsbt, List utxos) async { + Future signPsbt(String preProcessedPsbt, List utxos) async { final psbt = PsbtV2()..deserializeV0(base64Decode(preProcessedPsbt)); await psbt.signWithUTXO(utxos, (txDigest, utxo, key, sighash) { - return utxo.utxo.isP2tr() + return utxo.utxo.isP2tr ? key.signTapRoot( txDigest, sighash: sighash, @@ -428,17 +414,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { Future signMessage(String message, {String? address = null}) async { if (walletInfo.isHardwareWallet) { final addressEntry = address != null - ? walletAddresses.allAddresses - .firstWhere((element) => element.address == address) + ? walletAddresses.allAddresses.firstWhere((element) => element.address == address) : null; final index = addressEntry?.index ?? 0; final isChange = addressEntry?.isHidden == true ? 1 : 0; final accountPath = walletInfo.derivationInfo?.derivationPath; - final derivationPath = - accountPath != null ? "$accountPath/$isChange/$index" : null; + final derivationPath = accountPath != null ? "$accountPath/$isChange/$index" : null; - final signature = await _bitcoinLedgerApp!.signMessage( - message: ascii.encode(message), signDerivationPath: derivationPath); + final signature = await _bitcoinLedgerApp! + .signMessage(message: ascii.encode(message), signDerivationPath: derivationPath); return base64Encode(signature); } diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index 0fefe4e57..dd6dc5fae 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -47,10 +47,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S if (addressType == P2pkhAddressType.p2pkh) return generateP2PKHAddress(hd: hd, index: index, network: network); - if (addressType == SegwitAddresType.p2tr) + if (addressType == SegwitAddressType.p2tr) return generateP2TRAddress(hd: hd, index: index, network: network); - if (addressType == SegwitAddresType.p2wsh) + if (addressType == SegwitAddressType.p2wsh) return generateP2WSHAddress(hd: hd, index: index, network: network); if (addressType == P2shAddressType.p2wpkhInP2sh) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 35c15682c..d7449c011 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -5,7 +5,6 @@ import 'dart:isolate'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_amount_format.dart'; -import 'package:cw_core/format_amount.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_bitcoin/bitcoin_wallet.dart'; import 'package:cw_bitcoin/litecoin_wallet.dart'; @@ -18,7 +17,7 @@ import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/bitcoin_wallet_keys.dart'; -import 'package:cw_bitcoin/electrum.dart'; +import 'package:cw_bitcoin/electrum.dart' as electrum; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_bitcoin/electrum_transaction_history.dart'; @@ -69,7 +68,7 @@ abstract class ElectrumWalletBase Uint8List? seedBytes, this.passphrase, List? initialAddresses, - ElectrumClient? electrumClient, + electrum.ElectrumClient? electrumClient, ElectrumBalance? initialBalance, CryptoCurrency? currency, this.alwaysScan, @@ -96,7 +95,7 @@ abstract class ElectrumWalletBase this.isTestnet = !network.isMainnet, this._mnemonic = mnemonic, super(walletInfo) { - this.electrumClient = electrumClient ?? ElectrumClient(); + this.electrumClient = electrumClient ?? electrum.ElectrumClient(); this.walletInfo = walletInfo; transactionHistory = ElectrumTransactionHistory( walletInfo: walletInfo, @@ -167,7 +166,7 @@ abstract class ElectrumWalletBase @observable bool isEnabledAutoGenerateSubaddress; - late ElectrumClient electrumClient; + late electrum.ElectrumClient electrumClient; Box unspentCoinsInfo; @override @@ -182,7 +181,7 @@ abstract class ElectrumWalletBase SyncStatus syncStatus; Set get addressesSet => walletAddresses.allAddresses - .where((element) => element.type != SegwitAddresType.mweb) + .where((element) => element.type != SegwitAddressType.mweb) .map((addr) => addr.address) .toSet(); @@ -333,14 +332,14 @@ abstract class ElectrumWalletBase final receivePort = ReceivePort(); _isolate = Isolate.spawn( - startRefresh, + _handleScanSilentPayments, ScanData( sendPort: receivePort.sendPort, silentAddress: walletAddresses.silentAddress!, network: network, height: height, chainTip: chainTip, - electrumClient: ElectrumClient(), + electrumClient: electrum.ElectrumClient(), transactionHistoryIds: transactionHistory.transactions.keys.toList(), node: (await getNodeSupportsSilentPayments()) == true ? ScanNode(node!.uri, node!.useSSL) @@ -439,7 +438,6 @@ abstract class ElectrumWalletBase BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)), ) : silentAddress.B_spend, - network: network, ); final addressRecord = walletAddresses.silentAddresses @@ -564,7 +562,7 @@ abstract class ElectrumWalletBase node!.save(); return node!.supportsSilentPayments!; } - } on RequestFailedTimeoutException catch (_) { + } on electrum.RequestFailedTimeoutException catch (_) { node!.supportsSilentPayments = false; node!.save(); return node!.supportsSilentPayments!; @@ -625,9 +623,9 @@ abstract class ElectrumWalletBase switch (coinTypeToSpendFrom) { case UnspentCoinType.mweb: - return utx.bitcoinAddressRecord.type == SegwitAddresType.mweb; + return utx.bitcoinAddressRecord.type == SegwitAddressType.mweb; case UnspentCoinType.nonMweb: - return utx.bitcoinAddressRecord.type != SegwitAddresType.mweb; + return utx.bitcoinAddressRecord.type != SegwitAddressType.mweb; case UnspentCoinType.any: return true; } @@ -635,7 +633,7 @@ abstract class ElectrumWalletBase final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList(); // sort the unconfirmed coins so that mweb coins are last: - availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddresType.mweb ? 1 : -1); + availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddressType.mweb ? 1 : -1); for (int i = 0; i < availableInputs.length; i++) { final utx = availableInputs[i]; @@ -643,7 +641,7 @@ abstract class ElectrumWalletBase if (paysToSilentPayment) { // Check inputs for shared secret derivation - if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) { + if (utx.bitcoinAddressRecord.type == SegwitAddressType.p2wsh) { throw BitcoinTransactionSilentPaymentsNotSupported(); } } @@ -678,7 +676,7 @@ abstract class ElectrumWalletBase if (privkey != null) { inputPrivKeyInfos.add(ECPrivateInfo( privkey, - address.type == SegwitAddresType.p2tr, + address.type == SegwitAddressType.p2tr, tweak: !isSilentPayment, )); @@ -1164,7 +1162,7 @@ abstract class ElectrumWalletBase throw Exception(error); } - if (utxo.utxo.isP2tr()) { + if (utxo.utxo.isP2tr) { hasTaprootInputs = true; return key.privkey.signTapRoot( txDigest, @@ -1176,20 +1174,18 @@ abstract class ElectrumWalletBase } }); - return PendingBitcoinTransaction( - transaction, - type, - electrumClient: electrumClient, - amount: estimatedTx.amount, - fee: estimatedTx.fee, - feeRate: feeRateInt.toString(), - network: network, - hasChange: estimatedTx.hasChange, - isSendAll: estimatedTx.isSendAll, - hasTaprootInputs: hasTaprootInputs, - utxos: estimatedTx.utxos, - publicKeys: estimatedTx.publicKeys - )..addListener((transaction) async { + return PendingBitcoinTransaction(transaction, type, + electrumClient: electrumClient, + amount: estimatedTx.amount, + fee: estimatedTx.fee, + feeRate: feeRateInt.toString(), + network: network, + hasChange: estimatedTx.hasChange, + isSendAll: estimatedTx.isSendAll, + hasTaprootInputs: hasTaprootInputs, + utxos: estimatedTx.utxos, + publicKeys: estimatedTx.publicKeys) + ..addListener((transaction) async { transactionHistory.addOne(transaction); if (estimatedTx.spendsSilentPayment) { transactionHistory.transactions.values.forEach((tx) { @@ -1233,7 +1229,7 @@ abstract class ElectrumWalletBase 'change_address_index': walletAddresses.currentChangeAddressIndexByType, 'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(), 'address_page_type': walletInfo.addressPageType == null - ? SegwitAddresType.p2wpkh.toString() + ? SegwitAddressType.p2wpkh.toString() : walletInfo.addressPageType.toString(), 'balance': balance[currency]?.toJSON(), 'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index, @@ -1373,7 +1369,7 @@ abstract class ElectrumWalletBase List updatedUnspentCoins = []; final previousUnspentCoins = List.from(unspentCoins.where((utxo) => - utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb && + utxo.bitcoinAddressRecord.type != SegwitAddressType.mweb && utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)); if (hasSilentPaymentsScanning) { @@ -1387,13 +1383,13 @@ abstract class ElectrumWalletBase // Set the balance of all non-silent payment and non-mweb addresses to 0 before updating walletAddresses.allAddresses - .where((element) => element.type != SegwitAddresType.mweb) + .where((element) => element.type != SegwitAddressType.mweb) .forEach((addr) { if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0; }); final addressFutures = walletAddresses.allAddresses - .where((element) => element.type != SegwitAddresType.mweb) + .where((element) => element.type != SegwitAddressType.mweb) .map((address) => fetchUnspent(address)) .toList(); @@ -1834,7 +1830,7 @@ abstract class ElectrumWalletBase throw Exception("Cannot find private key"); } - if (utxo.utxo.isP2tr()) { + if (utxo.utxo.isP2tr) { return key.signTapRoot(txDigest, sighash: sighash); } else { return key.signInput(txDigest, sigHash: sighash); @@ -1984,7 +1980,7 @@ abstract class ElectrumWalletBase .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); } else if (type == WalletType.litecoin) { await Future.wait(LITECOIN_ADDRESS_TYPES - .where((type) => type != SegwitAddresType.mweb) + .where((type) => type != SegwitAddressType.mweb) .map((type) => fetchTransactionsForAddressType(historiesWithDetails, type))); } @@ -2173,7 +2169,7 @@ abstract class ElectrumWalletBase final unsubscribedScriptHashes = walletAddresses.allAddresses.where( (address) => !_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) && - address.type != SegwitAddresType.mweb, + address.type != SegwitAddressType.mweb, ); await Future.wait(unsubscribedScriptHashes.map((address) async { @@ -2396,9 +2392,9 @@ abstract class ElectrumWalletBase derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1); @action - void _onConnectionStatusChange(ConnectionStatus status) { + void _onConnectionStatusChange(electrum.ConnectionStatus status) { switch (status) { - case ConnectionStatus.connected: + case electrum.ConnectionStatus.connected: if (syncStatus is NotConnectedSyncStatus || syncStatus is LostConnectionSyncStatus || syncStatus is ConnectingSyncStatus) { @@ -2406,19 +2402,19 @@ abstract class ElectrumWalletBase } break; - case ConnectionStatus.disconnected: + case electrum.ConnectionStatus.disconnected: if (syncStatus is! NotConnectedSyncStatus && syncStatus is! ConnectingSyncStatus && syncStatus is! SyncronizingSyncStatus) { syncStatus = NotConnectedSyncStatus(); } break; - case ConnectionStatus.failed: + case electrum.ConnectionStatus.failed: if (syncStatus is! LostConnectionSyncStatus) { syncStatus = LostConnectionSyncStatus(); } break; - case ConnectionStatus.connecting: + case electrum.ConnectionStatus.connecting: if (syncStatus is! ConnectingSyncStatus) { syncStatus = ConnectingSyncStatus(); } @@ -2530,7 +2526,7 @@ class ScanData { final ScanNode? node; final BasedUtxoNetwork network; final int chainTip; - final ElectrumClient electrumClient; + final electrum.ElectrumClient electrumClient; final List transactionHistoryIds; final Map labels; final List labelIndexes; @@ -2574,6 +2570,234 @@ class SyncResponse { SyncResponse(this.height, this.syncStatus); } +Future _handleScanSilentPayments(ScanData scanData) async { + try { + // if (scanData.shouldSwitchNodes) { + var scanningClient = await ElectrumProvider.connect( + ElectrumTCPService.connect( + Uri.parse("tcp://electrs.cakewallet.com:50001"), + ), + ); + // } + + int syncHeight = scanData.height; + int initialSyncHeight = syncHeight; + + final receiver = Receiver( + scanData.silentAddress.b_scan.toHex(), + scanData.silentAddress.B_spend.toHex(), + scanData.network == BitcoinNetwork.testnet, + scanData.labelIndexes, + ); + + int getCountToScanPerRequest(int syncHeight) { + if (scanData.isSingleScan) { + return 1; + } + + final amountLeft = scanData.chainTip - syncHeight + 1; + return amountLeft; + } + + // Initial status UI update, send how many blocks in total to scan + scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight))); + + final req = ElectrumTweaksSubscribe( + height: syncHeight, + count: getCountToScanPerRequest(syncHeight), + historicalMode: false, + ); + + var _scanningStream = await scanningClient.subscribe(req); + + void listenFn(Map event, ElectrumTweaksSubscribe req) { + final response = req.onResponse(event); + + if (response == null || _scanningStream == null) { + return; + } + + // is success or error msg + final noData = response.message != null; + + if (noData) { + if (scanData.isSingleScan) { + return; + } + + // re-subscribe to continue receiving messages, starting from the next unscanned height + final nextHeight = syncHeight + 1; + + if (nextHeight <= scanData.chainTip) { + final nextStream = scanningClient.subscribe( + ElectrumTweaksSubscribe( + height: nextHeight, + count: getCountToScanPerRequest(nextHeight), + historicalMode: false, + ), + ); + + if (nextStream != null) { + nextStream.listen((event) => listenFn(event, req)); + } else { + scanData.sendPort.send( + SyncResponse(scanData.height, LostConnectionSyncStatus()), + ); + } + } + + return; + } + + final tweakHeight = response.block; + + if (initialSyncHeight < tweakHeight) initialSyncHeight = tweakHeight; + + // Continuous status UI update, send how many blocks left to scan + final syncingStatus = scanData.isSingleScan + ? SyncingSyncStatus(1, 0) + : SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, tweakHeight); + + scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus)); + + try { + final blockTweaks = response.blockTweaks; + + for (final txid in blockTweaks.keys) { + final tweakData = blockTweaks[txid]; + final outputPubkeys = tweakData!.outputPubkeys; + final tweak = tweakData.tweak; + + try { + final addToWallet = {}; + + // receivers.forEach((receiver) { + // NOTE: scanOutputs, from sp_scanner package, called from rust here + final scanResult = scanOutputs([outputPubkeys.keys.toList()], tweak, receiver); + + if (scanResult.isEmpty) { + continue; + } + + if (addToWallet[receiver.BSpend] == null) { + addToWallet[receiver.BSpend] = scanResult; + } else { + addToWallet[receiver.BSpend].addAll(scanResult); + } + // }); + + if (addToWallet.isEmpty) { + // no results tx, continue to next tx + continue; + } + + // initial placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s) on the following loop + final txInfo = ElectrumTransactionInfo( + WalletType.bitcoin, + id: txid, + height: tweakHeight, + amount: 0, + fee: 0, + direction: TransactionDirection.incoming, + isReplaced: false, + date: DateTime.fromMillisecondsSinceEpoch( + DateTime.now().millisecondsSinceEpoch * 1000, + ), + confirmations: scanData.chainTip - tweakHeight + 1, + isReceivedSilentPayment: true, + isPending: false, + unspents: [], + ); + + List unspents = []; + + addToWallet.forEach((BSpend, scanResultPerLabel) { + scanResultPerLabel.forEach((label, scanOutput) { + final labelValue = label == "None" ? null : label.toString(); + + (scanOutput as Map).forEach((outputPubkey, tweak) { + final t_k = tweak as String; + + final receivingOutputAddress = ECPublic.fromHex(outputPubkey) + .toTaprootAddress(tweak: false) + .toAddress(scanData.network); + + final matchingOutput = outputPubkeys[outputPubkey]!; + final amount = matchingOutput.amount; + final pos = matchingOutput.vout; + + // final matchingSPWallet = scanData.silentPaymentsWallets.firstWhere( + // (receiver) => receiver.B_spend.toHex() == BSpend.toString(), + // ); + + // final labelIndex = labelValue != null ? scanData.labels[label] : 0; + // final balance = ElectrumBalance(); + // balance.confirmed = amount; + + final receivedAddressRecord = BitcoinSilentPaymentAddressRecord( + receivingOutputAddress, + index: 0, + isHidden: false, + isUsed: true, + network: scanData.network, + silentPaymentTweak: t_k, + type: SegwitAddressType.p2tr, + txCount: 1, + balance: amount, + ); + + final unspent = BitcoinSilentPaymentsUnspent( + receivedAddressRecord, + txid, + amount, + pos, + silentPaymentTweak: t_k, + silentPaymentLabel: labelValue, + ); + + unspents.add(unspent); + txInfo.unspents!.add(unspent); + txInfo.amount += unspent.value; + }); + }); + }); + + scanData.sendPort.send({txInfo.id: txInfo}); + } catch (e, stacktrace) { + printV(stacktrace); + printV(e.toString()); + } + } + } catch (e, stacktrace) { + printV(stacktrace); + printV(e.toString()); + } + + syncHeight = tweakHeight; + + if ((tweakHeight >= scanData.chainTip) || scanData.isSingleScan) { + if (tweakHeight >= scanData.chainTip) + scanData.sendPort.send( + SyncResponse(syncHeight, SyncedTipSyncStatus(scanData.chainTip)), + ); + + if (scanData.isSingleScan) { + scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus())); + } + + _scanningStream?.close(); + _scanningStream = null; + return; + } + } + + _scanningStream?.listen((event) => listenFn(event, req)); + } catch (e) { + printV("Error in _handleScanSilentPayments: $e"); + scanData.sendPort.send(SyncResponse(scanData.height, LostConnectionSyncStatus())); + } +} + Future startRefresh(ScanData scanData) async { int syncHeight = scanData.height; int initialSyncHeight = syncHeight; @@ -2586,7 +2810,7 @@ Future startRefresh(ScanData scanData) async { useSSL: scanData.node?.useSSL ?? false, ); - int getCountPerRequest(int syncHeight) { + int getCountToScanPerRequest(int syncHeight) { if (scanData.isSingleScan) { return 1; } @@ -2601,11 +2825,10 @@ Future startRefresh(ScanData scanData) async { scanData.silentAddress.B_spend.toHex(), scanData.network == BitcoinNetwork.testnet, scanData.labelIndexes, - scanData.labelIndexes.length, ); // Initial status UI update, send how many blocks in total to scan - final initialCount = getCountPerRequest(syncHeight); + final initialCount = getCountToScanPerRequest(syncHeight); scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight))); tweaksSubscription = await electrumClient.tweaksSubscribe( @@ -2616,22 +2839,24 @@ Future startRefresh(ScanData scanData) async { Future listenFn(t) async { final tweaks = t as Map; final msg = tweaks["message"]; - // success or error msg + + // is success or error msg final noData = msg != null; if (noData) { + if (scanData.isSingleScan) { + return; + } + // re-subscribe to continue receiving messages, starting from the next unscanned height final nextHeight = syncHeight + 1; - final nextCount = getCountPerRequest(nextHeight); - if (nextCount > 0) { - tweaksSubscription?.close(); - - final nextTweaksSubscription = electrumClient.tweaksSubscribe( + if (nextHeight <= scanData.chainTip) { + final nextStream = electrumClient.tweaksSubscribe( height: nextHeight, - count: nextCount, + count: getCountToScanPerRequest(nextHeight), ); - nextTweaksSubscription?.listen(listenFn); + nextStream?.listen(listenFn); } return; @@ -2713,7 +2938,7 @@ Future startRefresh(ScanData scanData) async { isUsed: true, network: scanData.network, silentPaymentTweak: t_k, - type: SegwitAddresType.p2tr, + type: SegwitAddressType.p2tr, txCount: 1, balance: amount!, ); @@ -2806,15 +3031,15 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) { } else if (type is P2shAddress) { return P2shAddressType.p2wpkhInP2sh; } else if (type is P2wshAddress) { - return SegwitAddresType.p2wsh; + return SegwitAddressType.p2wsh; } else if (type is P2trAddress) { - return SegwitAddresType.p2tr; + return SegwitAddressType.p2tr; } else if (type is MwebAddress) { - return SegwitAddresType.mweb; + return SegwitAddressType.mweb; } else if (type is SilentPaymentsAddresType) { return SilentPaymentsAddresType.p2sp; } else { - return SegwitAddresType.p2wpkh; + return SegwitAddressType.p2wpkh; } } diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 614a06a3b..9d1ae54aa 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -17,16 +17,16 @@ part 'electrum_wallet_addresses.g.dart'; class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses; const List BITCOIN_ADDRESS_TYPES = [ - SegwitAddresType.p2wpkh, + SegwitAddressType.p2wpkh, P2pkhAddressType.p2pkh, - SegwitAddresType.p2tr, - SegwitAddresType.p2wsh, + SegwitAddressType.p2tr, + SegwitAddressType.p2wsh, P2shAddressType.p2wpkhInP2sh, ]; const List LITECOIN_ADDRESS_TYPES = [ - SegwitAddresType.p2wpkh, - SegwitAddresType.mweb, + SegwitAddressType.p2wpkh, + SegwitAddressType.mweb, ]; const List BITCOIN_CASH_ADDRESS_TYPES = [ @@ -62,7 +62,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { _addressPageType = initialAddressPageType ?? (walletInfo.addressPageType != null ? BitcoinAddressType.fromValue(walletInfo.addressPageType!) - : SegwitAddresType.p2wpkh), + : SegwitAddressType.p2wpkh), silentAddresses = ObservableList.of( (initialSilentAddresses ?? []).toSet()), currentSilentAddressIndex = initialSilentAddressIndex, @@ -71,9 +71,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { super(walletInfo) { if (masterHd != null) { silentAddress = SilentPaymentOwner.fromPrivateKeys( - b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privateKey.toHex()), - b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privateKey.toHex()), - network: network, + b_scan: ECPrivate.fromHex( + masterHd.derivePath("m/352'/1'/0'/1'/0").privateKey.toHex(), + ), + b_spend: ECPrivate.fromHex( + masterHd.derivePath("m/352'/1'/0'/0'/0").privateKey.toHex(), + ), ); if (silentAddresses.length == 0) { @@ -144,12 +147,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return silentAddress.toString(); } - final typeMatchingAddresses = _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList(); - final typeMatchingReceiveAddresses = typeMatchingAddresses.where((addr) => !addr.isUsed).toList(); + final typeMatchingAddresses = + _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList(); + final typeMatchingReceiveAddresses = + typeMatchingAddresses.where((addr) => !addr.isUsed).toList(); if (!isEnabledAutoGenerateSubaddress) { - if (previousAddressRecord != null && - previousAddressRecord!.type == addressPageType) { + if (previousAddressRecord != null && previousAddressRecord!.type == addressPageType) { return previousAddressRecord!.address; } @@ -249,17 +253,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { if (walletInfo.type == WalletType.bitcoinCash) { await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); } else if (walletInfo.type == WalletType.litecoin) { - await _generateInitialAddresses(type: SegwitAddresType.p2wpkh); + await _generateInitialAddresses(type: SegwitAddressType.p2wpkh); if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) { - await _generateInitialAddresses(type: SegwitAddresType.mweb); + await _generateInitialAddresses(type: SegwitAddressType.mweb); } } else if (walletInfo.type == WalletType.bitcoin) { await _generateInitialAddresses(); if (!isHardwareWallet) { await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh); - await _generateInitialAddresses(type: SegwitAddresType.p2tr); - await _generateInitialAddresses(type: SegwitAddresType.p2wsh); + await _generateInitialAddresses(type: SegwitAddressType.p2tr); + await _generateInitialAddresses(type: SegwitAddressType.p2wsh); } } @@ -323,7 +327,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { BaseBitcoinAddressRecord generateNewAddress({String label = ''}) { if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) { final currentSilentAddressIndex = silentAddresses - .where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr) + .where((addressRecord) => addressRecord.type != SegwitAddressType.p2tr) .length - 1; @@ -381,7 +385,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void addBitcoinAddressTypes() { final lastP2wpkh = _addresses .where((addressRecord) => - _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) + _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh)) .toList() .last; if (lastP2wpkh.address != address) { @@ -407,7 +411,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } final lastP2tr = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr)); + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2tr)); if (lastP2tr.address != address) { addressesMap[lastP2tr.address] = 'P2TR'; } else { @@ -415,7 +419,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } final lastP2wsh = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh)); + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wsh)); if (lastP2wsh.address != address) { addressesMap[lastP2wsh.address] = 'P2WSH'; } else { @@ -440,7 +444,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void addLitecoinAddressTypes() { final lastP2wpkh = _addresses .where((addressRecord) => - _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh)) + _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh)) .toList() .last; if (lastP2wpkh.address != address) { @@ -450,7 +454,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } final lastMweb = _addresses.firstWhere( - (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb)); + (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.mweb)); if (lastMweb.address != address) { addressesMap[lastMweb.address] = 'MWEB'; } else { @@ -560,14 +564,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { addressRecord.isHidden && !addressRecord.isUsed && // TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type - (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh)); + (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddressType.p2wpkh)); changeAddresses.addAll(newAddresses); } @action Future discoverAddresses(List addressList, bool isHidden, Future Function(BitcoinAddressRecord) getAddressHistory, - {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async { + {BitcoinAddressType type = SegwitAddressType.p2wpkh}) async { final newAddresses = await _createNewAddresses(gap, startIndex: addressList.length, isHidden: isHidden, type: type); addAddresses(newAddresses); @@ -581,7 +585,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } Future _generateInitialAddresses( - {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async { + {BitcoinAddressType type = SegwitAddressType.p2wpkh}) async { var countOfReceiveAddresses = 0; var countOfHiddenAddresses = 0; @@ -658,7 +662,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { void _validateAddresses() { _addresses.forEach((element) async { - if (element.type == SegwitAddresType.mweb) { + if (element.type == SegwitAddressType.mweb) { // this would add a ton of startup lag for mweb addresses since we have 1000 of them return; } diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 990719089..3e5f331df 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -87,8 +87,8 @@ class ElectrumWalletSnapshot { final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ?? ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0); - var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; - var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; + var regularAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0}; + var changeAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0}; var silentAddressIndex = 0; final derivationType = DerivationType @@ -97,10 +97,10 @@ class ElectrumWalletSnapshot { try { regularAddressIndexByType = { - SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0') + SegwitAddressType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0') }; changeAddressIndexByType = { - SegwitAddresType.p2wpkh.toString(): + SegwitAddressType.p2wpkh.toString(): int.parse(data['change_address_index'] as String? ?? '0') }; silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0'); diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 08c56c600..4e6b2536a 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -16,7 +16,6 @@ import 'package:fixnum/fixnum.dart'; import 'package:bip39/bip39.dart' as bip39; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; @@ -971,9 +970,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { List? inputPrivKeyInfos, List? vinOutpoints, }) async { - bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb); + bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddressType.mweb); bool paysToMweb = outputs - .any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb); + .any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddressType.mweb); bool isRegular = !spendsMweb && !paysToMweb; bool isMweb = spendsMweb || paysToMweb; @@ -1064,9 +1063,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { tx.isMweb = mwebEnabled; if (!mwebEnabled) { - tx.changeAddressOverride = - (await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb)) - .address; + tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses) + .getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb)) + .address; return tx; } await waitForMwebAddresses(); @@ -1108,7 +1107,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // check if mweb inputs are used: for (final utxo in tx.utxos) { - if (utxo.utxo.scriptType == SegwitAddresType.mweb) { + if (utxo.utxo.scriptType == SegwitAddressType.mweb) { hasMwebInput = true; } } @@ -1119,7 +1118,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { bool isRegular = !hasMwebInput && !hasMwebOutput; bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput; tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses) - .getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any)) + .getChangeAddress( + coinTypeToSpendFrom: + shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any)) .address; if (isRegular) { tx.isMweb = false; diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index bbb987766..5e5a27fe8 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -106,7 +106,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with .map((e) => BitcoinAddressRecord( e.value, index: e.key, - type: SegwitAddresType.mweb, + type: SegwitAddressType.mweb, network: network, )) .toList(); @@ -128,7 +128,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) { - if (addressType == SegwitAddresType.mweb) { + if (addressType == SegwitAddressType.mweb) { return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1]; } return generateP2WPKHAddress(hd: hd, index: index, network: network); @@ -140,7 +140,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType, }) async { - if (addressType == SegwitAddresType.mweb) { + if (addressType == SegwitAddressType.mweb) { await ensureMwebAddressUpToIndexExists(index); } return getAddress(index: index, hd: hd, addressType: addressType); @@ -195,7 +195,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with return BitcoinAddressRecord( mwebAddrs[0], index: 0, - type: SegwitAddresType.mweb, + type: SegwitAddressType.mweb, network: network, ); } @@ -207,7 +207,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with String get addressForExchange { // don't use mweb addresses for exchange refund address: final addresses = receiveAddresses - .where((element) => element.type == SegwitAddresType.p2wpkh && !element.isUsed); + .where((element) => element.type == SegwitAddressType.p2wpkh && !element.isUsed); return addresses.first.address; } } diff --git a/cw_bitcoin/lib/payjoin/manager.dart b/cw_bitcoin/lib/payjoin/manager.dart index 7ba3ceb9b..9476ce23e 100644 --- a/cw_bitcoin/lib/payjoin/manager.dart +++ b/cw_bitcoin/lib/payjoin/manager.dart @@ -31,8 +31,8 @@ class PayjoinManager { 'https://ohttp.cakewallet.com', ]; - static Future randomOhttpRelayUrl() => PayjoinUri.Url.fromStr( - ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]); + static Future randomOhttpRelayUrl() => + PayjoinUri.Url.fromStr(ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]); static const payjoinDirectoryUrl = 'https://payjo.in'; @@ -59,8 +59,7 @@ class PayjoinManager { Future initSender( String pjUriString, String originalPsbt, int networkFeesSatPerVb) async { try { - final pjUri = - (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported(); + final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported(); final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250); final senderBuilder = await SenderBuilder.fromPsbtAndUri( psbtBase64: originalPsbt, @@ -79,8 +78,7 @@ class PayjoinManager { bool isTestnet = false, }) async { final pjUri = Uri.parse(pjUrl).queryParameters['pj']!; - await _payjoinStorage.insertSenderSession( - sender, pjUri, _wallet.id, amount); + await _payjoinStorage.insertSenderSession(sender, pjUri, _wallet.id, amount); return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri); } @@ -140,11 +138,9 @@ class PayjoinManager { return completer.future; } - Future initReceiver(String address, - [bool isTestnet = false]) async { + Future initReceiver(String address, [bool isTestnet = false]) async { try { - final payjoinDirectory = - await PayjoinUri.Url.fromStr(payjoinDirectoryUrl); + final payjoinDirectory = await PayjoinUri.Url.fromStr(payjoinDirectoryUrl); final ohttpKeys = await PayjoinUri.fetchOhttpKeys( ohttpRelay: await randomOhttpRelayUrl(), @@ -199,8 +195,7 @@ class PayjoinManager { _payjoinStorage.markReceiverSessionInProgress(receiver.id()); final inputScript = message['input_script'] as Uint8List; - final isOwned = - _wallet.isMine(Script.fromRaw(byteData: inputScript)); + final isOwned = _wallet.isMine(Script.fromRaw(bytes: inputScript)); mainToIsolateSendPort?.send({ 'requestId': message['requestId'], 'result': isOwned, @@ -209,8 +204,7 @@ class PayjoinManager { case PayjoinReceiverRequestTypes.checkIsReceiverOutput: final outputScript = message['output_script'] as Uint8List; - final isReceiverOutput = - _wallet.isMine(Script.fromRaw(byteData: outputScript)); + final isReceiverOutput = _wallet.isMine(Script.fromRaw(bytes: outputScript)); mainToIsolateSendPort?.send({ 'requestId': message['requestId'], 'result': isReceiverOutput, @@ -243,15 +237,13 @@ class PayjoinManager { } } catch (e) { _cleanupSession(receiver.id()); - await _payjoinStorage.markReceiverSessionUnrecoverable( - receiver.id(), e.toString()); + await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), e.toString()); completer.completeError(e); } } else if (message is PayjoinSessionError) { _cleanupSession(receiver.id()); if (message is UnrecoverableError) { - await _payjoinStorage.markReceiverSessionUnrecoverable( - receiver.id(), message.message); + await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), message.message); completer.complete(); } else if (message is RecoverableError) { completer.complete(); diff --git a/cw_bitcoin/lib/psbt/signer.dart b/cw_bitcoin/lib/psbt/signer.dart index 1d0ceba8b..a56aec37d 100644 --- a/cw_bitcoin/lib/psbt/signer.dart +++ b/cw_bitcoin/lib/psbt/signer.dart @@ -40,8 +40,7 @@ extension PsbtSigner on PsbtV2 { return tx.buffer(); } - Future signWithUTXO( - List utxos, UTXOSignerCallBack signer, + Future signWithUTXO(List utxos, UTXOSignerCallBack signer, [UTXOGetterCallBack? getTaprootPair]) async { final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false)); final tx = BtcTransaction.fromRaw(raw); @@ -51,10 +50,10 @@ extension PsbtSigner on PsbtV2 { List taprootAmounts = []; List