diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 84ac69197..c3c61865f 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -110,6 +110,7 @@ jobs: echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart + echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart - name: Rename app run: sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 85331ee31..90051f1f7 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -119,7 +119,7 @@ class CryptoCurrency extends EnumerableItem with Serializable { static const ape = CryptoCurrency(title: 'APE', tag: 'ETH', fullName: 'ApeCoin', raw: 30, name: 'ape', iconPath: 'assets/images/ape_icon.png'); static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'C-CHAIN', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png'); static const btt = CryptoCurrency(title: 'BTT', tag: 'ETH', fullName: 'BitTorrent', raw: 32, name: 'btt', iconPath: 'assets/images/btt_icon.png'); - static const bttc = CryptoCurrency(title: 'BTTC', tag: 'BSC', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/bttbsc_icon.png'); + static const bttc = CryptoCurrency(title: 'BTTC', tag: 'TRX', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/bttbsc_icon.png'); static const doge = CryptoCurrency(title: 'DOGE', fullName: 'Dogecoin', raw: 34, name: 'doge', iconPath: 'assets/images/doge_icon.png'); static const firo = CryptoCurrency(title: 'FIRO', raw: 35, name: 'firo', iconPath: 'assets/images/firo_icon.png'); static const usdttrc20 = CryptoCurrency(title: 'USDT', tag: 'TRX', fullName: 'USDT Tether', raw: 36, name: 'usdttrc20', iconPath: 'assets/images/usdttrc20_icon.png'); diff --git a/lib/anypay/anypay_api.dart b/lib/anypay/anypay_api.dart index 5eb9e460b..679d0eabf 100644 --- a/lib/anypay/anypay_api.dart +++ b/lib/anypay/anypay_api.dart @@ -80,7 +80,7 @@ class AnyPayApi { final response = await post(Uri.parse(uri), headers: headers, body: utf8.encode(json.encode(body))); if (response.statusCode == 400) { final decodedBody = json.decode(response.body) as Map; - throw Exception(decodedBody['message'] as String); + throw Exception(decodedBody['message'] as String? ?? 'Unexpected response\nError code: 400'); } if (response.statusCode != 200) { diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 519cd92a3..22ad6a9d8 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -198,4 +198,26 @@ class AddressValidator extends TextValidator { return []; } } + + static String? getAddressFromStringPattern(CryptoCurrency type) { + switch (type) { + case CryptoCurrency.xmr: + return '([^0-9a-zA-Z]|^)4[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)'; + case CryptoCurrency.btc: + return '([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{39}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{59}([^0-9a-zA-Z]|\$)'; + case CryptoCurrency.ltc: + return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)' + '|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)'; + default: + return null; + } + } } diff --git a/lib/entities/openalias_record.dart b/lib/entities/openalias_record.dart index 9e2b3f680..842a711fe 100644 --- a/lib/entities/openalias_record.dart +++ b/lib/entities/openalias_record.dart @@ -1,5 +1,4 @@ import 'package:basic_utils/basic_utils.dart'; -import 'package:cw_core/wallet_type.dart'; class OpenaliasRecord { OpenaliasRecord({ @@ -22,69 +21,68 @@ class OpenaliasRecord { return formattedName; } - static Future fetchAddressAndName({ + static Future?> lookupOpenAliasRecord(String name) async { + try { + final txtRecord = await DnsUtils.lookupRecord(name, RRecordType.TXT, dnssec: true); + + return txtRecord; + } catch (e) { + print("${e.toString()}"); + return null; + } + } + + static OpenaliasRecord fetchAddressAndName({ required String formattedName, required String ticker, - }) async { + required List txtRecord, + }) { String address = formattedName; String name = formattedName; String note = ''; - if (formattedName.contains(".")) { - try { - final txtRecord = await DnsUtils.lookupRecord( - formattedName, RRecordType.TXT, - dnssec: true); + for (RRecord element in txtRecord) { + String record = element.data; - if (txtRecord != null) { - for (RRecord element in txtRecord) { - String record = element.data; + if (record.contains("oa1:$ticker") && record.contains("recipient_address")) { + record = record.replaceAll('\"', ""); - if (record.contains("oa1:$ticker") && - record.contains("recipient_address")) { - record = record.replaceAll('\"', ""); + final dataList = record.split(";"); - final dataList = record.split(";"); + address = dataList + .where((item) => (item.contains("recipient_address"))) + .toString() + .replaceAll("oa1:$ticker recipient_address=", "") + .replaceAll("(", "") + .replaceAll(")", "") + .trim(); - address = dataList - .where((item) => (item.contains("recipient_address"))) - .toString() - .replaceAll("oa1:$ticker recipient_address=", "") - .replaceAll("(", "") - .replaceAll(")", "") - .trim(); + final recipientName = dataList + .where((item) => (item.contains("recipient_name"))) + .toString() + .replaceAll("(", "") + .replaceAll(")", "") + .trim(); - final recipientName = dataList - .where((item) => (item.contains("recipient_name"))) - .toString() - .replaceAll("(", "") - .replaceAll(")", "") - .trim(); - - if (recipientName.isNotEmpty) { - name = recipientName.replaceAll("recipient_name=", ""); - } - - final description = dataList - .where((item) => (item.contains("tx_description"))) - .toString() - .replaceAll("(", "") - .replaceAll(")", "") - .trim(); - - if (description.isNotEmpty) { - note = description.replaceAll("tx_description=", ""); - } - - break; - } - } + if (recipientName.isNotEmpty) { + name = recipientName.replaceAll("recipient_name=", ""); } - } catch (e) { - print("${e.toString()}"); - } - } - return OpenaliasRecord(address: address, name: name, description: note); + final description = dataList + .where((item) => (item.contains("tx_description"))) + .toString() + .replaceAll("(", "") + .replaceAll(")", "") + .trim(); + + if (description.isNotEmpty) { + note = description.replaceAll("tx_description=", ""); + } + + break; + } + } + return OpenaliasRecord(address: address, name: name, description: note); +} } diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index 775f6e229..6574d26fd 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -1,40 +1,61 @@ +import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/entities/openalias_record.dart'; import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; import 'package:cake_wallet/entities/emoji_string_extension.dart'; +import 'package:cake_wallet/twitter/twitter_api.dart'; +import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/entities/fio_address_provider.dart'; class AddressResolver { - AddressResolver({required this.yatService, required this.walletType}); - + final YatService yatService; final WalletType walletType; - + static const unstoppableDomains = [ - 'crypto', - 'zil', - 'x', - 'coin', - 'wallet', - 'bitcoin', - '888', - 'nft', - 'dao', - 'blockchain' -]; + 'crypto', + 'zil', + 'x', + 'coin', + 'wallet', + 'bitcoin', + '888', + 'nft', + 'dao', + 'blockchain' + ]; + + static String? extractAddressByType({required String raw, required CryptoCurrency type}) { + final addressPattern = AddressValidator.getAddressFromStringPattern(type); + + if (addressPattern == null) { + throw 'Unexpected token: $type for getAddressFromStringPattern'; + } + + final match = RegExp(addressPattern).firstMatch(raw); + return match?.group(0)?.replaceAll(RegExp('[^0-9a-zA-Z]'), ''); + } Future resolve(String text, String ticker) async { try { - if (text.contains('@') && !text.contains('.')) { + if (text.startsWith('@') && !text.substring(1).contains('@')) { + final formattedName = text.substring(1); + final twitterUser = await TwitterApi.lookupUserByName(userName: formattedName); + final address = extractAddressByType( + raw: twitterUser.description ?? '', type: CryptoCurrency.fromString(ticker)); + if (address != null) { + return ParsedAddress.fetchTwitterAddress(address: address, name: text); + } + } + if (!text.startsWith('@') && text.contains('@') && !text.contains('.')) { final bool isFioRegistered = await FioAddressProvider.checkAvail(text); if (isFioRegistered) { final address = await FioAddressProvider.getPubAddress(text, ticker); return ParsedAddress.fetchFioAddress(address: address, name: text); - } - + } } if (text.hasOnlyEmojis) { if (walletType != WalletType.haven) { @@ -55,10 +76,14 @@ class AddressResolver { return ParsedAddress.fetchUnstoppableDomainAddress(address: address, name: text); } - final record = await OpenaliasRecord.fetchAddressAndName( - formattedName: formattedName, ticker: ticker); - return ParsedAddress.fetchOpenAliasAddress(record: record, name: text); - + if (formattedName.contains(".")) { + final txtRecord = await OpenaliasRecord.lookupOpenAliasRecord(formattedName); + if (txtRecord != null) { + final record = await OpenaliasRecord.fetchAddressAndName( + formattedName: formattedName, ticker: ticker, txtRecord: txtRecord); + return ParsedAddress.fetchOpenAliasAddress(record: record, name: text); + } + } } catch (e) { print(e.toString()); } diff --git a/lib/entities/parsed_address.dart b/lib/entities/parsed_address.dart index 09cecbf04..a73b44e33 100644 --- a/lib/entities/parsed_address.dart +++ b/lib/entities/parsed_address.dart @@ -1,8 +1,7 @@ import 'package:cake_wallet/entities/openalias_record.dart'; import 'package:cake_wallet/entities/yat_record.dart'; -import 'package:flutter/material.dart'; -enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed } +enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter } class ParsedAddress { ParsedAddress({ @@ -41,11 +40,7 @@ class ParsedAddress { ); } - factory ParsedAddress.fetchOpenAliasAddress({OpenaliasRecord? record, required String name}){ - final formattedName = OpenaliasRecord.formatDomainName(name); - if (record == null || record.address.contains(formattedName)) { - return ParsedAddress(addresses: [name]); - } + factory ParsedAddress.fetchOpenAliasAddress({required OpenaliasRecord record, required String name}){ return ParsedAddress( addresses: [record.address], name: record.name, @@ -62,6 +57,14 @@ class ParsedAddress { ); } + factory ParsedAddress.fetchTwitterAddress({required String address, required String name}){ + return ParsedAddress( + addresses: [address], + name: name, + parseFrom: ParseFrom.twitter, + ); + } + final List addresses; final String name; final String description; diff --git a/lib/exchange/changenow/changenow_exchange_provider.dart b/lib/exchange/changenow/changenow_exchange_provider.dart index 88f52cd70..e173dbdf6 100644 --- a/lib/exchange/changenow/changenow_exchange_provider.dart +++ b/lib/exchange/changenow/changenow_exchange_provider.dart @@ -276,6 +276,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider { switch(currency) { case CryptoCurrency.zec: return 'zec'; + case CryptoCurrency.usdcpoly: + return 'usdcmatic'; + case CryptoCurrency.maticpoly: + return 'maticmainnet'; default: return currency.title.toLowerCase(); } diff --git a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart index e965e5331..6c5c23460 100644 --- a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart +++ b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart @@ -235,6 +235,10 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { return 'usdcpoly'; case CryptoCurrency.usdcsol: return 'usdcspl'; + case CryptoCurrency.matic: + return 'maticerc20'; + case CryptoCurrency.maticpoly: + return 'matic'; default: return currency.title.toLowerCase(); } diff --git a/lib/main.dart b/lib/main.dart index af7bb9eff..7ba1f204a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,22 +1,12 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:isolate'; -import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/entities/language_service.dart'; import 'package:cake_wallet/buy/order.dart'; -import 'package:cake_wallet/entities/preferences_key.dart'; -import 'package:cake_wallet/ionia/ionia_category.dart'; -import 'package:cake_wallet/ionia/ionia_merchant.dart'; -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/store/yat/yat_store.dart'; -import 'package:cake_wallet/themes/theme_list.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_mailer/flutter_mailer.dart'; import 'package:hive/hive.dart'; import 'package:cake_wallet/di.dart'; import 'package:path_provider/path_provider.dart'; @@ -50,21 +40,18 @@ import 'package:cake_wallet/wallet_type_utils.dart'; final navigatorKey = GlobalKey(); final rootKey = GlobalKey(); final RouteObserver routeObserver = RouteObserver(); -bool hasError = false; Future main() async { await runZonedGuarded(() async { WidgetsFlutterBinding.ensureInitialized(); - FlutterError.onError = (errorDetails) { - _onError(errorDetails); - }; + FlutterError.onError = ExceptionHandler.onError; /// A callback that is invoked when an unhandled error occurs in the root /// isolate. PlatformDispatcher.instance.onError = (error, stack) { - _onError(FlutterErrorDetails(exception: error, stack: stack)); + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack)); return true; }; @@ -155,92 +142,10 @@ Future main() async { initialMigrationVersion: 19); runApp(App()); }, (error, stackTrace) async { - _onError(FlutterErrorDetails(exception: error, stack: stackTrace)); + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace)); }); } -void _saveException(String? error, StackTrace? stackTrace) async { - final appDocDir = await getApplicationDocumentsDirectory(); - - final file = File('${appDocDir.path}/error.txt'); - final exception = { - "${DateTime.now()}": { - "Error": error, - "StackTrace": stackTrace.toString(), - } - }; - - const String separator = - '''\n\n========================================================== - ==========================================================\n\n'''; - - await file.writeAsString( - jsonEncode(exception) + separator, - mode: FileMode.append, - ); -} - -void _sendExceptionFile() async { - try { - final appDocDir = await getApplicationDocumentsDirectory(); - - final file = File('${appDocDir.path}/error.txt'); - - final MailOptions mailOptions = MailOptions( - subject: 'Mobile App Issue', - recipients: ['support@cakewallet.com'], - attachments: [file.path], - ); - - final result = await FlutterMailer.send(mailOptions); - - // Clear file content if the error was sent or saved. - // On android we can't know if it was sent or saved - if (result.name == MailerResponse.sent.name || - result.name == MailerResponse.saved.name || - result.name == MailerResponse.android.name) { - file.writeAsString("", mode: FileMode.write); - } - } catch (e, s) { - _saveException(e.toString(), s); - } -} - -void _onError(FlutterErrorDetails errorDetails) { - _saveException(errorDetails.exception.toString(), errorDetails.stack); - - if (hasError) { - return; - } - hasError = true; - - WidgetsBinding.instance.addPostFrameCallback( - (timeStamp) async { - await showPopUp( - context: navigatorKey.currentContext!, - builder: (context) { - return AlertWithTwoActions( - isDividerExist: true, - alertTitle: S.of(context).error, - alertContent: S.of(context).error_dialog_content, - rightButtonText: S.of(context).send, - leftButtonText: S.of(context).do_not_send, - actionRightButton: () { - Navigator.of(context).pop(); - _sendExceptionFile(); - }, - actionLeftButton: () { - Navigator.of(context).pop(); - }, - ); - }, - ); - - hasError = false; - }, - ); -} - Future initialSetup( {required SharedPreferences sharedPreferences, required Box nodes, diff --git a/lib/src/screens/ionia/cards/ionia_payment_status_page.dart b/lib/src/screens/ionia/cards/ionia_payment_status_page.dart index e02a3cee8..4096560db 100644 --- a/lib/src/screens/ionia/cards/ionia_payment_status_page.dart +++ b/lib/src/screens/ionia/cards/ionia_payment_status_page.dart @@ -157,7 +157,8 @@ class _IoniaPaymentStatusPageBodyBodyState extends State<_IoniaPaymentStatusPage Container( padding: EdgeInsets.only(left: 40, right: 40, bottom: 20), child: Text( - S.of(context).proceed_after_one_minute, + widget.viewModel.payingByBitcoin ? S.of(context).bitcoin_payments_require_1_confirmation + : S.of(context).proceed_after_one_minute, style: textMedium( color: Theme.of(context).primaryTextTheme!.headline6!.color!, ).copyWith(fontWeight: FontWeight.w500), diff --git a/lib/src/screens/send/widgets/extract_address_from_parsed.dart b/lib/src/screens/send/widgets/extract_address_from_parsed.dart index 4ddd6ada0..a293835a3 100644 --- a/lib/src/screens/send/widgets/extract_address_from_parsed.dart +++ b/lib/src/screens/send/widgets/extract_address_from_parsed.dart @@ -19,13 +19,18 @@ Future extractAddressFromParsed( address = parsedAddress.addresses.first; break; case ParseFrom.openAlias: - title = S.of(context).openalias_alert_title; - content = S.of(context).openalias_alert_content(parsedAddress.name); + title = S.of(context).address_detected; + content = S.of(context).extracted_address_content('${parsedAddress.name} (OpenAlias)'); address = parsedAddress.addresses.first; break; case ParseFrom.fio: title = S.of(context).address_detected; - content = S.of(context).openalias_alert_content(parsedAddress.name); + content = S.of(context).extracted_address_content('${parsedAddress.name} (FIO)'); + address = parsedAddress.addresses.first; + break; + case ParseFrom.twitter: + title = S.of(context).address_detected; + content = S.of(context).extracted_address_content('${parsedAddress.name} (Twitter)'); address = parsedAddress.addresses.first; break; case ParseFrom.yatRecord: diff --git a/lib/twitter/twitter_api.dart b/lib/twitter/twitter_api.dart new file mode 100644 index 000000000..27fb7d1a2 --- /dev/null +++ b/lib/twitter/twitter_api.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; +import 'package:cake_wallet/twitter/twitter_user.dart'; +import 'package:http/http.dart' as http; +import 'package:cake_wallet/.secrets.g.dart' as secrets; + +class TwitterApi { + static const twitterBearerToken = secrets.twitterBearerToken; + static const httpsScheme = 'https'; + static const apiHost = 'api.twitter.com'; + static const userPath = '/2/users/by/username/'; + + static Future lookupUserByName({required String userName}) async { + final queryParams = {'user.fields': 'description'}; + + final headers = {'authorization': 'Bearer $twitterBearerToken'}; + + final uri = Uri( + scheme: httpsScheme, + host: apiHost, + path: userPath + userName, + queryParameters: queryParams, + ); + + var response = await http.get(uri, headers: headers); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + final responseJSON = json.decode(response.body) as Map; + + if (responseJSON['errors'] != null) { + throw Exception(responseJSON['errors'][0]['detail']); + } + + return TwitterUser.fromJson(responseJSON['data'] as Map); + } +} diff --git a/lib/twitter/twitter_user.dart b/lib/twitter/twitter_user.dart new file mode 100644 index 000000000..c4bda7859 --- /dev/null +++ b/lib/twitter/twitter_user.dart @@ -0,0 +1,16 @@ +class TwitterUser { + TwitterUser({required this.id, required this.username, required this.name, this.description}); + + final String id; + final String username; + final String name; + final String? description; + + factory TwitterUser.fromJson(Map json) { + return TwitterUser( + id: json['id'] as String, + username: json['username'] as String, + name: json['name'] as String, + description: json['description'] as String?); + } +} diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart new file mode 100644 index 000000000..889f86c63 --- /dev/null +++ b/lib/utils/exception_handler.dart @@ -0,0 +1,105 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/main.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mailer/flutter_mailer.dart'; +import 'package:path_provider/path_provider.dart'; + +class ExceptionHandler { + static bool _hasError = false; + + static void _saveException(String? error, StackTrace? stackTrace) async { + final appDocDir = await getApplicationDocumentsDirectory(); + + final file = File('${appDocDir.path}/error.txt'); + final exception = { + "${DateTime.now()}": { + "Error": error, + "StackTrace": stackTrace.toString(), + } + }; + + const String separator = '''\n\n========================================================== + ==========================================================\n\n'''; + + await file.writeAsString( + jsonEncode(exception) + separator, + mode: FileMode.append, + ); + } + + static void _sendExceptionFile() async { + try { + final appDocDir = await getApplicationDocumentsDirectory(); + + final file = File('${appDocDir.path}/error.txt'); + + final MailOptions mailOptions = MailOptions( + subject: 'Mobile App Issue', + recipients: ['support@cakewallet.com'], + attachments: [file.path], + ); + + final result = await FlutterMailer.send(mailOptions); + + // Clear file content if the error was sent or saved. + // On android we can't know if it was sent or saved + if (result.name == MailerResponse.sent.name || + result.name == MailerResponse.saved.name || + result.name == MailerResponse.android.name) { + file.writeAsString("", mode: FileMode.write); + } + } catch (e, s) { + _saveException(e.toString(), s); + } + } + + static void onError(FlutterErrorDetails errorDetails) { + if (_ignoreError(errorDetails.exception.toString())) { + return; + } + + _saveException(errorDetails.exception.toString(), errorDetails.stack); + + if (_hasError) { + return; + } + _hasError = true; + + WidgetsBinding.instance.addPostFrameCallback( + (timeStamp) async { + await showPopUp( + context: navigatorKey.currentContext!, + builder: (context) { + return AlertWithTwoActions( + isDividerExist: true, + alertTitle: S.of(context).error, + alertContent: S.of(context).error_dialog_content, + rightButtonText: S.of(context).send, + leftButtonText: S.of(context).do_not_send, + actionRightButton: () { + Navigator.of(context).pop(); + _sendExceptionFile(); + }, + actionLeftButton: () { + Navigator.of(context).pop(); + }, + ); + }, + ); + + _hasError = false; + }, + ); + } + + /// Ignore User related errors or system errors + static bool _ignoreError(String error) { + return error.contains("errno = 103") || // SocketException: Software caused connection abort + error.contains("errno = 9"); // SocketException: Bad file descriptor (iOS socket exception) + } +} diff --git a/lib/view_model/ionia/ionia_payment_status_view_model.dart b/lib/view_model/ionia/ionia_payment_status_view_model.dart index 19f5f8537..8f43e0244 100644 --- a/lib/view_model/ionia/ionia_payment_status_view_model.dart +++ b/lib/view_model/ionia/ionia_payment_status_view_model.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/anypay/any_pay_chain.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter/foundation.dart'; import 'package:cake_wallet/ionia/ionia_service.dart'; @@ -39,6 +40,8 @@ abstract class IoniaPaymentStatusViewModelBase with Store { Timer? get timer => _timer; + bool get payingByBitcoin => paymentInfo.anyPayPayment.chain == AnyPayChain.btc; + Timer? _timer; @action diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index e111bb467..8222467bd 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -398,8 +398,7 @@ "biometric_auth_reason":"امسح بصمة إصبعك للمصادقة", "version":"الإصدار ${currentVersion}", - "openalias_alert_title":"تم ايجاد العنوان", - "openalias_alert_content":"سوف ترسل الأموال إلى\n${recipient_name}", + "extracted_address_content":"سوف ترسل الأموال إلى\n${recipient_name}", "card_address":"العنوان:", "buy":"اشتري", @@ -679,6 +678,9 @@ "tor_only":"Tor فقط", "unmatched_currencies": "عملة محفظتك الحالية لا تتطابق مع عملة QR الممسوحة ضوئيًا", "orbot_running_alert": "يرجى التأكد من تشغيل Orbot قبل الاتصال بهذه العقدة.", + "bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.", "send_to_this_address" : "أرسل ${currency} ${tag}إلى هذا العنوان", - "arrive_in_this_address" : "سيصل ${currency} ${tag}إلى هذا العنوان" + "arrive_in_this_address" : "سيصل ${currency} ${tag}إلى هذا العنوان", + "do_not_send": "لا ترسل", + "error_dialog_content": "عفوًا ، لقد حصلنا على بعض الخطأ.\n\nيرجى إرسال تقرير التعطل إلى فريق الدعم لدينا لتحسين التطبيق." } diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 018aa7e7c..6483305a4 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Scannen Sie Ihren Fingerabdruck zur Authentifizierung", "version" : "Version ${currentVersion}", - "openalias_alert_title" : "Adresse Erkannt", - "openalias_alert_content" : "Sie senden Geld an\n${recipient_name}", + "extracted_address_content" : "Sie senden Geld an\n${recipient_name}", "card_address" : "Adresse:", "buy" : "Kaufen", @@ -681,6 +680,7 @@ "orbot_running_alert": "Bitte stellen Sie sicher, dass Orbot läuft, bevor Sie sich mit diesem Knoten verbinden.", "contact_list_contacts": "Kontakte", "contact_list_wallets": "Meine Geldbörsen", + "bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.", "send_to_this_address" : "Senden Sie ${currency} ${tag}an diese Adresse", "arrive_in_this_address" : "${currency} ${tag}wird an dieser Adresse ankommen", "do_not_send": "Nicht senden", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 24baded77..655350304 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Scan your fingerprint to authenticate", "version" : "Version ${currentVersion}", - "openalias_alert_title" : "Address Detected", - "openalias_alert_content" : "You will be sending funds to\n${recipient_name}", + "extracted_address_content" : "You will be sending funds to\n${recipient_name}", "card_address" : "Address:", "buy" : "Buy", @@ -681,6 +680,7 @@ "orbot_running_alert": "Please make sure Orbot is running prior to connecting to this node.", "contact_list_contacts": "Contacts", "contact_list_wallets": "My Wallets", + "bitcoin_payments_require_1_confirmation": "Bitcoin payments require 1 confirmation, which can take 20 minutes or longer. Thanks for your patience! You will be emailed when the payment is confirmed.", "send_to_this_address" : "Send ${currency} ${tag}to this address", "arrive_in_this_address" : "${currency} ${tag}will arrive in this address", "do_not_send": "Don't send", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 82889df37..51d4bb0e3 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Escanee su huella digital para autenticar", "version" : "Versión ${currentVersion}", - "openalias_alert_title" : "Destinatario detectado", - "openalias_alert_content" : "Enviará fondos a\n${recipient_name}", + "extracted_address_content" : "Enviará fondos a\n${recipient_name}", "card_address" : "Dirección:", "buy" : "Comprar", @@ -681,6 +680,7 @@ "orbot_running_alert": "Asegúrese de que Orbot se esté ejecutando antes de conectarse a este nodo.", "contact_list_contacts": "Contactos", "contact_list_wallets": "Mis billeteras", + "bitcoin_payments_require_1_confirmation": "Los pagos de Bitcoin requieren 1 confirmación, que puede demorar 20 minutos o más. ¡Gracias por su paciencia! Se le enviará un correo electrónico cuando se confirme el pago.", "send_to_this_address" : "Enviar ${currency} ${tag}a esta dirección", "arrive_in_this_address" : "${currency} ${tag}llegará a esta dirección", "do_not_send": "no enviar", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index a3288f958..5e412d4f1 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -396,8 +396,7 @@ "biometric_auth_reason" : "Scannez votre empreinte digitale pour vous authentifier", "version" : "Version ${currentVersion}", - "openalias_alert_title" : "Adresse Détectée", - "openalias_alert_content" : "Vous allez envoyer des fonds à\n${recipient_name}", + "extracted_address_content" : "Vous allez envoyer des fonds à\n${recipient_name}", "card_address" : "Adresse :", "buy" : "Acheter", @@ -679,6 +678,7 @@ "orbot_running_alert": "Veuillez vous assurer qu'Orbot est en cours d'exécution avant de vous connecter à ce nœud.", "contact_list_contacts": "Contacts", "contact_list_wallets": "Mes portefeuilles (wallets)", + "bitcoin_payments_require_1_confirmation": "Les paiements Bitcoin nécessitent 1 confirmation, ce qui peut prendre 20 minutes ou plus. Merci pour votre patience! Vous serez averti par e-mail lorsque le paiement sera confirmé.", "send_to_this_address" : "Envoyez ${currency} ${tag}à cette adresse", "arrive_in_this_address" : "${currency} ${tag}arrivera à cette adresse", "do_not_send": "N'envoyez pas", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 7ec7de38d..1a3ddff74 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "प्रमाणित करने के लिए अपने फ़िंगरप्रिंट को स्कैन करें", "version" : "संस्करण ${currentVersion}", - "openalias_alert_title" : "पता मिला", - "openalias_alert_content" : "आपको धनराशि भेजी जाएगी\n${recipient_name}", + "extracted_address_content" : "आपको धनराशि भेजी जाएगी\n${recipient_name}", "card_address" : "पता:", "buy" : "खरीदें", @@ -681,6 +680,7 @@ "orbot_running_alert": "कृपया सुनिश्चित करें कि इस नोड से कनेक्ट करने से पहले Orbot चल रहा है।", "contact_list_contacts": "संपर्क", "contact_list_wallets": "मेरा बटुआ", + "bitcoin_payments_require_1_confirmation": "बिटकॉइन भुगतान के लिए 1 पुष्टिकरण की आवश्यकता होती है, जिसमें 20 मिनट या अधिक समय लग सकता है। आपके धैर्य के लिए धन्यवाद! भुगतान की पुष्टि होने पर आपको ईमेल किया जाएगा।", "send_to_this_address" : "इस पते पर ${currency} ${tag}भेजें", "arrive_in_this_address" : "${currency} ${tag}इस पते पर पहुंचेंगे", "do_not_send": "मत भेजो", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 79bad1e66..c012ca72d 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Skenirajte svoj otisak prsta za autentifikaciju", "version" : "Verzija ${currentVersion}", - "openalias_alert_title" : "Otkrivena je adresa", - "openalias_alert_content" : "Poslat ćete sredstva primatelju\n${recipient_name}", + "extracted_address_content" : "Poslat ćete sredstva primatelju\n${recipient_name}", "card_address" : "Adresa:", "buy" : "Kupi", @@ -681,6 +680,7 @@ "orbot_running_alert": "Provjerite radi li Orbot prije spajanja na ovaj čvor.", "contact_list_contacts": "Kontakti", "contact_list_wallets": "Moji novčanici", + "bitcoin_payments_require_1_confirmation": "Bitcoin plaćanja zahtijevaju 1 potvrdu, što može potrajati 20 minuta ili dulje. Hvala na Vašem strpljenju! Dobit ćete e-poruku kada plaćanje bude potvrđeno.", "send_to_this_address" : "Pošaljite ${currency} ${tag}na ovu adresu", "arrive_in_this_address" : "${currency} ${tag}će stići na ovu adresu", "do_not_send": "Ne šalji", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 28f502cd6..4048e0866 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Scansiona la tua impronta per autenticarti", "version" : "Versione ${currentVersion}", - "openalias_alert_title" : "Indirizzo Rilevato", - "openalias_alert_content" : "Invierai i tuoi fondi a\n${recipient_name}", + "extracted_address_content" : "Invierai i tuoi fondi a\n${recipient_name}", "card_address" : "Indirizzo:", "buy" : "Comprare", @@ -681,6 +680,7 @@ "orbot_running_alert": "Assicurati che Orbot sia in esecuzione prima di connetterti a questo nodo.", "contact_list_contacts": "Contatti", "contact_list_wallets": "I miei portafogli", + "bitcoin_payments_require_1_confirmation": "I pagamenti in bitcoin richiedono 1 conferma, che può richiedere 20 minuti o più. Grazie per la vostra pazienza! Riceverai un'e-mail quando il pagamento sarà confermato.", "send_to_this_address" : "Invia ${currency} ${tag}a questo indirizzo", "arrive_in_this_address" : "${currency} ${tag}arriverà a questo indirizzo", "do_not_send": "Non inviare", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index f1a05b987..831056785 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "प指紋をスキャンして認証する", "version" : "バージョン ${currentVersion}", - "openalias_alert_title" : "アドレスが検出されました", - "openalias_alert_content" : "に送金します\n${recipient_name}", + "extracted_address_content" : "に送金します\n${recipient_name}", "card_address" : "住所:", "buy" : "購入", @@ -681,6 +680,7 @@ "orbot_running_alert": "このノードに接続する前に、Orbot が実行されていることを確認してください", "contact_list_contacts": "連絡先", "contact_list_wallets": "マイウォレット", + "bitcoin_payments_require_1_confirmation": "ビットコインの支払いには 1 回の確認が必要で、これには 20 分以上かかる場合があります。お待ち頂きまして、ありがとうございます!支払いが確認されると、メールが送信されます。", "send_to_this_address" : "${currency} ${tag}をこのアドレスに送金", "arrive_in_this_address" : "${currency} ${tag}はこの住所に到着します", "do_not_send": "送信しない", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index fa8565e6d..2c2d564ae 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "지문을 스캔하여 인증", "version" : "버전 ${currentVersion}", - "openalias_alert_title" : "주소 감지됨", - "openalias_alert_content" : "당신은에 자금을 보낼 것입니다\n${recipient_name}", + "extracted_address_content" : "당신은에 자금을 보낼 것입니다\n${recipient_name}", "card_address" : "주소:", "buy" : "구입", @@ -681,6 +680,7 @@ "orbot_running_alert": "이 노드에 연결하기 전에 Orbot이 실행 중인지 확인하십시오.", "contact_list_contacts": "콘택트 렌즈", "contact_list_wallets": "내 지갑", + "bitcoin_payments_require_1_confirmation": "비트코인 결제는 1번의 확인이 필요하며 20분 이상이 소요될 수 있습니다. 기다려 주셔서 감사합니다! 결제가 확인되면 이메일이 전송됩니다.", "send_to_this_address" : "이 주소로 ${currency} ${tag}송금", "arrive_in_this_address" : "${currency} ${tag}이(가) 이 주소로 도착합니다", "do_not_send": "보내지 마세요", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 0979948c1..401f5af3a 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "စစ်မှန်ကြောင်းအထောက်အထားပြရန် သင့်လက်ဗွေကို စကန်ဖတ်ပါ။", "version" : "ဗားရှင်း ${currentVersion}", - "openalias_alert_title" : "လိပ်စာကို ရှာတွေ့သည်။", - "openalias_alert_content" : "သင်သည် \n${recipient_name} သို့ ရန်ပုံငွေများ ပေးပို့ပါမည်", + "extracted_address_content" : "သင်သည် \n${recipient_name} သို့ ရန်ပုံငွေများ ပေးပို့ပါမည်", "card_address" : "လိပ်စာ-", "buy" : "ဝယ်ပါ။", @@ -678,8 +677,12 @@ "enabled" : "ဖွင့်ထားသည်။", "tor_only" : "Tor သာ", "unmatched_currencies" : "သင့်လက်ရှိပိုက်ဆံအိတ်၏ငွေကြေးသည် စကင်ဖတ်ထားသော QR နှင့် မကိုက်ညီပါ။", + "orbot_running_alert": "ဤ node သို့မချိတ်ဆက်မီ Orbot အလုပ်လုပ်နေကြောင်း သေချာပါစေ။", "contact_list_contacts" : "အဆက်အသွယ်များ", "contact_list_wallets" : "ကျွန်ုပ်၏ ပိုက်ဆံအိတ်များ", + "bitcoin_payments_require_1_confirmation": "Bitcoin ငွေပေးချေမှုများသည် မိနစ် 20 သို့မဟုတ် ထို့ထက်ပိုကြာနိုင်သည် 1 အတည်ပြုချက် လိုအပ်သည်။ မင်းရဲ့စိတ်ရှည်မှုအတွက် ကျေးဇူးတင်ပါတယ်။ ငွေပေးချေမှုကို အတည်ပြုပြီးသောအခါ သင့်ထံ အီးမေးလ်ပို့ပါမည်။", "send_to_this_address" : "ဤလိပ်စာသို့ ${currency} ${tag}သို့ ပို့ပါ။", - "arrive_in_this_address" : "${currency} ${tag}ဤလိပ်စာသို့ ရောက်ရှိပါမည်။" + "arrive_in_this_address" : "${currency} ${tag}ဤလိပ်စာသို့ ရောက်ရှိပါမည်။", + "do_not_send": "မပို့ပါနှင့်", + "error_dialog_content": "အိုး၊ ကျွန်ုပ်တို့တွင် အမှားအယွင်းအချို့ရှိသည်။\n\nအပလီကေးရှင်းကို ပိုမိုကောင်းမွန်စေရန်အတွက် ပျက်စီးမှုအစီရင်ခံစာကို ကျွန်ုပ်တို့၏ပံ့ပိုးကူညီရေးအဖွဲ့ထံ ပေးပို့ပါ။" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index c10b1722f..a3a043b75 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Scan uw vingerafdruk om te verifiëren", "version" : "Versie ${currentVersion}", - "openalias_alert_title" : "Adres Gedetecteerd", - "openalias_alert_content" : "U stuurt geld naar\n${recipient_name}", + "extracted_address_content" : "U stuurt geld naar\n${recipient_name}", "card_address" : "Adres:", "buy" : "Kopen", @@ -681,6 +680,7 @@ "orbot_running_alert": "Zorg ervoor dat Orbot actief is voordat u verbinding maakt met dit knooppunt.", "contact_list_contacts": "Contacten", "contact_list_wallets": "Mijn portefeuilles", + "bitcoin_payments_require_1_confirmation": "Bitcoin-betalingen vereisen 1 bevestiging, wat 20 minuten of langer kan duren. Dank voor uw geduld! U ontvangt een e-mail wanneer de betaling is bevestigd.", "send_to_this_address" : "Stuur ${currency} ${tag}naar dit adres", "arrive_in_this_address" : "${currency} ${tag}komt aan op dit adres", "do_not_send": "Niet sturen", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 72b93a08b..3c9b60eaa 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Zeskanuj swój odcisk palca, aby uwierzytelnić", "version" : "Wersja ${currentVersion}", - "openalias_alert_title" : "Wykryto Adres", - "openalias_alert_content" : "Wysyłasz środki na\n${recipient_name}", + "extracted_address_content" : "Wysyłasz środki na\n${recipient_name}", "card_address" : "Adres:", "buy" : "Kup", @@ -681,6 +680,7 @@ "orbot_running_alert": "Upewnij się, że Orbot działa przed połączeniem z tym węzłem.", "contact_list_contacts": "Łączność", "contact_list_wallets": "Moje portfele", + "bitcoin_payments_require_1_confirmation": "Płatności Bitcoin wymagają 1 potwierdzenia, co może zająć 20 minut lub dłużej. Dziękuję za cierpliwość! Otrzymasz wiadomość e-mail, gdy płatność zostanie potwierdzona.", "send_to_this_address" : "Wyślij ${currency} ${tag}na ten adres", "arrive_in_this_address" : "${currency} ${tag}dotrze na ten adres", "do_not_send": "Nie wysyłaj", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 4ed4df754..39cef5f67 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Digitalize sua impressão digital para autenticar", "version" : "Versão ${currentVersion}", - "openalias_alert_title" : "Endereço Detectado", - "openalias_alert_content" : "Você enviará fundos para\n${recipient_name}", + "extracted_address_content" : "Você enviará fundos para\n${recipient_name}", "card_address" : "Endereço:", "buy" : "Comprar", @@ -680,6 +679,7 @@ "orbot_running_alert": "Certifique-se de que o Orbot esteja em execução antes de se conectar a este nó.", "contact_list_contacts": "Contatos", "contact_list_wallets": "minhas carteiras", + "bitcoin_payments_require_1_confirmation": "Os pagamentos em Bitcoin exigem 1 confirmação, o que pode levar 20 minutos ou mais. Obrigado pela sua paciência! Você receberá um e-mail quando o pagamento for confirmado.", "send_to_this_address" : "Envie ${currency} ${tag}para este endereço", "arrive_in_this_address" : "${currency} ${tag}chegará neste endereço", "do_not_send": "não envie", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 8482a1bda..50c183f1b 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Отсканируйте свой отпечаток пальца для аутентификации", "version" : "Версия ${currentVersion}", - "openalias_alert_title" : "Адрес Обнаружен", - "openalias_alert_content" : "Вы будете отправлять средства\n${recipient_name}", + "extracted_address_content" : "Вы будете отправлять средства\n${recipient_name}", "card_address" : "Адрес:", "buy" : "Купить", @@ -681,6 +680,7 @@ "orbot_running_alert": "Перед подключением к этому узлу убедитесь, что Orbot запущен.", "contact_list_contacts": "Контакты", "contact_list_wallets": "Мои кошельки", + "bitcoin_payments_require_1_confirmation": "Биткойн-платежи требуют 1 подтверждения, что может занять 20 минут или дольше. Спасибо тебе за твое терпение! Вы получите электронное письмо, когда платеж будет подтвержден.", "send_to_this_address" : "Отправить ${currency} ${tag}на этот адрес", "arrive_in_this_address" : "${currency} ${tag}придет на этот адрес", "do_not_send": "Не отправлять", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 8e108f6c7..2ecf1bf75 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -396,8 +396,7 @@ "biometric_auth_reason" : "สแกนลายนิ้วมือของคุณเพื่อยืนยันตัวตน", "version" : "เวอร์ชัน ${currentVersion}", - "openalias_alert_title" : "พบที่อยู่", - "openalias_alert_content" : "คุณกำลังจะส่งเงินไปยัง\n${recipient_name}", + "extracted_address_content" : "คุณกำลังจะส่งเงินไปยัง\n${recipient_name}", "card_address" : "ที่อยู่:", "buy" : "ซื้อ", @@ -679,6 +678,7 @@ "orbot_running_alert": "โปรดตรวจสอบว่า Orbot กำลังทำงานก่อนที่จะเชื่อมต่อกับโหนดนี้", "contact_list_contacts": "ติดต่อ", "contact_list_wallets": "กระเป๋าเงินของฉัน", + "bitcoin_payments_require_1_confirmation": "การชำระเงินด้วย Bitcoin ต้องการการยืนยัน 1 ครั้ง ซึ่งอาจใช้เวลา 20 นาทีหรือนานกว่านั้น ขอบคุณสำหรับความอดทนของคุณ! คุณจะได้รับอีเมลเมื่อการชำระเงินได้รับการยืนยัน", "send_to_this_address" : "ส่ง ${currency} ${tag}ไปยังที่อยู่นี้", "arrive_in_this_address" : "${currency} ${tag}จะมาถึงที่อยู่นี้", "do_not_send": "อย่าส่ง", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 46f7213d4..08dec0649 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "Kimlik doğrulaması için parmak izini okutun", "version" : "Sürüm ${currentVersion}", - "openalias_alert_title" : "Adres tespit edildi", - "openalias_alert_content" : "Parayı buraya gönderceksin:\n${recipient_name}", + "extracted_address_content" : "Parayı buraya gönderceksin:\n${recipient_name}", "card_address" : "Adres:", "buy" : "Alış", @@ -678,8 +677,12 @@ "enabled": "Etkin", "tor_only": "Yalnızca Tor", "unmatched_currencies": "Mevcut cüzdanınızın para birimi taranan QR ile eşleşmiyor", + "orbot_running_alert": "Lütfen bu düğüme bağlanmadan önce Orbot'un çalıştığından emin olun.", "contact_list_contacts": "Rehberim", "contact_list_wallets": "Cüzdanlarım", + "bitcoin_payments_require_1_confirmation": "Bitcoin ödemeleri, 20 dakika veya daha uzun sürebilen 1 onay gerektirir. Sabrınız için teşekkürler! Ödeme onaylandığında e-posta ile bilgilendirileceksiniz.", "send_to_this_address" : "Bu adrese ${currency} ${tag}gönder", - "arrive_in_this_address" : "${currency} ${tag}bu adrese ulaşacak" + "arrive_in_this_address" : "${currency} ${tag}bu adrese ulaşacak", + "do_not_send": "Gönderme", + "error_dialog_content": "Hay aksi, bir hatamız var.\n\nUygulamayı daha iyi hale getirmek için lütfen kilitlenme raporunu destek ekibimize gönderin." } diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 2ed7c7fdb..19cf7946d 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -397,8 +397,7 @@ "biometric_auth_reason" : "Відскануйте свій відбиток пальця для аутентифікації", "version" : "Версія ${currentVersion}", - "openalias_alert_title" : "Виявлено адресу", - "openalias_alert_content" : "Ви будете відправляти кошти\n${recipient_name}", + "extracted_address_content" : "Ви будете відправляти кошти\n${recipient_name}", "card_address" : "Адреса:", "buy" : "Купити", @@ -680,6 +679,7 @@ "orbot_running_alert": "Перед підключенням до цього вузла переконайтеся, що Orbot запущено.", "contact_list_contacts": "Контакти", "contact_list_wallets": "Мої гаманці", + "bitcoin_payments_require_1_confirmation": "Платежі Bitcoin потребують 1 підтвердження, яке може зайняти 20 хвилин або більше. Дякую за Ваше терпіння! Ви отримаєте електронний лист, коли платіж буде підтверджено.", "send_to_this_address" : "Надіслати ${currency} ${tag}на цю адресу", "arrive_in_this_address" : "${currency} ${tag}надійде на цю адресу", "do_not_send": "Не надсилайте", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 7355ac3bd..52fc9cd7a 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -398,8 +398,7 @@ "biometric_auth_reason" : "扫描指纹进行身份认证", "version" : "版本 ${currentVersion}", - "openalias_alert_title" : "检测到地址", - "openalias_alert_content" : "您将汇款至\n${recipient_name}", + "extracted_address_content" : "您将汇款至\n${recipient_name}", "card_address" : "地址:", "buy" : "购买", @@ -679,6 +678,7 @@ "orbot_running_alert": "请确保 Orbot 在连接到此节点之前正在运行。", "contact_list_contacts": "联系人", "contact_list_wallets": "我的钱包", + "bitcoin_payments_require_1_confirmation": "比特币支付需要 1 次确认,这可能需要 20 分钟或更长时间。谢谢你的耐心!确认付款后,您将收到电子邮件。", "send_to_this_address" : "发送 ${currency} ${tag}到这个地址", "arrive_in_this_address" : "${currency} ${tag}将到达此地址", "do_not_send": "不要发送", diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index c74d97c0f..0de5985d5 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -14,14 +14,14 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.2.5" -MONERO_COM_BUILD_NUMBER=36 +MONERO_COM_VERSION="1.2.6" +MONERO_COM_BUILD_NUMBER=37 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.5.6" -CAKEWALLET_BUILD_NUMBER=141 +CAKEWALLET_VERSION="4.5.7" +CAKEWALLET_BUILD_NUMBER=143 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 8588bfcb2..f0e701691 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.2.5" -MONERO_COM_BUILD_NUMBER=33 +MONERO_COM_VERSION="1.2.6" +MONERO_COM_BUILD_NUMBER=34 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.5.6" -CAKEWALLET_BUILD_NUMBER=138 +CAKEWALLET_VERSION="4.5.7" +CAKEWALLET_BUILD_NUMBER=139 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index ff74da8a7..8ac236088 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -6,17 +6,12 @@ class SecretKey { static final base = [ SecretKey('salt', () => hex.encode(encrypt.Key.fromSecureRandom(16).bytes)), - SecretKey('keychainSalt', - () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)), + SecretKey('keychainSalt', () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)), SecretKey('key', () => hex.encode(encrypt.Key.fromSecureRandom(16).bytes)), - SecretKey( - 'walletSalt', () => hex.encode(encrypt.Key.fromSecureRandom(4).bytes)), - SecretKey( - 'shortKey', () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)), - SecretKey( - 'backupSalt', () => hex.encode(encrypt.Key.fromSecureRandom(8).bytes)), - SecretKey('backupKeychainSalt', - () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)), + SecretKey('walletSalt', () => hex.encode(encrypt.Key.fromSecureRandom(4).bytes)), + SecretKey('shortKey', () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)), + SecretKey('backupSalt', () => hex.encode(encrypt.Key.fromSecureRandom(8).bytes)), + SecretKey('backupKeychainSalt', () => hex.encode(encrypt.Key.fromSecureRandom(12).bytes)), SecretKey('changeNowApiKey', () => ''), SecretKey('wyreSecretKey', () => ''), SecretKey('wyreApiKey', () => ''), @@ -30,6 +25,7 @@ class SecretKey { SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''), SecretKey('trocadorApiKey', () => ''), + SecretKey('twitterBearerToken', () => ''), ]; final String name;