From 1b2e3f2ee1d069c20c57c7cb889cfd0dad8dd7fa Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Thu, 15 May 2025 13:56:43 +0200 Subject: [PATCH 01/63] fix: Payjoin button not showing (#2272) --- .../screens/receive/widgets/qr_widget.dart | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index 5c0b12e38..1f3f4a88e 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -210,24 +210,30 @@ class QRWidget extends StatelessWidget { ), ), ), - if (addressListViewModel.payjoinEndpoint.isNotEmpty) ...[ - Padding( - padding: EdgeInsets.only(top: 12), - child: PrimaryImageButton( - onPressed: () { - Clipboard.setData(ClipboardData( - text: addressListViewModel.payjoinEndpoint)); - showBar(context, S.of(context).copied_to_clipboard); - }, - image: Image.asset('assets/images/payjoin.png', width: 25,), - text: S.of(context).copy_payjoin_url, - color: Theme.of(context).cardColor, - textColor: Theme.of(context) - .extension()! - .buttonTextColor, + Observer( + builder: (_) => Offstage( + offstage: addressListViewModel.payjoinEndpoint.isEmpty, + child: Padding( + padding: EdgeInsets.only(top: 12), + child: PrimaryImageButton( + onPressed: () { + Clipboard.setData( + ClipboardData(text: addressUri.toString())); + showBar(context, S.of(context).copied_to_clipboard); + }, + image: Image.asset( + 'assets/images/payjoin.png', + width: 25, + ), + text: S.of(context).copy_payjoin_url, + color: Theme.of(context).cardColor, + textColor: Theme.of(context) + .extension()! + .buttonTextColor, + ), ), ), - ], + ), ], ), ), From 66efce4d702b8a3f2eb35d03641083547620b3ef Mon Sep 17 00:00:00 2001 From: cyan Date: Thu, 15 May 2025 14:17:56 +0200 Subject: [PATCH 02/63] fix(cw_monero): move isolates to top level functions so WalletBase will not get sent over. (#2269) --- cw_monero/lib/monero_wallet_service.dart | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index c82905d4a..45a0a70d6 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -224,19 +224,7 @@ class MoneroWalletService extends WalletService< final wmaddr = wmPtr.ffiAddress(); final waddr = w.ffiAddress(); openedWalletsByPath.remove("$path/$wallet"); - if (Platform.isWindows) { - await Isolate.run(() { - monero.WalletManager_closeWallet( - Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); - monero.WalletManager_errorString(Pointer.fromAddress(wmaddr)); - }); - } else { - unawaited(Isolate.run(() { - monero.WalletManager_closeWallet( - Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); - monero.WalletManager_errorString(Pointer.fromAddress(wmaddr)); - })); - } + await closeWalletAwaitIfShould(wmaddr, waddr); printV("wallet closed"); } @@ -570,3 +558,19 @@ class MoneroWalletService extends WalletService< false; } } + +Future closeWalletAwaitIfShould(int wmaddr, int waddr) async { + if (Platform.isWindows) { + await Isolate.run(() { + monero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr)); + }); + } else { + unawaited(Isolate.run(() { + monero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr)); + })); + } +} \ No newline at end of file From 557e1c9839807c601626038637b737ab11705106 Mon Sep 17 00:00:00 2001 From: Serhii Date: Thu, 15 May 2025 19:48:06 +0300 Subject: [PATCH 03/63] Cw 935 get exchange payment method recommendations from on ramper (#2235) * fix: default to recommended payment method in Onramper * fix: support displaying unknown payment methods * feat: fetch recommended Onramper payment type --- lib/buy/buy_provider.dart | 1 + lib/buy/buy_quote.dart | 5 +- lib/buy/dfx/dfx_buy_provider.dart | 3 +- lib/buy/kryptonim/kryptonim.dart | 3 +- lib/buy/meld/meld_buy_provider.dart | 1 + lib/buy/moonpay/moonpay_provider.dart | 3 +- lib/buy/onramper/onramper_buy_provider.dart | 56 ++++++++++++++++--- lib/buy/payment_method.dart | 19 +++++-- lib/buy/robinhood/robinhood_buy_provider.dart | 3 +- lib/view_model/buy/buy_sell_view_model.dart | 16 +++++- 10 files changed, 89 insertions(+), 21 deletions(-) diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart index 55ef5e6c3..f57cc5472 100644 --- a/lib/buy/buy_provider.dart +++ b/lib/buy/buy_provider.dart @@ -64,6 +64,7 @@ abstract class BuyProvider { required bool isBuyAction, required String walletAddress, PaymentType? paymentType, + String? customPaymentMethodType, String? countryCode}) async => null; } diff --git a/lib/buy/buy_quote.dart b/lib/buy/buy_quote.dart index 1805b7e1a..da854faaf 100644 --- a/lib/buy/buy_quote.dart +++ b/lib/buy/buy_quote.dart @@ -50,6 +50,7 @@ class Quote extends SelectableOption { this.rampName, this.rampIconPath, this.limits, + this.customPaymentMethodType, }) : super(title: provider.isAggregator ? rampName ?? '' : provider.title); final double rate; @@ -68,6 +69,7 @@ class Quote extends SelectableOption { bool _isBestRate = false; bool isBuyAction; Limits? limits; + String? customPaymentMethodType; late FiatCurrency _fiatCurrency; late CryptoCurrency _cryptoCurrency; @@ -130,7 +132,7 @@ class Quote extends SelectableOption { set setLimits(Limits limits) => this.limits = limits; factory Quote.fromOnramperJson(Map json, bool isBuyAction, - Map metaData, PaymentType paymentType) { + Map metaData, PaymentType paymentType, String? customPaymentMethodType) { final rate = _toDouble(json['rate']) ?? 0.0; final networkFee = _toDouble(json['networkFee']) ?? 0.0; final transactionFee = _toDouble(json['transactionFee']) ?? 0.0; @@ -183,6 +185,7 @@ class Quote extends SelectableOption { rampName: rampName, rampIconPath: rampIconPath, paymentType: paymentType, + customPaymentMethodType: customPaymentMethodType, quoteId: json['quoteId'] as String? ?? '', recommendations: enumRecommendations, provider: ProvidersHelper.getProviderByType(ProviderType.onramper)!, diff --git a/lib/buy/dfx/dfx_buy_provider.dart b/lib/buy/dfx/dfx_buy_provider.dart index 267406893..150f731dc 100644 --- a/lib/buy/dfx/dfx_buy_provider.dart +++ b/lib/buy/dfx/dfx_buy_provider.dart @@ -231,6 +231,7 @@ class DFXBuyProvider extends BuyProvider { required bool isBuyAction, required String walletAddress, PaymentType? paymentType, + String? customPaymentMethodType, String? countryCode}) async { /// if buying with any currency other than eur or chf then DFX is not supported @@ -373,7 +374,7 @@ class DFXBuyProvider extends BuyProvider { case 'Instant': return PaymentType.sepa; default: - return PaymentType.all; + return PaymentType.unknown; } } diff --git a/lib/buy/kryptonim/kryptonim.dart b/lib/buy/kryptonim/kryptonim.dart index 6e00a7e07..5e58ce190 100644 --- a/lib/buy/kryptonim/kryptonim.dart +++ b/lib/buy/kryptonim/kryptonim.dart @@ -113,6 +113,7 @@ class KryptonimBuyProvider extends BuyProvider { required bool isBuyAction, required String walletAddress, PaymentType? paymentType, + String? customPaymentMethodType, String? countryCode, }) async { log('Kryptonim: Fetching quote: ${isBuyAction ? cryptoCurrency : fiatCurrency} -> ${isBuyAction ? fiatCurrency : cryptoCurrency}, amount: $amount'); @@ -149,7 +150,7 @@ class KryptonimBuyProvider extends BuyProvider { final selectedPaymentType = PaymentMethod.getPaymentTypeId(selectedPaymentMethod['payment_method'] as String?); - final quote = Quote.fromKryptonimJson(selectedPaymentMethod, isBuyAction, selectedPaymentType); + final quote = Quote.fromKryptonimJson(selectedPaymentMethod, isBuyAction, selectedPaymentType ?? PaymentType.unknown); quote.setFiatCurrency = fiatCurrency; quote.setCryptoCurrency = cryptoCurrency; diff --git a/lib/buy/meld/meld_buy_provider.dart b/lib/buy/meld/meld_buy_provider.dart index e96a3575c..db593d358 100644 --- a/lib/buy/meld/meld_buy_provider.dart +++ b/lib/buy/meld/meld_buy_provider.dart @@ -104,6 +104,7 @@ class MeldBuyProvider extends BuyProvider { required bool isBuyAction, required String walletAddress, PaymentType? paymentType, + String? customPaymentMethodType, String? countryCode}) async { String? paymentMethod; if (paymentType != null && paymentType != PaymentType.all) { diff --git a/lib/buy/moonpay/moonpay_provider.dart b/lib/buy/moonpay/moonpay_provider.dart index 6c568886f..1cfef7202 100644 --- a/lib/buy/moonpay/moonpay_provider.dart +++ b/lib/buy/moonpay/moonpay_provider.dart @@ -162,6 +162,7 @@ class MoonPayProvider extends BuyProvider { required bool isBuyAction, required String walletAddress, PaymentType? paymentType, + String? customPaymentMethodType, String? countryCode}) async { String? paymentMethod; @@ -410,7 +411,7 @@ class MoonPayProvider extends BuyProvider { case 'yellow_card_bank_transfer': return PaymentType.yellowCardBankTransfer; default: - return PaymentType.all; + return PaymentType.unknown; } } } diff --git a/lib/buy/onramper/onramper_buy_provider.dart b/lib/buy/onramper/onramper_buy_provider.dart index dc9812d1d..35683f01d 100644 --- a/lib/buy/onramper/onramper_buy_provider.dart +++ b/lib/buy/onramper/onramper_buy_provider.dart @@ -33,6 +33,7 @@ class OnRamperBuyProvider extends BuyProvider { static const quotes = '/quotes'; static const paymentTypes = '/payment-types'; static const supported = '/supported'; + static const defaultsAll = '/defaults/all'; static const List _notSupportedCrypto = []; static const List _notSupportedFiat = []; @@ -40,6 +41,8 @@ class OnRamperBuyProvider extends BuyProvider { final SettingsStore _settingsStore; + String? recommendedPaymentType; + String get _apiKey => secrets.onramperApiKey; @override @@ -57,6 +60,34 @@ class OnRamperBuyProvider extends BuyProvider { @override bool get isAggregator => true; + Future getRecommendedPaymentType(bool isBuyAction) async { + + final params = {'type': isBuyAction ? 'buy' : 'sell'}; + + final url = Uri.https(_baseApiUrl, '$supported$defaultsAll', params); + + try { + final response = + await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'}); + + if (response.statusCode == 200) { + final Map data = jsonDecode(response.body) as Map; + final recommended = data['message']['recommended'] as Map; + + final recommendedPaymentType = recommended['paymentMethod'] as String?; + + return recommendedPaymentType ; + } else { + final responseBody = + jsonDecode(response.body) as Map; + printV('Failed to fetch available payment types: ${responseBody['message']}'); + } + } catch (e) { + printV('Failed to fetch available payment types: $e'); + } + return null; + } + Future> getAvailablePaymentTypes( String fiatCurrency, CryptoCurrency cryptoCurrency, bool isBuyAction) async { @@ -77,9 +108,14 @@ class OnRamperBuyProvider extends BuyProvider { if (response.statusCode == 200) { final Map data = jsonDecode(response.body) as Map; final List message = data['message'] as List; - return message + + final allAvailablePaymentMethods = message .map((item) => PaymentMethod.fromOnramperJson(item as Map)) .toList(); + + recommendedPaymentType = await getRecommendedPaymentType(isBuyAction); + + return allAvailablePaymentMethods; } else { final responseBody = jsonDecode(response.body) as Map; @@ -131,13 +167,13 @@ class OnRamperBuyProvider extends BuyProvider { required bool isBuyAction, required String walletAddress, PaymentType? paymentType, + String? customPaymentMethodType, String? countryCode}) async { String? paymentMethod; - if (paymentType != null && paymentType != PaymentType.all) { - paymentMethod = normalizePaymentMethod(paymentType); - if (paymentMethod == null) paymentMethod = paymentType.name; - } + if (paymentType == PaymentType.all && recommendedPaymentType != null) paymentMethod = recommendedPaymentType!; + else if (paymentType == PaymentType.unknown) paymentMethod = customPaymentMethodType; + else if (paymentType != null) paymentMethod = normalizePaymentMethod(paymentType); final actionType = isBuyAction ? 'buy' : 'sell'; @@ -182,7 +218,7 @@ class OnRamperBuyProvider extends BuyProvider { if (rampMetaData == null) continue; final quote = Quote.fromOnramperJson( - item, isBuyAction, _onrampMetadata, _getPaymentTypeByString(paymentMethod)); + item, isBuyAction, _onrampMetadata, _getPaymentTypeByString(paymentMethod), customPaymentMethodType); quote.setFiatCurrency = fiatCurrency; quote.setCryptoCurrency = cryptoCurrency; validQuotes.add(quote); @@ -225,7 +261,7 @@ class OnRamperBuyProvider extends BuyProvider { final defaultCrypto = quote.cryptoCurrency.title + _getNormalizeNetwork(quote.cryptoCurrency).toLowerCase(); - final paymentMethod = normalizePaymentMethod(quote.paymentType); + final paymentMethod = quote.paymentType == PaymentType.unknown ? quote.customPaymentMethodType : normalizePaymentMethod(quote.paymentType); final uri = Uri.https(_baseUrl, '', { 'apiKey': _apiKey, @@ -330,6 +366,8 @@ class OnRamperBuyProvider extends BuyProvider { return 'dana'; case PaymentType.ideal: return 'ideal'; + case PaymentType.pixPay: + return 'pix'; default: return null; } @@ -379,8 +417,10 @@ class OnRamperBuyProvider extends BuyProvider { return PaymentType.dana; case 'ideal': return PaymentType.ideal; + case 'pix': + return PaymentType.pixPay; default: - return PaymentType.all; + return PaymentType.unknown; } } diff --git a/lib/buy/payment_method.dart b/lib/buy/payment_method.dart index 14b119aa0..322ca68ba 100644 --- a/lib/buy/payment_method.dart +++ b/lib/buy/payment_method.dart @@ -34,6 +34,8 @@ enum PaymentType { yellowCardBankTransfer, fiatBalance, bancontact, + pixPay, + unknown, } extension PaymentTypeTitle on PaymentType { @@ -101,6 +103,8 @@ extension PaymentTypeTitle on PaymentType { return 'Fiat Balance'; case PaymentType.bancontact: return 'Bancontact'; + case PaymentType.pixPay: + return 'PIX Pay'; default: return null; } @@ -158,12 +162,14 @@ class PaymentMethod extends SelectableOption { required this.customTitle, required this.customIconPath, this.customDescription, + this.customPaymentMethodType, }) : super(title: paymentMethodType.title ?? customTitle); final PaymentType paymentMethodType; final String customTitle; final String customIconPath; final String? customDescription; + final String? customPaymentMethodType; bool isSelected = false; @override @@ -188,7 +194,8 @@ class PaymentMethod extends SelectableOption { factory PaymentMethod.fromOnramperJson(Map json) { final type = PaymentMethod.getPaymentTypeId(json['paymentTypeId'] as String?); return PaymentMethod( - paymentMethodType: type, + paymentMethodType: type ?? PaymentType.unknown, + customPaymentMethodType: json['paymentTypeId'] as String?, customTitle: json['name'] as String? ?? 'Unknown', customIconPath: json['icon'] as String? ?? 'assets/images/card.png', customDescription: json['description'] as String?); @@ -212,7 +219,7 @@ class PaymentMethod extends SelectableOption { final type = PaymentMethod.getPaymentTypeId(json['paymentMethod'] as String?); final logos = json['logos'] as Map; return PaymentMethod( - paymentMethodType: type, + paymentMethodType: type ?? PaymentType.unknown, customTitle: json['name'] as String? ?? 'Unknown', customIconPath: logos['dark'] as String? ?? 'assets/images/card.png', customDescription: json['description'] as String?); @@ -221,13 +228,13 @@ class PaymentMethod extends SelectableOption { factory PaymentMethod.fromKryptonimJson(Map json) { final type = PaymentMethod.getPaymentTypeId(json['payment_method'] as String?); return PaymentMethod( - paymentMethodType: type, + paymentMethodType: type ?? PaymentType.unknown, customTitle: json['payment_method'] as String? ?? 'Unknown', customIconPath: 'assets/images/card.png', ); } - static PaymentType getPaymentTypeId(String? type) { + static PaymentType? getPaymentTypeId(String? type) { switch (type?.toLowerCase()) { case 'banktransfer': case 'bank': @@ -289,8 +296,10 @@ class PaymentMethod extends SelectableOption { return PaymentType.sepaOpenBankingPayment; case 'bancontact': return PaymentType.bancontact; + case 'pix': + return PaymentType.pixPay; default: - return PaymentType.all; + return null; } } } diff --git a/lib/buy/robinhood/robinhood_buy_provider.dart b/lib/buy/robinhood/robinhood_buy_provider.dart index 93efd5642..4945529ef 100644 --- a/lib/buy/robinhood/robinhood_buy_provider.dart +++ b/lib/buy/robinhood/robinhood_buy_provider.dart @@ -192,6 +192,7 @@ class RobinhoodBuyProvider extends BuyProvider { required bool isBuyAction, required String walletAddress, PaymentType? paymentType, + String? customPaymentMethodType, String? countryCode}) async { String? paymentMethod; @@ -267,7 +268,7 @@ class RobinhoodBuyProvider extends BuyProvider { case 'bank_transfer': return PaymentType.bankTransfer; default: - return PaymentType.all; + return PaymentType.unknown; } } } diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index df4b838f2..db75dc236 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -359,14 +359,23 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S onTimeout: () => [], ))); - final Map uniquePaymentMethods = {}; + final List tempPaymentMethods = []; + for (var methods in result) { for (var method in methods) { - uniquePaymentMethods[method.paymentMethodType] = method; + final alreadyExists = tempPaymentMethods.any((m) { + return m.paymentMethodType == method.paymentMethodType && + m.customTitle == method.customTitle; + }); + + if (!alreadyExists) { + tempPaymentMethods.add(method); + } } } - paymentMethods = ObservableList.of(uniquePaymentMethods.values); + paymentMethods = ObservableList.of(tempPaymentMethods); + if (paymentMethods.isNotEmpty) { paymentMethods.insert(0, PaymentMethod.all()); selectedPaymentMethod = paymentMethods.first; @@ -404,6 +413,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S paymentType: selectedPaymentMethod?.paymentMethodType, isBuyAction: isBuyAction, walletAddress: wallet.walletAddresses.address, + customPaymentMethodType: selectedPaymentMethod?.customPaymentMethodType, ) .timeout( Duration(seconds: 10), From bc52cf485e52733f95bad6b187820a5bbcf4a825 Mon Sep 17 00:00:00 2001 From: cyan Date: Thu, 15 May 2025 18:48:43 +0200 Subject: [PATCH 04/63] fix(cw_monero): transaction not appearing after send (#2268) * fix: catch error in newly added fetchTransactions call fix: null handling in cw_monero * fix(cw_monero): transaction not appearing after send --- cw_monero/lib/monero_wallet.dart | 2 +- cw_monero/lib/pending_monero_transaction.dart | 7 ++++++- lib/view_model/send/send_view_model.dart | 7 ------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 7a4d943fe..c3433a4c9 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -446,7 +446,7 @@ abstract class MoneroWalletBase extends WalletBase pendingTransactionDescription.hash; @@ -54,6 +56,7 @@ class PendingMoneroTransaction with PendingTransaction { rethrow; } + await wallet.fetchTransactions(); storeSync(force: true); } @@ -63,6 +66,8 @@ class PendingMoneroTransaction with PendingTransaction { final ret = monero_transaction_history.commitTransactionFromPointerAddress( address: pendingTransactionDescription.pointerAddress, useUR: true); + await wallet.fetchTransactions(); + storeSync(force: true); return ret; } catch (e) { final message = e.toString(); diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index a675042e3..94b3618ce 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -593,13 +593,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor } final sharedPreferences = await SharedPreferences.getInstance(); await sharedPreferences.setString(PreferencesKey.backgroundSyncLastTrigger(wallet.name), DateTime.now().add(Duration(minutes: 1)).toIso8601String()); - unawaited(() { - try { - wallet.fetchTransactions(); - } catch (e) { - printV(e); - } - }()); state = TransactionCommitted(); } catch (e) { state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency)); From c12daced403660399188e64ebeb9dae306a18c5d Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Thu, 15 May 2025 21:57:02 +0200 Subject: [PATCH 05/63] feat: Load default ERC20 Tokens for existing ETH and Polygon Wallets (#2274) --- cw_core/lib/crypto_currency.dart | 2 +- cw_ethereum/lib/default_ethereum_erc20_tokens.dart | 2 +- cw_ethereum/lib/ethereum_wallet.dart | 9 ++++++--- cw_ethereum/lib/ethereum_wallet_service.dart | 1 + cw_evm/lib/evm_chain_wallet.dart | 2 +- cw_polygon/lib/default_polygon_erc20_tokens.dart | 7 +++++++ cw_polygon/lib/polygon_wallet.dart | 9 ++++++--- cw_polygon/lib/polygon_wallet_service.dart | 1 + 8 files changed, 24 insertions(+), 9 deletions(-) diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index cb8485ec5..4b0f9521e 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -232,7 +232,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const ton = CryptoCurrency(title: 'TON', fullName: 'Toncoin', raw: 95, name: 'ton', iconPath: 'assets/images/ton_icon.png', decimals: 8); static const zano = CryptoCurrency(title: 'ZANO', tag: 'ZANO', fullName: 'Zano', raw: 96, name: 'zano', iconPath: 'assets/images/zano_icon.png', decimals: 12); static const flip = CryptoCurrency(title: 'FLIP', tag: 'ETH', fullName: 'Chainflip', raw: 97, name: 'flip', iconPath: 'assets/images/flip_icon.png', decimals: 18); - static const deuro = CryptoCurrency(title: 'DEURO', tag: 'ETH', fullName: 'Digital Euro', raw: 98, name: 'deuro', iconPath: 'assets/images/deuro_icon.png', decimals: 18); + static const deuro = CryptoCurrency(title: 'DEURO', tag: 'ETH', fullName: 'Decentralized Euro', raw: 98, name: 'deuro', iconPath: 'assets/images/deuro_icon.png', decimals: 18); static final Map _rawCurrencyMap = [...all, ...havenCurrencies].fold>({}, (acc, item) { diff --git a/cw_ethereum/lib/default_ethereum_erc20_tokens.dart b/cw_ethereum/lib/default_ethereum_erc20_tokens.dart index 8381744d6..630424967 100644 --- a/cw_ethereum/lib/default_ethereum_erc20_tokens.dart +++ b/cw_ethereum/lib/default_ethereum_erc20_tokens.dart @@ -18,7 +18,7 @@ class DefaultEthereumErc20Tokens { enabled: true, ), Erc20Token( - name: "Digital Euro", + name: "Decentralized Euro", symbol: "DEURO", contractAddress: "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea", decimal: 18, diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index 7cc140c5a..c22be3cb2 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -31,11 +31,14 @@ class EthereumWallet extends EVMChainWallet { }) : super(nativeCurrency: CryptoCurrency.eth); @override - void addInitialTokens() { + void addInitialTokens([bool isMigration = false]) { final initialErc20Tokens = DefaultEthereumErc20Tokens().initialErc20Tokens; - for (var token in initialErc20Tokens) { - evmChainErc20TokensBox.put(token.contractAddress, token); + for (final token in initialErc20Tokens) { + if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) { + if (isMigration) token.enabled = false; + evmChainErc20TokensBox.put(token.contractAddress, token); + } } } diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart index 858416055..06ba07bfc 100644 --- a/cw_ethereum/lib/ethereum_wallet_service.dart +++ b/cw_ethereum/lib/ethereum_wallet_service.dart @@ -53,6 +53,7 @@ class EthereumWalletService extends EVMChainWalletService { ); await wallet.init(); + wallet.addInitialTokens(true); await wallet.save(); saveBackup(name); return wallet; diff --git a/cw_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart index d640f8c14..a1b253dd8 100644 --- a/cw_evm/lib/evm_chain_wallet.dart +++ b/cw_evm/lib/evm_chain_wallet.dart @@ -136,7 +136,7 @@ abstract class EVMChainWalletBase //! Methods to be overridden by every child - void addInitialTokens(); + void addInitialTokens([bool isMigration]); // Future open({ // required String name, diff --git a/cw_polygon/lib/default_polygon_erc20_tokens.dart b/cw_polygon/lib/default_polygon_erc20_tokens.dart index deff285c0..2c739e8c5 100644 --- a/cw_polygon/lib/default_polygon_erc20_tokens.dart +++ b/cw_polygon/lib/default_polygon_erc20_tokens.dart @@ -31,6 +31,13 @@ class DefaultPolygonErc20Tokens { decimal: 6, enabled: true, ), + Erc20Token( + name: "Decentralized Euro", + symbol: "DEURO", + contractAddress: "0xC2ff25dD99e467d2589b2c26EDd270F220F14E47", + decimal: 18, + enabled: true, + ), Erc20Token( name: "Avalanche Token", symbol: "AVAX", diff --git a/cw_polygon/lib/polygon_wallet.dart b/cw_polygon/lib/polygon_wallet.dart index b2bf064b1..c1d36b9cb 100644 --- a/cw_polygon/lib/polygon_wallet.dart +++ b/cw_polygon/lib/polygon_wallet.dart @@ -41,11 +41,14 @@ class PolygonWallet extends EVMChainWallet { } @override - void addInitialTokens() { + void addInitialTokens([bool isMigration = false]) { final initialErc20Tokens = DefaultPolygonErc20Tokens().initialPolygonErc20Tokens; - for (var token in initialErc20Tokens) { - evmChainErc20TokensBox.put(token.contractAddress, token); + for (final token in initialErc20Tokens) { + if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) { + if (isMigration) token.enabled = false; + evmChainErc20TokensBox.put(token.contractAddress, token); + } } } diff --git a/cw_polygon/lib/polygon_wallet_service.dart b/cw_polygon/lib/polygon_wallet_service.dart index 994912e8d..05af311e7 100644 --- a/cw_polygon/lib/polygon_wallet_service.dart +++ b/cw_polygon/lib/polygon_wallet_service.dart @@ -55,6 +55,7 @@ class PolygonWalletService extends EVMChainWalletService { ); await wallet.init(); + wallet.addInitialTokens(true); await wallet.save(); saveBackup(name); return wallet; From 914561716d048ccbdd0f29f0e09e8cabed9d6eaa Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 16 May 2025 13:51:36 +0300 Subject: [PATCH 06/63] v4.28.0 Release Candidate (#2260) * v4.28.0 Release Candidate * Fix Android deeplinking * minor [skip ci] * update app versions [skip ci] * merge main * - Re-enable SwapTrade - Hide Keyboard on opening fee selection * handle old backups import * - Fix seed type UI - Temp fix for Deleting Monero wallet * update build number [skip ci] * minor nullability handling * disable payjoin for SP --- android/app/src/main/AndroidManifestBase.xml | 2 + assets/text/Monerocom_Release_Notes.txt | 5 +- assets/text/Release_Notes.txt | 7 +-- cw_bitcoin/lib/address_from_output.dart | 1 + cw_bitcoin/pubspec.lock | 2 +- cw_core/pubspec.lock | 2 +- cw_monero/lib/api/account_list.dart | 4 +- cw_monero/lib/monero_wallet.dart | 16 +---- cw_monero/pubspec.lock | 2 +- cw_wownero/pubspec.lock | 2 +- ios/Podfile.lock | 61 +++++++++++-------- ios/Runner/InfoBase.plist | 2 + lib/core/backup_service.dart | 33 +++++----- lib/entities/seed_type.dart | 11 ++-- .../provider/swaptrade_exchange_provider.dart | 6 +- .../advanced_privacy_settings_page.dart | 1 + .../screens/receive/widgets/qr_widget.dart | 29 ++++----- lib/src/screens/send/widgets/send_card.dart | 2 + lib/utils/exception_handler.dart | 2 +- .../wallet_address_list_view_model.dart | 2 +- scripts/android/app_env.sh | 8 +-- scripts/ios/app_env.sh | 8 +-- scripts/linux/app_env.sh | 4 +- scripts/macos/app_env.sh | 8 +-- scripts/windows/build_exe_installer.iss | 2 +- 25 files changed, 113 insertions(+), 109 deletions(-) diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml index 280a45b3c..8283a7c8c 100644 --- a/android/app/src/main/AndroidManifestBase.xml +++ b/android/app/src/main/AndroidManifestBase.xml @@ -111,6 +111,8 @@ + + =3.5.0 <4.0.0" - flutter: ">=3.27.4" + flutter: ">=3.24.0" diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index 265eaa9bc..36bba72b6 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -810,4 +810,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.5.0 <4.0.0" - flutter: ">=3.27.4" + flutter: ">=3.24.0" diff --git a/cw_monero/lib/api/account_list.dart b/cw_monero/lib/api/account_list.dart index 7f6a3f1aa..b753b4656 100644 --- a/cw_monero/lib/api/account_list.dart +++ b/cw_monero/lib/api/account_list.dart @@ -25,8 +25,8 @@ bool isUpdating = false; void refreshAccounts() { try { isUpdating = true; - subaddressAccount = currentWallet!.subaddressAccount(); - subaddressAccount!.refresh(); + subaddressAccount = currentWallet?.subaddressAccount(); + subaddressAccount?.refresh(); isUpdating = false; } catch (e) { isUpdating = false; diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index c3433a4c9..1e6d3f6a3 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -34,12 +34,12 @@ import 'package:cw_monero/monero_transaction_history.dart'; import 'package:cw_monero/monero_transaction_info.dart'; import 'package:cw_monero/monero_unspent.dart'; import 'package:cw_monero/monero_wallet_addresses.dart'; +import 'package:cw_monero/monero_wallet_service.dart'; import 'package:cw_monero/pending_monero_transaction.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus.dart'; import 'package:mobx/mobx.dart'; -import 'package:monero/src/monero.dart' as m; import 'package:monero/monero.dart' as monero; part 'monero_wallet.g.dart'; @@ -193,19 +193,7 @@ abstract class MoneroWalletBase extends WalletBase=3.6.0 <4.0.0" - flutter: ">=3.27.4" + flutter: ">=3.24.0" diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock index c2d5eeea7..66fd1fc56 100644 --- a/cw_wownero/pubspec.lock +++ b/cw_wownero/pubspec.lock @@ -845,4 +845,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.5.0 <4.0.0" - flutter: ">=3.27.4" + flutter: ">=3.24.0" diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3d39d589e..575b43d5f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -76,6 +76,7 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - payjoin_flutter (0.20.0) - permission_handler_apple (9.3.0): - Flutter - reown_yttrium (0.0.1): @@ -126,6 +127,7 @@ DEPENDENCIES: - integration_test (from `.symlinks/plugins/integration_test/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - payjoin_flutter (from `.symlinks/plugins/payjoin_flutter/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - reown_yttrium (from `.symlinks/plugins/reown_yttrium/ios`) - sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`) @@ -186,6 +188,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + payjoin_flutter: + :path: ".symlinks/plugins/payjoin_flutter/ios" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" reown_yttrium: @@ -208,40 +212,43 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: - connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf + connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90 - cw_decred: a02cf30175a46971c1e2fa22c48407534541edc6 - cw_mweb: 3aea2fb35b2bd04d8b2d21b83216f3b8fb768d85 - device_display_brightness: 04374ebd653619292c1d996f00f42877ea19f17f - device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89 - devicelocale: bd64aa714485a8afdaded0892c1e7d5b7f680cf8 + cw_decred: 9c0e1df74745b51a1289ec5e91fb9e24b68fa14a + cw_mweb: 22cd01dfb8ad2d39b15332006f22046aaa8352a3 + device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7 + device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 + devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926 DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 - fast_scanner: 2cb1ad3e69e645e9980fb4961396ce5804caa3e3 - file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517 + fast_scanner: 44c00940355a51258cd6c2085734193cd23d95bc + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99 - flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f - flutter_mailer: 3a8cd4f36c960fb04528d5471097270c19fec1c4 - flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418 - fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 - in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457 - integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e + flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 + flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb + flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f + flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be + fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f + in_app_review: a31b5257259646ea78e0e35fc914979b0031d011 + integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 - package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d - ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda + package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + payjoin_flutter: 6397d7b698cdad6453be4949ab6aca1863f6c5e5 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + reown_yttrium: c0e87e5965fa60a3559564cc35cffbba22976089 SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8 - sensitive_clipboard: 161e9abc3d56b3131309d8a321eb4690a803c16b - share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 - sp_scanner: b1bc9321690980bdb44bba7ec85d5543e716d1b5 + sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986 + share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 - uni_links: ed8c961e47ed9ce42b6d91e1de8049e38a4b3152 - universal_ble: ff19787898040d721109c6324472e5dd4bc86adc - url_launcher_ios: 694010445543906933d732453a59da0a173ae33d - wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 + uni_links: d97da20c7701486ba192624d99bffaaffcfc298a + universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e + YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966 PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist index f27ef8d4f..40868aa9f 100644 --- a/ios/Runner/InfoBase.plist +++ b/ios/Runner/InfoBase.plist @@ -327,5 +327,7 @@ UIViewControllerBasedStatusBarAppearance + FlutterDeepLinkingEnabled + diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index cb36072fe..c7d10598b 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -83,7 +83,7 @@ class $BackupService { outer: for (var file in zip.files) { final filename = file.name; - for (var ignore in ignoreFiles) { + for (var ignore in ignoreFiles) { if (filename.endsWith(ignore) && !filename.contains("wallets/")) { printV("ignoring backup file: $filename"); continue outer; @@ -145,7 +145,7 @@ class $BackupService { MapEntry(key, TransactionDescription.fromJson(value as Map))); var box = transactionDescriptionBox; if (!box.isOpen) { - final transactionDescriptionsBoxKey = + final transactionDescriptionsBoxKey = await getEncryptionKey(secureStorage: _secureStorage, forKey: TransactionDescription.boxKey); box = await CakeHive.openBox( TransactionDescription.boxName, @@ -251,19 +251,22 @@ class $BackupService { await importWalletKeychainInfo(info); }); - for (var key in (keychainJSON['_all'] as Map).keys) { - try { - if (!key.startsWith('MONERO_WALLET_')) continue; - final decodedPassword = decodeWalletPassword(password: keychainJSON['_all'][key].toString()); - final walletName = key.split('_WALLET_')[1]; - final walletType = key.split('_WALLET_')[0].toLowerCase(); - await importWalletKeychainInfo({ - 'name': walletName, - 'type': "WalletType.$walletType", - 'password': decodedPassword, - }); - } catch (e) { - printV('Error importing wallet ($key) password: $e'); + if (keychainJSON['_all'] is Map) { + for (var key in (keychainJSON['_all'] as Map).keys) { + try { + if (!key.startsWith('MONERO_WALLET_')) continue; + final decodedPassword = decodeWalletPassword( + password: keychainJSON['_all'][key].toString()); + final walletName = key.split('_WALLET_')[1]; + final walletType = key.split('_WALLET_')[0].toLowerCase(); + await importWalletKeychainInfo({ + 'name': walletName, + 'type': "WalletType.$walletType", + 'password': decodedPassword, + }); + } catch (e) { + printV('Error importing wallet ($key) password: $e'); + } } } diff --git a/lib/entities/seed_type.dart b/lib/entities/seed_type.dart index 0c5632875..22f0be045 100644 --- a/lib/entities/seed_type.dart +++ b/lib/entities/seed_type.dart @@ -2,16 +2,19 @@ import 'package:cw_core/enumerable_item.dart'; import 'package:cw_core/wallet_info.dart'; class MoneroSeedType extends EnumerableItem with Serializable { - const MoneroSeedType({required String title, required int raw}) : super(title: title, raw: raw); + const MoneroSeedType({required String title, required int raw, this.shortTitle}) + : super(title: title, raw: raw); + + final String? shortTitle; static const all = [legacy, polyseed, bip39]; static const defaultSeedType = polyseed; - static const legacy = MoneroSeedType(raw: 0, title: 'Legacy (25 words)'); - static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed (16 words)'); + static const legacy = MoneroSeedType(raw: 0, title: 'Legacy (25 words)', shortTitle: "Legacy"); + static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed (16 words)', shortTitle: "Polyseed"); static const wowneroSeed = MoneroSeedType(raw: 2, title: 'Wownero'); - static const bip39 = MoneroSeedType(raw: 3, title: 'BIP39 (12 words)'); + static const bip39 = MoneroSeedType(raw: 3, title: 'BIP39 (12 words)', shortTitle: "BIP39"); static MoneroSeedType deserialize({required int raw}) { switch (raw) { diff --git a/lib/exchange/provider/swaptrade_exchange_provider.dart b/lib/exchange/provider/swaptrade_exchange_provider.dart index b86482335..d3f64b712 100644 --- a/lib/exchange/provider/swaptrade_exchange_provider.dart +++ b/lib/exchange/provider/swaptrade_exchange_provider.dart @@ -47,10 +47,10 @@ class SwapTradeExchangeProvider extends ExchangeProvider { String get title => 'SwapTrade'; @override - bool get isAvailable => false; + bool get isAvailable => true; @override - bool get isEnabled => false; + bool get isEnabled => true; @override bool get supportsFixedRate => false; @@ -59,7 +59,7 @@ class SwapTradeExchangeProvider extends ExchangeProvider { ExchangeProviderDescription get description => ExchangeProviderDescription.swapTrade; @override - Future checkIsAvailable() async => false; + Future checkIsAvailable() async => true; @override Future fetchLimits({ diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 1511ffd83..b0501e169 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -154,6 +154,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo items: MoneroSeedType.all, selectedItem: widget.seedTypeViewModel.moneroSeedType, onItemSelected: widget.seedTypeViewModel.setMoneroSeedType, + displayItem: (seedType) => seedType.shortTitle ?? seedType.toString(), ), ); }), diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index 1f3f4a88e..69b2fa445 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -97,8 +97,7 @@ 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( @@ -112,11 +111,10 @@ class QRWidget extends StatelessWidget { ), ), ), - if (addressListViewModel - .payjoinEndpoint.isNotEmpty) ...[ + if (addressListViewModel.payjoinEndpoint.isNotEmpty && + !addressListViewModel.isSilentPayments) ...[ Row( - mainAxisAlignment: - MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: EdgeInsets.only( @@ -196,10 +194,12 @@ class QRWidget extends StatelessWidget { walletType: addressListViewModel.type, textAlign: TextAlign.center, evenTextStyle: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w500, - color: - Theme.of(context).extension()!.textColor))), + fontSize: 15, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.textColor, + ), + ), + ), Padding( padding: EdgeInsets.only(left: 12), child: copyImage, @@ -212,13 +212,12 @@ class QRWidget extends StatelessWidget { ), Observer( builder: (_) => Offstage( - offstage: addressListViewModel.payjoinEndpoint.isEmpty, + 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( @@ -227,9 +226,7 @@ class QRWidget extends StatelessWidget { ), text: S.of(context).copy_payjoin_url, color: Theme.of(context).cardColor, - textColor: Theme.of(context) - .extension()! - .buttonTextColor, + textColor: Theme.of(context).extension()!.buttonTextColor, ), ), ), diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index 655686b8e..6952df24d 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -600,6 +600,8 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin( context: context, builder: (BuildContext context) { diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index e5b1c3579..bba9d71ac 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -225,7 +225,7 @@ class ExceptionHandler { // just ignoring until we find a solution to this issue or migrate from flutter secure storage "core/auth_service.dart:64", "core/key_service.dart:14", - "core/wallet_loading_service.dart:134", + "core/wallet_loading_service.dart:139", ]; static Future _addDeviceInfo(File file) async { 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 09ea6b5a1..2052a79a4 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 @@ -82,7 +82,7 @@ class BitcoinURI extends PaymentURI { final qp = {}; if (amount.isNotEmpty) qp['amount'] = amount.replaceAll(',', '.'); - if (pjUri.isNotEmpty) { + if (pjUri.isNotEmpty && !address.startsWith("sp")) { qp['pjos'] = '0'; qp['pj'] = pjUri; } diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 817b6aeda..34192e9ed 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -14,15 +14,15 @@ TYPES=($MONERO_COM $CAKEWALLET) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="4.27.0" -MONERO_COM_BUILD_NUMBER=121 +MONERO_COM_VERSION="4.28.0" +MONERO_COM_BUILD_NUMBER=123 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.27.0" -CAKEWALLET_BUILD_NUMBER=259 +CAKEWALLET_VERSION="4.28.0" +CAKEWALLET_BUILD_NUMBER=261 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index a6d01f16e..6535931c8 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -12,13 +12,13 @@ TYPES=($MONERO_COM $CAKEWALLET) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="4.27.1" -MONERO_COM_BUILD_NUMBER=120 +MONERO_COM_VERSION="4.28.0" +MONERO_COM_BUILD_NUMBER=122 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.27.1" -CAKEWALLET_BUILD_NUMBER=314 +CAKEWALLET_VERSION="4.28.0" +CAKEWALLET_BUILD_NUMBER=318 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" diff --git a/scripts/linux/app_env.sh b/scripts/linux/app_env.sh index 9e3676b22..cc4d1ac4a 100755 --- a/scripts/linux/app_env.sh +++ b/scripts/linux/app_env.sh @@ -14,8 +14,8 @@ if [ -n "$1" ]; then fi CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.27.0" -CAKEWALLET_BUILD_NUMBER=54 +CAKEWALLET_VERSION="4.28.0" +CAKEWALLET_BUILD_NUMBER=55 if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then echo "Wrong app type." diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index f99cee740..5f323c25e 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -16,13 +16,13 @@ if [ -n "$1" ]; then fi MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="4.27.0" -MONERO_COM_BUILD_NUMBER=51 +MONERO_COM_VERSION="4.28.0" +MONERO_COM_BUILD_NUMBER=53 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.27.0" -CAKEWALLET_BUILD_NUMBER=113 +CAKEWALLET_VERSION="4.28.0" +CAKEWALLET_BUILD_NUMBER=115 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/scripts/windows/build_exe_installer.iss b/scripts/windows/build_exe_installer.iss index 56285af77..f7cbef293 100644 --- a/scripts/windows/build_exe_installer.iss +++ b/scripts/windows/build_exe_installer.iss @@ -1,5 +1,5 @@ #define MyAppName "Cake Wallet" -#define MyAppVersion "4.27.0" +#define MyAppVersion "4.28.0" #define MyAppPublisher "Cake Labs LLC" #define MyAppURL "https://cakewallet.com/" #define MyAppExeName "CakeWallet.exe" From 1c7dd02a53ad03f8ddbe5e8fd6098f6a4d1797df Mon Sep 17 00:00:00 2001 From: cyan Date: Sat, 17 May 2025 01:03:02 +0200 Subject: [PATCH 07/63] fix(cw_monero): wait a bit before refreshing txlist so it appears (#2275) --- cw_monero/lib/pending_monero_transaction.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cw_monero/lib/pending_monero_transaction.dart b/cw_monero/lib/pending_monero_transaction.dart index 31965b10d..4f8606a79 100644 --- a/cw_monero/lib/pending_monero_transaction.dart +++ b/cw_monero/lib/pending_monero_transaction.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/structs/pending_transaction.dart'; import 'package:cw_monero/api/transaction_history.dart' @@ -56,8 +58,11 @@ class PendingMoneroTransaction with PendingTransaction { rethrow; } - await wallet.fetchTransactions(); storeSync(force: true); + unawaited(() async { + await Future.delayed(const Duration(milliseconds: 250)); + await wallet.fetchTransactions(); + }()); } @override @@ -66,8 +71,11 @@ class PendingMoneroTransaction with PendingTransaction { final ret = monero_transaction_history.commitTransactionFromPointerAddress( address: pendingTransactionDescription.pointerAddress, useUR: true); - await wallet.fetchTransactions(); storeSync(force: true); + unawaited(() async { + await Future.delayed(const Duration(milliseconds: 250)); + await wallet.fetchTransactions(); + }()); return ret; } catch (e) { final message = e.toString(); From 1e5568fc1b76fe4f5760e1b3c5f97a2bb840dcbe Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Sat, 17 May 2025 03:30:49 +0300 Subject: [PATCH 08/63] Update button text --- lib/src/screens/receive/widgets/qr_widget.dart | 2 +- lib/view_model/wallet_list/wallet_list_view_model.dart | 2 -- res/values/strings_ar.arb | 3 ++- res/values/strings_bg.arb | 3 ++- res/values/strings_cs.arb | 3 ++- res/values/strings_de.arb | 3 ++- res/values/strings_en.arb | 3 ++- res/values/strings_es.arb | 3 ++- res/values/strings_fr.arb | 3 ++- res/values/strings_ha.arb | 3 ++- res/values/strings_hi.arb | 3 ++- res/values/strings_hr.arb | 3 ++- res/values/strings_hy.arb | 3 ++- res/values/strings_id.arb | 3 ++- res/values/strings_it.arb | 3 ++- res/values/strings_ja.arb | 3 ++- res/values/strings_ko.arb | 3 ++- res/values/strings_my.arb | 3 ++- res/values/strings_nl.arb | 3 ++- res/values/strings_pl.arb | 3 ++- res/values/strings_pt.arb | 3 ++- res/values/strings_ru.arb | 3 ++- res/values/strings_th.arb | 3 ++- res/values/strings_tl.arb | 3 ++- res/values/strings_tr.arb | 3 ++- res/values/strings_uk.arb | 3 ++- res/values/strings_ur.arb | 3 ++- res/values/strings_vi.arb | 3 ++- res/values/strings_yo.arb | 3 ++- res/values/strings_zh.arb | 3 ++- scripts/android/app_env.sh | 4 ++-- scripts/ios/app_env.sh | 4 ++-- 32 files changed, 61 insertions(+), 35 deletions(-) diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index 69b2fa445..2df1c7b90 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -224,7 +224,7 @@ class QRWidget extends StatelessWidget { 'assets/images/payjoin.png', width: 25, ), - text: S.of(context).copy_payjoin_url, + text: S.of(context).copy_payjoin_address, color: Theme.of(context).cardColor, textColor: Theme.of(context).extension()!.buttonTextColor, ), diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index 96662e0b6..4046488fd 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -2,8 +2,6 @@ import 'package:cake_wallet/core/wallet_loading_service.dart'; import 'package:cake_wallet/entities/wallet_group.dart'; import 'package:cake_wallet/entities/wallet_list_order_types.dart'; import 'package:cake_wallet/entities/wallet_manager.dart'; -import 'package:cake_wallet/reactions/bip39_wallet_utils.dart'; -import 'package:cw_core/utils/print_verbose.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/store/app_store.dart'; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index d862512b0..7caf3a126 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -202,6 +202,7 @@ "copy": "نسخ", "copy_address": "نسخ العنوان", "copy_id": "نسخ معرف العملية", + "copy_payjoin_address": "نسخ عنوان payjoin", "copy_payjoin_url": "نسخ Payjoin url", "copyWalletConnectLink": "ﺎﻨﻫ ﻪﻘﺼﻟﺍﻭ dApp ﻦﻣ WalletConnect ﻂﺑﺍﺭ ﺦﺴﻧﺍ", "corrupted_seed_notice": "تالف ملفات هذه المحفظة ولا يمكن فتحها. يرجى الاطلاع على عبارة البذور وحفظها واستعادة المحفظة.\n\nإذا كانت القيمة فارغة ، لم تتمكن البذور من استردادها بشكل صحيح.", @@ -1070,4 +1071,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 c4cf00fc7..ae08c4ac4 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -202,6 +202,7 @@ "copy": "Копиране", "copy_address": "Copy Address", "copy_id": "Копиране на ID", + "copy_payjoin_address": "Копирайте адрес на payjoin", "copy_payjoin_url": "Копиране Payjoin url", "copyWalletConnectLink": "Копирайте връзката WalletConnect от dApp и я поставете тук", "corrupted_seed_notice": "Файловете за този портфейл са повредени и не могат да бъдат отворени. Моля, прегледайте фразата за семена, запазете я и възстановете портфейла.\n\nАко стойността е празна, тогава семето не успя да бъде правилно възстановено.", @@ -1070,4 +1071,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 705195ced..e9a5bfb55 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -202,6 +202,7 @@ "copy": "Kopírovat", "copy_address": "Zkopírovat adresu", "copy_id": "Kopírovat ID", + "copy_payjoin_address": "Kopírovat adresu payjoin", "copy_payjoin_url": "Kopírovat Payjoin URL", "copyWalletConnectLink": "Zkopírujte odkaz WalletConnect z dApp a vložte jej sem", "corrupted_seed_notice": "Soubory pro tuto peněženku jsou poškozeny a nemohou být otevřeny. Podívejte se prosím na osivo, uložte ji a obnovte peněženku.\n\nPokud je hodnota prázdná, pak semeno nebylo možné správně obnovit.", @@ -1070,4 +1071,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 2105fa2a5..e35cf08ee 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -202,6 +202,7 @@ "copy": "Kopieren", "copy_address": "Adresse kopieren", "copy_id": "ID kopieren", + "copy_payjoin_address": "Kopieren Sie Payjoin -Adresse", "copy_payjoin_url": "Payjoin URL kopieren", "copyWalletConnectLink": "Kopieren Sie den WalletConnect-Link von dApp und fügen Sie ihn hier ein", "corrupted_seed_notice": "Die Dateien für diese Wallet sind beschädigt und können nicht geöffnet werden. Bitte sehen Sie sich die Seeds an, speichern Sie sie und stellen Sie die Wallet wieder her.\n\nWenn der Wert leer ist, konnte der Seed nicht korrekt wiederhergestellt werden.", @@ -1073,4 +1074,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 11de69b59..a71aa5f54 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -202,6 +202,7 @@ "copy": "Copy", "copy_address": "Copy Address", "copy_id": "Copy ID", + "copy_payjoin_address": "Copy Payjoin Address", "copy_payjoin_url": "Copy Payjoin URL", "copyWalletConnectLink": "Copy the WalletConnect link from dApp and paste here", "corrupted_seed_notice": "The files for this wallet are corrupted and are unable to be opened. Please view the seed phrase, save it, and restore the wallet.\n\nIf the value is empty, then the seed was unable to be correctly recovered.", @@ -1071,4 +1072,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 d9906e8b4..23dad880e 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -202,6 +202,7 @@ "copy": "Dupdo", "copy_address": "Copiar dirección ", "copy_id": "Copiar ID", + "copy_payjoin_address": "Copiar dirección de payjoin", "copy_payjoin_url": "Copiar Payjoin url", "copyWalletConnectLink": "Copie el enlace de WalletConnect de dApp y péguelo aquí", "corrupted_seed_notice": "Los archivos para esta billetera están dañados y no pueden abrirse. Vea la frase de semillas, guárdela y restaura la billetera.\n\nSi el valor está vacío, entonces la semilla no pudo recuperarse correctamente.", @@ -1071,4 +1072,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 fce699fec..d2b1c5356 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -202,6 +202,7 @@ "copy": "Copier", "copy_address": "Copier l'Adresse", "copy_id": "Copier l'ID", + "copy_payjoin_address": "Copier l'adresse de paiement", "copy_payjoin_url": "Copie Payjoin URL", "copyWalletConnectLink": "Copiez le lien WalletConnect depuis l'application décentralisée (dApp) et collez-le ici", "corrupted_seed_notice": "Les fichiers de ce portefeuille sont corrompus et ne peuvent pas être ouverts. Veuillez consulter la phrase de graines, sauver et restaurer le portefeuille.\n\nSi la valeur est vide, la graine n'a pas pu être correctement récupérée.", @@ -1070,4 +1071,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 2a711b1b3..ee1fed998 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -202,6 +202,7 @@ "copy": "Kwafi", "copy_address": "Kwafi Adireshin", "copy_id": "Kwafi ID", + "copy_payjoin_address": "Kwafi Adireshin Payjoin", "copy_payjoin_url": "Kwafa Payjoin url", "copyWalletConnectLink": "Kwafi hanyar haɗin WalletConnect daga dApp kuma liƙa a nan", "corrupted_seed_notice": "Fayilolin don wannan walat ɗin sun lalata kuma ba za a iya buɗe su ba. Da fatan za a duba kalmar iri, adana shi, da dawo da walat.\n\nIdan darajar ta kasance fanko, to sai zuriyar da ba ta iya murmurewa daidai ba.", @@ -1072,4 +1073,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 8cfd6aa90..41c2dfd45 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -202,6 +202,7 @@ "copy": "प्रतिलिपि", "copy_address": "पता कॉपी करें", "copy_id": "प्रतिलिपि ID", + "copy_payjoin_address": "कॉपी पेजोइन पता", "copy_payjoin_url": "कॉपी Payjoin url", "copyWalletConnectLink": "dApp से वॉलेटकनेक्ट लिंक को कॉपी करें और यहां पेस्ट करें", "corrupted_seed_notice": "इस वॉलेट की फाइलें दूषित हैं और उन्हें खोलने में असमर्थ हैं। कृपया बीज वाक्यांश देखें, इसे बचाएं, और बटुए को पुनर्स्थापित करें।\n\nयदि मूल्य खाली है, तो बीज सही ढंग से पुनर्प्राप्त करने में असमर्थ था।", @@ -1072,4 +1073,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 5ee9ca12b..d8c2a255f 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -202,6 +202,7 @@ "copy": "Kopiraj", "copy_address": "Kopiraj adresu", "copy_id": "Kopirati ID", + "copy_payjoin_address": "Kopirajte Payjoin adresu", "copy_payjoin_url": "Kopirajte Payjoin url", "copyWalletConnectLink": "Kopirajte vezu WalletConnect iz dApp-a i zalijepite je ovdje", "corrupted_seed_notice": "Datoteke za ovaj novčanik su oštećene i nisu u mogućnosti otvoriti. Molimo pogledajte sjemensku frazu, spremite je i vratite novčanik.\n\nAko je vrijednost prazna, tada sjeme nije bilo u stanju ispravno oporaviti.", @@ -1070,4 +1071,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 b7d14aaad..b39e9092f 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -202,6 +202,7 @@ "copy": "Պատճենել", "copy_address": "Պատճենել հասցեն", "copy_id": "Պատճենել ID", + "copy_payjoin_address": "Պատճենեք վճարման հասցեն", "copy_payjoin_url": "Պատճենել Payjoin url", "copyWalletConnectLink": "Պատճենել WalletConnect հղումը dApp-ից և տեղադրել այստեղ", "corrupted_seed_notice": "Այս դրամապանակի համար ֆայլերը կոռումպացված են եւ չեն կարողանում բացվել: Խնդրում ենք դիտել սերմերի արտահայտությունը, պահպանել այն եւ վերականգնել դրամապանակը:\n\nԵթե ​​արժեքը դատարկ է, ապա սերմը չկարողացավ ճիշտ վերականգնվել:", @@ -1068,4 +1069,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 247334fda..b77dc2cf0 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -202,6 +202,7 @@ "copy": "Salin", "copy_address": "Salin Alamat", "copy_id": "Salin ID", + "copy_payjoin_address": "Salin Alamat Payjoin", "copy_payjoin_url": "Salin Payjoin url", "copyWalletConnectLink": "Salin tautan WalletConnect dari dApp dan tempel di sini", "corrupted_seed_notice": "File untuk dompet ini rusak dan tidak dapat dibuka. Silakan lihat frasa benih, simpan, dan kembalikan dompet.\n\nJika nilainya kosong, maka benih tidak dapat dipulihkan dengan benar.", @@ -1073,4 +1074,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 6d713c182..a2753277f 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -202,6 +202,7 @@ "copy": "Copia", "copy_address": "Copia Indirizzo", "copy_id": "Copia ID", + "copy_payjoin_address": "Copia l'indirizzo PayJoin", "copy_payjoin_url": "Copia Payjoin url", "copyWalletConnectLink": "Copia il collegamento WalletConnect dalla dApp e incollalo qui", "corrupted_seed_notice": "I file per questo portafoglio sono corrotti e non è possibile aprirli. Visualizza la frase del seme, salvala e ripristina il portafoglio.\n\nSe il valore è vuoto, non è stato possibile recuperare correttamente il seme.", @@ -1072,4 +1073,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 1e846a9b1..c2ab7c2be 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -202,6 +202,7 @@ "copy": "コピー", "copy_address": "住所をコピー", "copy_id": "IDをコピー", + "copy_payjoin_address": "Payjoinアドレスをコピーします", "copy_payjoin_url": "Payjoin urlをコピーします", "copyWalletConnectLink": "dApp から WalletConnect リンクをコピーし、ここに貼り付けます", "corrupted_seed_notice": "このウォレットのファイルは破損しており、開くことができません。シードフレーズを表示し、保存し、財布を復元してください。\n\n値が空の場合、種子を正しく回復することができませんでした。", @@ -1071,4 +1072,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 8bad5a6d2..841cf7a37 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -202,6 +202,7 @@ "copy": "복사", "copy_address": "주소 복사", "copy_id": "ID 복사", + "copy_payjoin_address": "Payjoin 주소를 복사하십시오", "copy_payjoin_url": "Payjoin url을 복사하십시오", "copyWalletConnectLink": "dApp에서 WalletConnect 링크를 복사하여 여기에 붙여넣으세요", "corrupted_seed_notice": "이 지갑의 파일이 손상되어 열 수 없습니다. 시드 구문을 보고 저장한 다음 지갑을 복구하세요.\n\n값이 비어 있으면 시드를 올바르게 복구할 수 없었습니다.", @@ -1071,4 +1072,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 2bf5f2dde..54ad17d2b 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -202,6 +202,7 @@ "copy": "ကော်ပီ", "copy_address": "လိပ်စာကို ကူးယူပါ။", "copy_id": "ID ကူးယူပါ။", + "copy_payjoin_address": "Payjoin လိပ်စာကိုကူးယူပါ", "copy_payjoin_url": "Payjoin URL ကိုကူးယူပါ", "copyWalletConnectLink": "dApp မှ WalletConnect လင့်ခ်ကို ကူးယူပြီး ဤနေရာတွင် ကူးထည့်ပါ။", "corrupted_seed_notice": "ဤပိုက်ဆံအိတ်အတွက်ဖိုင်များသည်အကျင့်ပျက်ခြစားမှုများနှင့်မဖွင့်နိုင်ပါ။ ကျေးဇူးပြု. မျိုးစေ့များကိုကြည့်ပါ, ၎င်းကိုသိမ်းဆည်းပါ, ပိုက်ဆံအိတ်ကိုပြန်ယူပါ။\n\nအကယ်. တန်ဖိုးသည်အချည်းနှီးဖြစ်ပါကမျိုးစေ့ကိုမှန်ကန်စွာပြန်လည်ကောင်းမွန်မရရှိနိုင်ပါ။", @@ -1070,4 +1071,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 f3cacb927..aa489d10d 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -202,6 +202,7 @@ "copy": "Kopiëren", "copy_address": "Adres kopiëren", "copy_id": "ID kopiëren", + "copy_payjoin_address": "Payjoin -adres kopiëren", "copy_payjoin_url": "Kopieer Payjoin url", "copyWalletConnectLink": "Kopieer de WalletConnect-link van dApp en plak deze hier", "corrupted_seed_notice": "De bestanden voor deze portemonnee zijn beschadigd en kunnen niet worden geopend. Bekijk de zaadzin, bewaar deze en herstel de portemonnee.\n\nAls de waarde leeg is, kon het zaad niet correct worden hersteld.", @@ -1071,4 +1072,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 99dfabef3..b8e02aacc 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -202,6 +202,7 @@ "copy": "Kopiuj", "copy_address": "Skopiuj adress", "copy_id": "skopiuj ID", + "copy_payjoin_address": "Kopiuj adres PAYJOIN", "copy_payjoin_url": "Skopiuj Payjoin url", "copyWalletConnectLink": "Skopiuj link do WalletConnect z dApp i wklej tutaj", "corrupted_seed_notice": "Pliki dla tego portfela są uszkodzone i nie można ich otworzyć. Zobacz frazę seed, zapisz je i przywróć portfel.\n\nJeśli wartość jest pusta, frazy seed nie można było poprawnie odzyskać.", @@ -1070,4 +1071,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 f570605f5..c38a368db 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -202,6 +202,7 @@ "copy": "Copiar", "copy_address": "Copiar endereço", "copy_id": "Copiar ID", + "copy_payjoin_address": "Copie o endereço PayJoin", "copy_payjoin_url": "Copie Payjoin url", "copyWalletConnectLink": "Copie o link WalletConnect do dApp e cole aqui", "corrupted_seed_notice": "Os arquivos para esta carteira estão corrompidos e não podem ser abertos. Veja a frase das sementes, salve -a e restaure a carteira.\n\nSe o valor estiver vazio, a semente não pôde ser recuperada corretamente.", @@ -1073,4 +1074,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 ed3d9b33e..f532783ee 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -202,6 +202,7 @@ "copy": "Скопировать", "copy_address": "Cкопировать адрес", "copy_id": "Скопировать ID", + "copy_payjoin_address": "Скопируйте адрес оплатывания", "copy_payjoin_url": "Копировать Payjoin url", "copyWalletConnectLink": "Скопируйте ссылку WalletConnect из dApp и вставьте сюда.", "corrupted_seed_notice": "Файлы для этого кошелька повреждены и не могут быть открыты. Пожалуйста, просмотрите семенную фразу, сохраните ее и восстановите кошелек.\n\nЕсли значение пустое, то семя не смог правильно восстановить.", @@ -1071,4 +1072,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 de3898616..60508d827 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -202,6 +202,7 @@ "copy": "คัดลอก", "copy_address": "คัดลอกที่อยู่", "copy_id": "คัดลอก ID", + "copy_payjoin_address": "คัดลอกที่อยู่ PayJoin", "copy_payjoin_url": "คัดลอก Payjoin url", "copyWalletConnectLink": "คัดลอกลิงก์ WalletConnect จาก dApp แล้ววางที่นี่", "corrupted_seed_notice": "ไฟล์สำหรับกระเป๋าเงินนี้เสียหายและไม่สามารถเปิดได้ โปรดดูวลีเมล็ดบันทึกและกู้คืนกระเป๋าเงิน\n\nหากค่าว่างเปล่าเมล็ดก็ไม่สามารถกู้คืนได้อย่างถูกต้อง", @@ -1070,4 +1071,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 e681d3ffc..14b8e3768 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -202,6 +202,7 @@ "copy": "Kopyahin", "copy_address": "Kopyahin ang Address", "copy_id": "Kopyahin ang ID", + "copy_payjoin_address": "Kopyahin ang address ng PayJoin", "copy_payjoin_url": "Kopyahin ang Payjoin url", "copyWalletConnectLink": "Kopyahin ang link ng WalletConnect mula sa dApp at i-paste dito", "corrupted_seed_notice": "Ang mga file para sa pitaka na ito ay nasira at hindi mabubuksan. Mangyaring tingnan ang parirala ng binhi, i -save ito, at ibalik ang pitaka.\n\nKung ang halaga ay walang laman, kung gayon ang binhi ay hindi ma -recover nang tama.", @@ -1070,4 +1071,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 c61da2a19..6a9b433b3 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -202,6 +202,7 @@ "copy": "Kopyala", "copy_address": "Adresi kopyala", "copy_id": "ID'yi kopyala", + "copy_payjoin_address": "PayJoin adresini kopyala", "copy_payjoin_url": "Payjoin url kopyala", "copyWalletConnectLink": "WalletConnect bağlantısını dApp'ten kopyalayıp buraya yapıştırın", "corrupted_seed_notice": "Bu cüzdanın dosyaları bozuk ve açılamıyor. Lütfen tohum ifadesini görüntüleyin, kaydedin ve cüzdanı geri yükleyin.\n\nDeğer boşsa, tohum doğru bir şekilde geri kazanılamadı.", @@ -1070,4 +1071,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 8f890d000..9a03526b1 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -202,6 +202,7 @@ "copy": "Скопіювати", "copy_address": "Cкопіювати адресу", "copy_id": "Скопіювати ID", + "copy_payjoin_address": "Копіювати адресу Payjoin", "copy_payjoin_url": "Скопіюйте Payjoin url", "copyWalletConnectLink": "Скопіюйте посилання WalletConnect із dApp і вставте сюди", "corrupted_seed_notice": "Файли для цього гаманця пошкоджені і не можуть бути відкриті. Перегляньте насіннєву фразу, збережіть її та відновіть гаманець.\n\nЯкщо значення порожнє, то насіння не могло бути правильно відновленим.", @@ -1071,4 +1072,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 65a26fb2b..65fb7db39 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -202,6 +202,7 @@ "copy": "کاپی", "copy_address": "ایڈریس کاپی کریں۔", "copy_id": "کاپی ID", + "copy_payjoin_address": "پے جوائن ایڈریس کاپی کریں", "copy_payjoin_url": "کاپی کریں Payjoin url", "copyWalletConnectLink": "dApp ﮯﺳ WalletConnect ۔ﮟﯾﺮﮐ ﭧﺴﯿﭘ ﮞﺎﮩﯾ ﺭﻭﺍ ﮟﯾﺮﮐ ﯽﭘﺎﮐ ﻮﮐ ﮏﻨﻟ", "corrupted_seed_notice": "اس پرس کے لئے فائلیں خراب ہیں اور کھولنے سے قاصر ہیں۔ براہ کرم بیج کے فقرے کو دیکھیں ، اسے بچائیں ، اور بٹوے کو بحال کریں۔\n\nاگر قیمت خالی ہے ، تو بیج صحیح طور پر بازیافت کرنے سے قاصر تھا۔", @@ -1072,4 +1073,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 bd03941fa..f65192fb9 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -201,6 +201,7 @@ "copy": "Sao chép", "copy_address": "Sao chép Địa chỉ", "copy_id": "Sao chép ID", + "copy_payjoin_address": "Sao chép địa chỉ payjoin", "copy_payjoin_url": "Sao chép Payjoin url", "copyWalletConnectLink": "Sao chép liên kết WalletConnect từ dApp và dán vào đây", "corrupted_seed_notice": "Các tệp cho ví này bị hỏng và không thể mở. Vui lòng xem cụm từ hạt giống, lưu nó và khôi phục ví.\n\nNếu giá trị trống, thì hạt giống không thể được phục hồi chính xác.", @@ -1067,4 +1068,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 ed759ea0d..b7282aeab 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -202,6 +202,7 @@ "copy": "Ṣẹ̀dà", "copy_address": "Ṣẹ̀dà àdírẹ́sì", "copy_id": "Ṣẹ̀dà àmì ìdánimọ̀", + "copy_payjoin_address": "Daakọ Iwe Adirẹsi Payjoin", "copy_payjoin_url": "Daakọ Payjoin url", "copyWalletConnectLink": "Daakọ ọna asopọ WalletConnect lati dApp ki o si lẹẹmọ nibi", "corrupted_seed_notice": "Awọn faili fun apamọwọ yii jẹ ibajẹ ati pe ko lagbara lati ṣii. Jọwọ wo ọrọ iseda, fipamọ rẹ, ki o mu apamọwọ naa pada.\n\nTi iye ba ṣofo, lẹhinna irugbin naa ko lagbara lati gba pada ni deede.", @@ -1071,4 +1072,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 63dbe8e46..09ca7cd11 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -202,6 +202,7 @@ "copy": "复制", "copy_address": "复制地址", "copy_id": "复制ID", + "copy_payjoin_address": "复制工资公司地址", "copy_payjoin_url": "复制Payjoin url", "copyWalletConnectLink": "从 dApp 复制 WalletConnect 链接并粘贴到此处", "corrupted_seed_notice": "该钱包的文件被损坏,无法打开。请查看种子短语,保存并恢复钱包。\n\n如果该值为空,则种子无法正确恢复。", @@ -1070,4 +1071,4 @@ "you_will_send": "转换自", "youCanGoBackToYourDapp": "您现在可以回到DAPP", "yy": "YY" -} +} \ No newline at end of file diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 34192e9ed..be3d75f57 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,14 +15,14 @@ APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" MONERO_COM_VERSION="4.28.0" -MONERO_COM_BUILD_NUMBER=123 +MONERO_COM_BUILD_NUMBER=124 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="4.28.0" -CAKEWALLET_BUILD_NUMBER=261 +CAKEWALLET_BUILD_NUMBER=262 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 6535931c8..6633af56b 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,12 +13,12 @@ APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" MONERO_COM_VERSION="4.28.0" -MONERO_COM_BUILD_NUMBER=122 +MONERO_COM_BUILD_NUMBER=123 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="4.28.0" -CAKEWALLET_BUILD_NUMBER=318 +CAKEWALLET_BUILD_NUMBER=319 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" From dcaeac9b85ffb9a5c8f39378f120ba24dc212dbc Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 20 May 2025 03:10:49 +0300 Subject: [PATCH 09/63] disable swap until synced for btc/ltc/xmr --- lib/src/screens/exchange/exchange_page.dart | 34 ++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index e5027198b..b7b377fe1 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -97,6 +97,11 @@ class ExchangePage extends BasePage { } }; + bool get _shouldWaitTillSynced => + [CryptoCurrency.xmr, CryptoCurrency.btc, CryptoCurrency.ltc] + .contains(exchangeViewModel.depositCurrency) && + !(exchangeViewModel.status is SyncedSyncStatus); + @override Widget middle(BuildContext context) => Row( mainAxisAlignment: MainAxisAlignment.center, @@ -252,20 +257,21 @@ class ExchangePage extends BasePage { onPressed: exchangeViewModel.isAvailableInSelected ? () { FocusScope.of(context).unfocus(); - if (_formKey.currentState != null && - _formKey.currentState!.validate()) { - if ((exchangeViewModel.depositCurrency == CryptoCurrency.xmr) && - (!(exchangeViewModel.status is SyncedSyncStatus))) { - showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).exchange, - alertContent: S.of(context).exchange_sync_alert_content, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - } else { + if (_formKey.currentState != null && + _formKey.currentState!.validate()) { + if (_shouldWaitTillSynced) { + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).exchange, + alertContent: + S.of(context).exchange_sync_alert_content, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop(), + ); + }); + } else { final check = exchangeViewModel.shouldDisplayTOTP(); authService.authenticateAction( context, From 79faeb98afe3395f0a98dbd58e3fa28d291c2508 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 20 May 2025 21:25:00 +0300 Subject: [PATCH 10/63] 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 11/63] 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 12/63] 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 13/63] 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 14/63] 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 15/63] 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 16/63] 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 17/63] 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 18/63] 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