CW-224-Allow-Monero-proxy-in-node-settings (#966)

* update setupNodeSync func * minor fixes * localization * add request node with proxy * formatting

Serhii committed Aug 2, 2023 at 14:48 UTC d5c00129646c3a3e24c36526a9456682641172b8
38 files changed +180 -27
cw_core/lib/node.dart
+29 -4
@@ -18,6 +18,7 @@ class Node extends HiveObject with Keyable {
18 this.password,
19 this.useSSL,
20 this.trusted = false,
21 + this.socksProxyAddress,
22 String? uri,
23 WalletType? type,}) {
24 if (uri != null) {
@@ -33,7 +34,8 @@ class Node extends HiveObject with Keyable {
34 login = map['login'] as String?,
35 password = map['password'] as String?,
36 useSSL = map['useSSL'] as bool?,
36 - trusted = map['trusted'] as bool? ?? false;
37 + trusted = map['trusted'] as bool? ?? false,
38 + socksProxyAddress = map['socksProxyPort'] as String?;
39
40 static const typeId = 1;
41 static const boxName = 'Nodes';
@@ -56,8 +58,13 @@ class Node extends HiveObject with Keyable {
58 @HiveField(5, defaultValue: false)
59 bool trusted;
60
61 + @HiveField(6)
62 + String? socksProxyAddress;
63 +
64 bool get isSSL => useSSL ?? false;
65
66 + bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
67 +
68 Uri get uri {
69 switch (type) {
70 case WalletType.monero:
@@ -81,7 +88,8 @@ class Node extends HiveObject with Keyable {
88 other.password == password &&
89 other.typeRaw == typeRaw &&
90 other.useSSL == useSSL &&
84 - other.trusted == trusted);
91 + other.trusted == trusted &&
92 + other.socksProxyAddress == socksProxyAddress);
93
94 @override
95 int get hashCode =>
@@ -90,7 +98,8 @@ class Node extends HiveObject with Keyable {
98 password.hashCode ^
99 typeRaw.hashCode ^
100 useSSL.hashCode ^
93 - trusted.hashCode;
101 + trusted.hashCode ^
102 + socksProxyAddress.hashCode;
103
104 @override
105 dynamic get keyIndex {
@@ -108,7 +117,7 @@ class Node extends HiveObject with Keyable {
117 try {
118 switch (type) {
119 case WalletType.monero:
111 - return requestMoneroNode();
120 + return useSocksProxy ? requestNodeWithProxy(socksProxyAddress ?? '') : requestMoneroNode();
121 case WalletType.bitcoin:
122 return requestElectrumServer();
123 case WalletType.litecoin:
@@ -159,6 +168,22 @@ class Node extends HiveObject with Keyable {
168 }
169 }
170
171 + Future<bool> requestNodeWithProxy(String proxy) async {
172 +
173 + if (proxy.isEmpty || !proxy.contains(':')) {
174 + return false;
175 + }
176 + final proxyAddress = proxy.split(':')[0];
177 + final proxyPort = int.parse(proxy.split(':')[1]);
178 + try {
179 + final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5));
180 + socket.destroy();
181 + return true;
182 + } catch (_) {
183 + return false;
184 + }
185 + }
186 +
187 Future<bool> requestElectrumServer() async {
188 try {
189 await SecureSocket.connect(uri.host, uri.port,
cw_haven/lib/api/signatures.dart
+1 -1
@@ -39,7 +39,7 @@ typedef get_node_height = Int64 Function();
39 typedef is_connected = Int8 Function();
40
41 typedef setup_node = Int8 Function(
42 - Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>);
42 + Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>?, Pointer<Utf8>);
43
44 typedef start_refresh = Void Function();
45
cw_haven/lib/api/types.dart
+1 -1
@@ -39,7 +39,7 @@ typedef GetNodeHeight = int Function();
39 typedef IsConnected = int Function();
40
41 typedef SetupNode = int Function(
42 - Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>);
42 + Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>?, Pointer<Utf8>);
43
44 typedef StartRefresh = void Function();
45
cw_haven/lib/api/wallet.dart
+14 -3
@@ -154,9 +154,11 @@ bool setupNodeSync(
154 String? login,
155 String? password,
156 bool useSSL = false,
157 - bool isLightWallet = false}) {
157 + bool isLightWallet = false,
158 + String? socksProxyAddress}) {
159 final addressPointer = address.toNativeUtf8();
160 Pointer<Utf8>? loginPointer;
161 + Pointer<Utf8>? socksProxyAddressPointer;
162 Pointer<Utf8>? passwordPointer;
163
164 if (login != null) {
@@ -167,6 +169,10 @@ bool setupNodeSync(
169 passwordPointer = password.toNativeUtf8();
170 }
171
172 + if (socksProxyAddress != null) {
173 + socksProxyAddressPointer = socksProxyAddress.toNativeUtf8();
174 + }
175 +
176 final errorMessagePointer = ''.toNativeUtf8();
177 final isSetupNode = setupNodeNative(
178 addressPointer,
@@ -174,6 +180,7 @@ bool setupNodeSync(
180 passwordPointer,
181 _boolToInt(useSSL),
182 _boolToInt(isLightWallet),
183 + socksProxyAddressPointer,
184 errorMessagePointer) !=
185 0;
186
@@ -323,13 +330,15 @@ bool _setupNodeSync(Map args) {
330 final password = (args['password'] ?? '') as String;
331 final useSSL = args['useSSL'] as bool;
332 final isLightWallet = args['isLightWallet'] as bool;
333 + final socksProxyAddress = (args['socksProxyAddress'] ?? '') as String;
334
335 return setupNodeSync(
336 address: address,
337 login: login,
338 password: password,
339 useSSL: useSSL,
332 - isLightWallet: isLightWallet);
340 + isLightWallet: isLightWallet,
341 + socksProxyAddress: socksProxyAddress);
342 }
343
344 bool _isConnected(Object _) => isConnectedSync();
@@ -343,13 +352,15 @@ Future<void> setupNode(
352 String? login,
353 String? password,
354 bool useSSL = false,
355 + String? socksProxyAddress,
356 bool isLightWallet = false}) =>
357 compute<Map<String, Object?>, void>(_setupNodeSync, {
358 'address': address,
359 'login': login,
360 'password': password,
361 'useSSL': useSSL,
352 - 'isLightWallet': isLightWallet
362 + 'isLightWallet': isLightWallet,
363 + 'socksProxyAddress': socksProxyAddress
364 });
365
366 Future<void> store() => compute<int, void>(_storeSync, 0);
cw_haven/lib/haven_wallet.dart
+2 -1
@@ -125,7 +125,8 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
125 login: node.login,
126 password: node.password,
127 useSSL: node.useSSL ?? false,
128 - isLightWallet: false); // FIXME: hardcoded value
128 + isLightWallet: false, // FIXME: hardcoded value
129 + socksProxyAddress: node.socksProxyAddress);
130
131 haven_wallet.setTrustedDaemon(node.trusted);
132 syncStatus = ConnectedSyncStatus();
cw_monero/ios/Classes/monero_api.cpp
+9 -2
@@ -3,6 +3,7 @@
3 #include <chrono>
4 #include <functional>
5 #include <iostream>
6 +#include <fstream>
7 #include <unistd.h>
8 #include <mutex>
9 #include "thread"
@@ -405,13 +406,14 @@ extern "C"
406 return is_connected;
407 }
408
408 - bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error)
409 + bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error)
410 {
411 nice(19);
412 Monero::Wallet *wallet = get_current_wallet();
413
414 std::string _login = "";
415 std::string _password = "";
416 + std::string _socksProxyAddress = "";
417
418 if (login != nullptr)
419 {
@@ -423,7 +425,12 @@ extern "C"
425 _password = std::string(password);
426 }
427
426 - bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet);
428 + if (socksProxyAddress != nullptr)
429 + {
430 + _socksProxyAddress = std::string(socksProxyAddress);
431 + }
432 +
433 + bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress);
434
435 if (!inited)
436 {
cw_monero/lib/api/signatures.dart
+1 -1
@@ -35,7 +35,7 @@ typedef get_node_height = Int64 Function();
35 typedef is_connected = Int8 Function();
36
37 typedef setup_node = Int8 Function(
38 - Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>);
38 + Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>?, Pointer<Utf8>);
39
40 typedef start_refresh = Void Function();
41
cw_monero/lib/api/types.dart
+1 -1
@@ -35,7 +35,7 @@ typedef GetNodeHeight = int Function();
35 typedef IsConnected = int Function();
36
37 typedef SetupNode = int Function(
38 - Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>);
38 + Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>?, Pointer<Utf8>);
39
40 typedef StartRefresh = void Function();
41
cw_monero/lib/api/wallet.dart
+14 -3
@@ -158,9 +158,11 @@ bool setupNodeSync(
158 String? login,
159 String? password,
160 bool useSSL = false,
161 - bool isLightWallet = false}) {
161 + bool isLightWallet = false,
162 + String? socksProxyAddress}) {
163 final addressPointer = address.toNativeUtf8();
164 Pointer<Utf8>? loginPointer;
165 + Pointer<Utf8>? socksProxyAddressPointer;
166 Pointer<Utf8>? passwordPointer;
167
168 if (login != null) {
@@ -171,6 +173,10 @@ bool setupNodeSync(
173 passwordPointer = password.toNativeUtf8();
174 }
175
176 + if (socksProxyAddress != null) {
177 + socksProxyAddressPointer = socksProxyAddress.toNativeUtf8();
178 + }
179 +
180 final errorMessagePointer = ''.toNativeUtf8();
181 final isSetupNode = setupNodeNative(
182 addressPointer,
@@ -178,6 +184,7 @@ bool setupNodeSync(
184 passwordPointer,
185 _boolToInt(useSSL),
186 _boolToInt(isLightWallet),
187 + socksProxyAddressPointer,
188 errorMessagePointer) !=
189 0;
190
@@ -328,13 +335,15 @@ bool _setupNodeSync(Map<String, Object?> args) {
335 final password = (args['password'] ?? '') as String;
336 final useSSL = args['useSSL'] as bool;
337 final isLightWallet = args['isLightWallet'] as bool;
338 + final socksProxyAddress = (args['socksProxyAddress'] ?? '') as String;
339
340 return setupNodeSync(
341 address: address,
342 login: login,
343 password: password,
344 useSSL: useSSL,
337 - isLightWallet: isLightWallet);
345 + isLightWallet: isLightWallet,
346 + socksProxyAddress: socksProxyAddress);
347 }
348
349 bool _isConnected(Object _) => isConnectedSync();
@@ -348,13 +357,15 @@ Future<void> setupNode(
357 String? login,
358 String? password,
359 bool useSSL = false,
360 + String? socksProxyAddress,
361 bool isLightWallet = false}) =>
362 compute<Map<String, Object?>, void>(_setupNodeSync, {
363 'address': address,
364 'login': login ,
365 'password': password,
366 'useSSL': useSSL,
357 - 'isLightWallet': isLightWallet
367 + 'isLightWallet': isLightWallet,
368 + 'socksProxyAddress': socksProxyAddress
369 });
370
371 Future<void> store() => compute<int, void>(_storeSync, 0);
cw_monero/lib/monero_wallet.dart
+2 -1
@@ -138,7 +138,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
138 login: node.login,
139 password: node.password,
140 useSSL: node.isSSL,
141 - isLightWallet: false); // FIXME: hardcoded value
141 + isLightWallet: false, // FIXME: hardcoded value
142 + socksProxyAddress: node.socksProxyAddress);
143
144 monero_wallet.setTrustedDaemon(node.trusted);
145 syncStatus = ConnectedSyncStatus();
lib/core/socks_proxy_node_address_validator.dart new
+10
@@ -0,0 +1,10 @@
1 +import 'package:cake_wallet/core/validator.dart';
2 +import 'package:cake_wallet/generated/i18n.dart';
3 +
4 +class SocksProxyNodeAddressValidator extends TextValidator {
5 + SocksProxyNodeAddressValidator()
6 + : super(
7 + errorMessage: S.current.error_text_node_proxy_address,
8 + pattern:
9 + '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+\$');
10 +}
lib/src/screens/nodes/widgets/node_form.dart
+45 -2
@@ -1,5 +1,6 @@
1 import 'package:cake_wallet/core/node_address_validator.dart';
2 import 'package:cake_wallet/core/node_port_validator.dart';
3 +import 'package:cake_wallet/core/socks_proxy_node_address_validator.dart';
4 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
5 import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
6 import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
@@ -17,7 +18,8 @@ class NodeForm extends StatelessWidget {
18 }) : _addressController = TextEditingController(text: editingNode?.uri.host.toString()),
19 _portController = TextEditingController(text: editingNode?.uri.port.toString()),
20 _loginController = TextEditingController(text: editingNode?.login),
20 - _passwordController = TextEditingController(text: editingNode?.password) {
21 + _passwordController = TextEditingController(text: editingNode?.password),
22 + _socksAddressController = TextEditingController(text: editingNode?.socksProxyAddress){
23 if (editingNode != null) {
24 nodeViewModel
25 ..setAddress((editingNode!.uri.host.toString()))
@@ -25,7 +27,9 @@ class NodeForm extends StatelessWidget {
27 ..setPassword((editingNode!.password ?? ''))
28 ..setLogin((editingNode!.login ?? ''))
29 ..setSSL((editingNode!.isSSL))
28 - ..setTrusted((editingNode!.trusted));
30 + ..setTrusted((editingNode!.trusted))
31 + ..setSocksProxy((editingNode!.useSocksProxy))
32 + ..setSocksProxyAddress((editingNode!.socksProxyAddress ?? ''));
33 }
34 if (nodeViewModel.hasAuthCredentials) {
35 reaction((_) => nodeViewModel.login, (String login) {
@@ -45,6 +49,7 @@ class NodeForm extends StatelessWidget {
49 _portController.addListener(() => nodeViewModel.port = _portController.text);
50 _loginController.addListener(() => nodeViewModel.login = _loginController.text);
51 _passwordController.addListener(() => nodeViewModel.password = _passwordController.text);
52 + _socksAddressController.addListener(() => nodeViewModel.socksProxyAddress = _socksAddressController.text);
53 }
54
55 final NodeCreateOrEditViewModel nodeViewModel;
@@ -55,6 +60,7 @@ class NodeForm extends StatelessWidget {
60 final TextEditingController _portController;
61 final TextEditingController _loginController;
62 final TextEditingController _passwordController;
63 + final TextEditingController _socksAddressController;
64
65 @override
66 Widget build(BuildContext context) {
@@ -138,6 +144,43 @@ class NodeForm extends StatelessWidget {
144 ],
145 ),
146 ),
147 + Observer(
148 + builder: (_) => Column(
149 + children: [
150 + Padding(
151 + padding: EdgeInsets.only(top: 20),
152 + child: Row(
153 + mainAxisAlignment: MainAxisAlignment.start,
154 + mainAxisSize: MainAxisSize.max,
155 + children: [
156 + StandardCheckbox(
157 + value: nodeViewModel.useSocksProxy,
158 + onChanged: (value) {
159 + if (!value) {
160 + _socksAddressController.text = '';
161 + }
162 + nodeViewModel.useSocksProxy = value;
163 + },
164 + caption: 'SOCKS Proxy',
165 + ),
166 + ],
167 + ),
168 + ),
169 + if (nodeViewModel.useSocksProxy) ...[
170 + SizedBox(height: 10.0),
171 + Row(
172 + children: <Widget>[
173 + Expanded(
174 + child: BaseTextFormField(
175 + controller: _socksAddressController,
176 + hintText: '[<ip>:]<port>',
177 + validator: SocksProxyNodeAddressValidator(),
178 + ))
179 + ],
180 + ),
181 + ]
182 + ],
183 + )),
184 ]
185 ],
186 ),
lib/view_model/node_list/node_create_or_edit_view_model.dart
+21 -3
@@ -19,7 +19,9 @@ abstract class NodeCreateOrEditViewModelBase with Store {
19 port = '',
20 login = '',
21 password = '',
22 - trusted = false;
22 + trusted = false,
23 + useSocksProxy = false,
24 + socksProxyAddress = '';
25
26 @observable
27 ExecutionState state;
@@ -45,6 +47,12 @@ abstract class NodeCreateOrEditViewModelBase with Store {
47 @observable
48 bool trusted;
49
50 + @observable
51 + bool useSocksProxy;
52 +
53 + @observable
54 + String socksProxyAddress;
55 +
56 @computed
57 bool get isReady => address.isNotEmpty && port.isNotEmpty;
58
@@ -73,6 +81,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
81 password = '';
82 useSSL = false;
83 trusted = false;
84 + useSocksProxy = false;
85 + socksProxyAddress = '';
86 }
87
88 @action
@@ -93,6 +103,12 @@ abstract class NodeCreateOrEditViewModelBase with Store {
103 @action
104 void setTrusted(bool val) => trusted = val;
105
106 + @action
107 + void setSocksProxy(bool val) => useSocksProxy = val;
108 +
109 + @action
110 + void setSocksProxyAddress(String val) => socksProxyAddress = val;
111 +
112 @action
113 Future<void> save({Node? editingNode, bool saveAsCurrent = false}) async {
114 final node = Node(
@@ -101,7 +117,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
117 login: login,
118 password: password,
119 useSSL: useSSL,
104 - trusted: trusted);
120 + trusted: trusted,
121 + socksProxyAddress: socksProxyAddress);
122 try {
123 state = IsExecutingState();
124 if (editingNode != null) {
@@ -130,7 +147,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
147 login: login,
148 password: password,
149 useSSL: useSSL,
133 - trusted: trusted);
150 + trusted: trusted,
151 + socksProxyAddress: socksProxyAddress);
152 try {
153 connectionState = IsExecutingState();
154 final isAlive = await node.requestNode();
res/values/strings_ar.arb
+3 -2
@@ -250,8 +250,8 @@
250 "transaction_details_recipient_address": "عناوين المستلم",
251 "wallet_list_title": "محفظة Monero",
252 "wallet_list_create_new_wallet": "إنشاء محفظة جديدة",
253 - "wallet_list_edit_wallet" : "تحرير المحفظة",
254 - "wallet_list_wallet_name" : "اسم المحفظة",
253 + "wallet_list_edit_wallet" : "تحرير المحفظة",
254 + "wallet_list_wallet_name" : "اسم المحفظة",
255 "wallet_list_restore_wallet": "استعادة المحفظة",
256 "wallet_list_load_wallet": "تحميل المحفظة",
257 "wallet_list_loading_wallet": "جار تحميل محفظة ${wallet_name}",
@@ -269,6 +269,7 @@
269 "error_text_address": "يجب أن يتوافق عنوان المحفظة مع نوع\nالعملة المشفرة",
270 "error_text_node_address": "الرجاء إدخال عنوان IPv4",
271 "error_text_node_port": "منفذ العقدة يمكن أن يحتوي فقط على أرقام بين 0 و 65535",
272 + "error_text_node_proxy_address": "الرجاء إدخال <عنوان IPv4>: <port> ، على سبيل المثال 127.0.0.1:9050",
273 "error_text_payment_id": "يمكن أن يحتوي معرّف الدفع فقط من 16 إلى 64 حرفًا hex",
274 "error_text_xmr": "لا يمكن أن تتجاوز قيمة XMR الرصيد المتاح.\nيجب أن يكون عدد الكسور أقل من أو يساوي 12",
275 "error_text_fiat": "لا يمكن أن تتجاوز قيمة المبلغ الرصيد المتاح.\nيجب أن يكون عدد الكسور أقل أو يساوي 2",
res/values/strings_bg.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Адресът на портфейла трябва да отговаря \n на вида криптовалута",
270 "error_text_node_address": "Моля, въведете iPv4 адрес",
271 "error_text_node_port": "Node port-ът е цяло число между 0 и 65535",
272 + "error_text_node_proxy_address": "Моля, въведете <IPv4 адрес>:<порт>, например 127.0.0.1:9050",
273 "error_text_payment_id": "Payment ID-то може да съдържа само между 16 и 64 шестнайсетични символа",
274 "error_text_xmr": "XMR сумата не може да надхвърля наличния баланс.\nБроят на цифрите след десетичната запетая може да бъде най-много 12",
275 "error_text_fiat": "Сумата не може да надвишава наличния баланс.\nThe number of fraction digits must be less or equal to 2",
res/values/strings_cs.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Adresa peněženky musí odpovídat typu\nkryptoměny",
270 "error_text_node_address": "prosím zadejte IPv4 adresu",
271 "error_text_node_port": "Port uzlu musí být číslo mezi 0 a 65535",
272 + "error_text_node_proxy_address": "Zadejte prosím <adresa IPv4>:<port>, například 127.0.0.1:9050",
273 "error_text_payment_id": "ID platby se musí skládat z 16 až 64 hexadecimálních znaků",
274 "error_text_xmr": "Hodnota XMR nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 12",
275 "error_text_fiat": "Částka nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 2",
res/values/strings_de.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Die Walletadresse muss dem Typ der Kryptowährung\nentsprechen",
270 "error_text_node_address": "Bitte geben Sie eine iPv4-Adresse ein",
271 "error_text_node_port": "Der Knotenport darf nur Nummern zwischen 0 und 65535 enthalten",
272 + "error_text_node_proxy_address": "Bitte geben Sie <IPv4-Adresse>:<Port> ein, zum Beispiel 127.0.0.1:9050",
273 "error_text_payment_id": "Die Zahlungs-ID darf nur 16 bis 64 hexadezimale Zeichen enthalten",
274 "error_text_xmr": "Der XMR-Wert darf das verfügbare Guthaben nicht überschreiten.\nDie Anzahl der Nachkommastellen muss kleiner oder gleich 12 sein",
275 "error_text_fiat": "Der Wert des Betrags darf den verfügbaren Kontostand nicht überschreiten.\nDie Anzahl der Nachkommastellen muss kleiner oder gleich 2 sein",
res/values/strings_en.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Wallet address must correspond to the type\nof cryptocurrency",
270 "error_text_node_address": "Please enter a iPv4 address",
271 "error_text_node_port": "Node port can only contain numbers between 0 and 65535",
272 + "error_text_node_proxy_address": "Please enter <IPv4 address>:<port>, for example 127.0.0.1:9050",
273 "error_text_payment_id": "Payment ID can only contain from 16 to 64 chars in hex",
274 "error_text_xmr": "XMR value can't exceed available balance.\nThe number of fraction digits must be less or equal to 12",
275 "error_text_fiat": "Value of amount can't exceed available balance.\nThe number of fraction digits must be less or equal to 2",
res/values/strings_es.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "La dirección de la billetera debe corresponder al tipo \nde criptomoneda",
270 "error_text_node_address": "Por favor, introduzca una dirección iPv4",
271 "error_text_node_port": "El puerto de nodo solo puede contener números entre 0 y 65535",
272 + "error_text_node_proxy_address": "Ingrese <dirección IPv4>:<puerto>, por ejemplo 127.0.0.1:9050",
273 "error_text_payment_id": "La ID de pago solo puede contener de 16 a 64 caracteres en hexadecimal",
274 "error_text_xmr": "El valor XMR no puede exceder el saldo disponible.\nTEl número de dígitos de fracción debe ser menor o igual a 12",
275 "error_text_fiat": "El valor de la cantidad no puede exceder el saldo disponible.\nEl número de dígitos de fracción debe ser menor o igual a 2",
res/values/strings_fr.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "L'adresse du portefeuille (wallet) doit correspondre au type de\ncryptomonnaie",
270 "error_text_node_address": "Merci d'entrer une adresse IPv4",
271 "error_text_node_port": "Le port d'un nœud doit être un nombre compris entre 0 et 65535",
272 + "error_text_node_proxy_address": "Veuillez saisir <adresse IPv4> :<port>, par exemple 127.0.0.1:9050",
273 "error_text_payment_id": "Un ID de paiement ne peut être constitué que de 16 à 64 caractères hexadécimaux",
274 "error_text_xmr": "La valeur de XMR dépasse le solde disponible.\nLa partie décimale doit comporter au plus 12 chiffres",
275 "error_text_fiat": "La valeur du montant ne peut dépasser le solde disponible.\nLa partie décimale doit comporter au plus 2 chiffres",
res/values/strings_ha.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Adireshin hujja ya kamata ya dace da irin\nna cryptocurrency",
270 "error_text_node_address": "Da fatan a shigar da iPv4 adireshin",
271 "error_text_node_port": "Node tashar jiragen ruwa zai iya ƙunsar lambobi tsakanin 0 zuwa 65535 kawai",
272 + "error_text_node_proxy_address": "Da fatan za a shigar da <IPv4 address>:<port>, misali 127.0.0.1:9050",
273 "error_text_payment_id": "ID na biyan kudi kawai zai iya ɗaukar daga 16 zuwa 64 haruffa a cikin hex",
274 "error_text_xmr": "XMR adadin ba zai iya wuce available balance.\nAdadin haruffan gaba zai kamata ya zama ko ƙasa daga na 12",
275 "error_text_fiat": "Adadin kudin ba zai iya wuce available balance.\nAdadin haruffan gaba zai kamata ya zama ko ƙasa daga na 2",
res/values/strings_hi.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "वॉलेट पता प्रकार के अनुरूप होना चाहिए\nक्रिप्टोकरेंसी का",
270 "error_text_node_address": "कृपया एक IPv4 पता दर्ज करें",
271 "error_text_node_port": "नोड पोर्ट में केवल 0 और 65535 के बीच संख्याएँ हो सकती हैं",
272 + "error_text_node_proxy_address": "कृपया <IPv4 पता> दर्ज करें: <पोर्ट>, उदाहरण के लिए 127.0.0.1:9050",
273 "error_text_payment_id": "पेमेंट आईडी केवल हेक्स में 16 से 64 चार्ट तक हो सकती है",
274 "error_text_xmr": "एक्सएमआर मूल्य उपलब्ध शेष राशि से अधिक नहीं हो सकता.\nअंश अंकों की संख्या 12 से कम या इसके बराबर होनी चाहिए",
275 "error_text_fiat": "राशि का मूल्य उपलब्ध शेष राशि से अधिक नहीं हो सकता.\nअंश अंकों की संख्या कम या 2 के बराबर होनी चाहिए",
res/values/strings_hr.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Adresa novčanika mora odgovarati\nvrsti kriptovalute",
270 "error_text_node_address": "Molimo unesite iPv4 adresu",
271 "error_text_node_port": "Node port smije sadržavati samo brojeve između 0 i 65535",
272 + "error_text_node_proxy_address": "Unesite <IPv4 adresa>:<port>, na primjer 127.0.0.1:9050",
273 "error_text_payment_id": "ID plaćanja smije sadržavati samo od 16 do 64 znakova hex vrijednosti",
274 "error_text_xmr": "XMR vrijednost ne smije biti veća od raspoloživog iznosa.\nBroj decimala smije biti 12 ili manji.",
275 "error_text_fiat": "Vrijednost iznosa ne smije biti veća od raspoloživog iznosa.\nBroj decimala smije biti 2 ili manji.",
res/values/strings_id.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Alamat dompet harus sesuai dengan tipe\nmata uang kripto",
270 "error_text_node_address": "Silakan masukkan alamat iPv4",
271 "error_text_node_port": "Port node hanya dapat berisi angka antara 0 dan 65535",
272 + "error_text_node_proxy_address": "Masukkan <alamat IPv4>:<port>, misalnya 127.0.0.1:9050",
273 "error_text_payment_id": "ID pembayaran hanya dapat berisi dari 16 hingga 64 karakter dalam hex",
274 "error_text_xmr": "Nilai XMR tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 12",
275 "error_text_fiat": "Nilai jumlah tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 2",
res/values/strings_it.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "L'indirizzo del Portafoglio deve corrispondere alla tipologia\ndi criptovaluta",
270 "error_text_node_address": "Gentilmente inserisci un indirizzo iPv4",
271 "error_text_node_port": "La porta del nodo può contenere solo numeri compresi tra 0 e 65535",
272 + "error_text_node_proxy_address": "Inserisci <indirizzo IPv4>:<porta>, ad esempio 127.0.0.1:9050",
273 "error_text_payment_id": "L'ID del pagamento può contenere solo da 16 a 64 caratteri in esadecimale",
274 "error_text_xmr": "Il valore in XMR non può eccedere il saldo disponibile.\nIl numero delle cifre decimali deve essere inferiore o uguale a 12",
275 "error_text_fiat": "L'ammontare non può eccedere il saldo dispoinibile.\nIl numero di cifre decimali deve essere inferiore o uguale a 2",
res/values/strings_ja.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "ウォレットアドレスは、\n暗号通貨",
270 "error_text_node_address": "iPv4アドレスを入力してください",
271 "error_text_node_port": "ノードポートには、0〜65535の数字のみを含めることができます",
272 + "error_text_node_proxy_address": "<IPv4 アドレス>:<ポート> を入力してください (例: 127.0.0.1:9050)",
273 "error_text_payment_id": "支払いIDには、16進数で16〜64文字しか含めることができません",
274 "error_text_xmr": "XMR値は利用可能な残高を超えることはできません.\n小数桁数は12以下でなければなりません",
275 "error_text_fiat": "金額は利用可能な残高を超えることはできません.\n小数桁の数は2以下でなければなりません",
res/values/strings_ko.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "지갑 주소는 유형과 일치해야합니다\n암호 화폐",
270 "error_text_node_address": "iPv4 주소를 입력하십시오",
271 "error_text_node_port": "노드 포트는 0에서 65535 사이의 숫자 만 포함 할 수 있습니다",
272 + "error_text_node_proxy_address": "<IPv4 주소>:<포트>를 입력하십시오(예: 127.0.0.1:9050).",
273 "error_text_payment_id": "지불 ID는 16 ~ 64 자의 16 진 문자 만 포함 할 수 있습니다",
274 "error_text_xmr": "XMR 값은 사용 가능한 잔액을 초과 할 수 없습니다.\n소수 자릿수는 12 이하 여야합니다",
275 "error_text_fiat": "금액은 사용 가능한 잔액을 초과 할 수 없습니다.\n소수 자릿수는 2보다 작거나 같아야합니다",
res/values/strings_my.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Wallet လိပ်စာသည် အမျိုးအစား\no cryptocurrency နှင့် ကိုက်ညီရပါမည်။",
270 "error_text_node_address": "ကျေးဇူးပြု၍ iPv4 လိပ်စာကို ထည့်ပါ။",
271 "error_text_node_port": "နော်ဒီဆိပ်ကမ်း တွင် 0 နှင့် 65535 အကြား နံပါတ်များသာ ပါဝင်နိုင်သည်။",
272 + "error_text_node_proxy_address": "ကျေးဇူးပြု၍ <IPv4 လိပ်စာ>:<port>၊ ဥပမာ 127.0.0.1:9050 ထည့်ပါ",
273 "error_text_payment_id": "ငွေပေးချေမှု ID တွင် hex တွင် စာလုံး 16 လုံးမှ 64 လုံးသာ ပါဝင်နိုင်သည်။",
274 "error_text_xmr": "XMR တန်ဖိုးသည် ရနိုင်သောလက်ကျန်ကို ကျော်လွန်၍မရပါ။\nအပိုင်းကိန်းဂဏန်းများ သည် 12 နှင့် လျော့နည်းရမည်",
275 "error_text_fiat": "ပမာဏ၏တန်ဖိုးသည် ရနိုင်သောလက်ကျန်ကို မကျော်လွန်နိုင်ပါ။\nအပိုင်းကိန်းဂဏန်းအရေအတွက်သည် 2 နှင့် လျော့နည်းရမည်",
res/values/strings_nl.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Portemonnee-adres moet overeenkomen met het type\nvan cryptocurrency",
270 "error_text_node_address": "Voer een iPv4-adres in",
271 "error_text_node_port": "Knooppuntpoort kan alleen nummers tussen 0 en 65535 bevatten",
272 + "error_text_node_proxy_address": "Voer <IPv4-adres>:<poort> in, bijvoorbeeld 127.0.0.1:9050",
273 "error_text_payment_id": "Betalings-ID kan alleen 16 tot 64 tekens bevatten in hexadecimale volgorde",
274 "error_text_xmr": "XMR-waarde kan het beschikbare saldo niet overschrijden.\nHet aantal breukcijfers moet kleiner zijn dan of gelijk zijn aan 12",
275 "error_text_fiat": "Waarde van bedrag kan het beschikbare saldo niet overschrijden.\nHet aantal breukcijfers moet kleiner zijn dan of gelijk zijn aan 2",
res/values/strings_pl.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Adres musi odpowiadać typowi kryptowaluty",
270 "error_text_node_address": "Wpisz adres iPv4",
271 "error_text_node_port": "Port węzła może zawierać tylko liczby od 0 do 65535",
272 + "error_text_node_proxy_address": "Wprowadź <adres IPv4>:<port>, na przykład 127.0.0.1:9050",
273 "error_text_payment_id": "ID może zawierać od 16 do 64 znaków w formacie szesnastkowym",
274 "error_text_xmr": "Wartość XMR nie może przekraczać dostępnego salda.\nLiczba cyfr ułamkowych musi być mniejsza lub równa 12",
275 "error_text_fiat": "Wartość kwoty nie może przekroczyć dostępnego salda.\nLiczba cyfr ułamkowych musi być mniejsza lub równa 2",
res/values/strings_pt.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "O endereço da carteira deve corresponder à\ncriptomoeda selecionada",
270 "error_text_node_address": "Digite um endereço iPv4",
271 "error_text_node_port": "A porta do nó deve conter apenas números entre 0 e 65535",
272 + "error_text_node_proxy_address": "Insira <endereço IPv4>:<porta>, por exemplo 127.0.0.1:9050",
273 "error_text_payment_id": "O ID de pagamento pode conter apenas de 16 a 64 caracteres em hexadecimal",
274 "error_text_xmr": "A quantia em XMR não pode exceder o saldo disponível.\nTO número de dígitos decimais deve ser menor ou igual a 12",
275 "error_text_fiat": "O valor do valor não pode exceder o saldo disponível.\nO número de dígitos decimais deve ser menor ou igual a 2",
res/values/strings_ru.arb
+2
@@ -269,6 +269,8 @@
269 "error_text_address": "Адрес кошелька должен соответствовать типу\nкриптовалюты",
270 "error_text_node_address": "Пожалуйста, введите iPv4 адрес",
271 "error_text_node_port": "Порт ноды может содержать только цифры от 0 до 65535",
272 + "error_text_node_proxy_address": "Введите <IPv4-адрес>:<порт>, например 127.0.0.1:9050.",
273 +
274 "error_text_payment_id": "Идентификатор платежа может содержать от 16 до 64 символов в hex",
275 "error_text_xmr": "Значение XMR не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 12",
276 "error_text_fiat": "Значение суммы не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 2",
res/values/strings_th.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "ที่อยู่กระเป๋าจะต้องสอดคล้องกับประเภท\nของเหรียญคริปโตเนียม",
270 "error_text_node_address": "โปรดป้อนที่อยู่ iPv4",
271 "error_text_node_port": "พอร์ตโหนดสามารถมีตัวเลขเท่านั้นระหว่าง 0 ถึง 65535",
272 + "error_text_node_proxy_address": "โปรดป้อน <ที่อยู่ IPv4>:<พอร์ต> เช่น 127.0.0.1:9050",
273 "error_text_payment_id": "Payment ID สามารถมีขนาดระหว่าง 16 ถึง 64 ตัวอักษรตามแบบ hex",
274 "error_text_xmr": "มูลค่า XMR ไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 12",
275 "error_text_fiat": "มูลค่าของจำนวนเงินไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 2",
res/values/strings_tr.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Cüzdan adresi kripto para biriminin\ntürüne karşılık gelmelidir",
270 "error_text_node_address": "Lütfen iPv4 adresi girin",
271 "error_text_node_port": "Düğüm port'u yalnızca 0 ve 65535 arasında rakam içerebilir",
272 + "error_text_node_proxy_address": "Lütfen <IPv4 adresi>:<bağlantı noktası> girin, örneğin 127.0.0.1:9050",
273 "error_text_payment_id": "Ödeme ID'si yalnızca onaltılık (hex) olarak 16 veya 64 karakter içerebilir",
274 "error_text_xmr": "XMR miktarı kullanılabilir bakiyeyi aşamaz.\nKesir basamaklarının sayısı 12'ye eşit veya daha az olmalıdır",
275 "error_text_fiat": "Tutarın değeri, mevcut bakiyeyi aşamaz.\nKesir basamaklarının sayısı 2'ye eşit veya daha az olmalıdır",
res/values/strings_uk.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "Адреса гаманця повинна відповідати типу\nкриптовалюти",
270 "error_text_node_address": "Будь ласка, введіть iPv4 адресу",
271 "error_text_node_port": "Порт вузла може містити тільки цифри від 0 до 65535",
272 + "error_text_node_proxy_address": "Будь ласка, введіть <IPv4-адреса>:<порт>, наприклад 127.0.0.1:9050",
273 "error_text_payment_id": "Ідентифікатор платежу може містити від 16 до 64 символів в hex",
274 "error_text_xmr": "Значення XMR не може перевищувати доступний баланс.\nКількість цифр після коми повинно бути меншим або дорівнювати 12",
275 "error_text_fiat": "Значення суми не може перевищувати доступний баланс.\nКількість цифр після коми повинно бути меншим або дорівнювати 2",
res/values/strings_ur.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "والیٹ کا پتہ cryptocurrency کی قسم\\nکے مطابق ہونا چاہیے۔",
270 "error_text_node_address": "براہ کرم ایک iPv4 پتہ درج کریں۔",
271 "error_text_node_port": "نوڈ پورٹ میں صرف 0 اور 65535 کے درمیان نمبر ہوسکتے ہیں۔",
272 + "error_text_node_proxy_address": "براہ کرم <IPv4 ایڈریس>:<port> درج کریں، مثال کے طور پر 127.0.0.1:9050",
273 "error_text_payment_id": "ادائیگی کی ID ہیکس میں صرف 16 سے 64 حروف پر مشتمل ہو سکتی ہے۔",
274 "error_text_xmr": "XMR قدر دستیاب بیلنس سے زیادہ نہیں ہو سکتی۔\\nفرکشن ہندسوں کی تعداد 12 سے کم یا اس کے برابر ہونی چاہیے۔",
275 "error_text_fiat": "رقم کی قدر دستیاب بیلنس سے زیادہ نہیں ہو سکتی۔\\nفرکشن ہندسوں کی تعداد 2 کے برابر یا کم ہونی چاہیے۔",
res/values/strings_yo.arb
+3 -2
@@ -250,8 +250,8 @@
250 "transaction_details_recipient_address": "Àwọn àdírẹ́sì olùgbà",
251 "wallet_list_title": "Àpamọ́wọ́ Monero",
252 "wallet_list_create_new_wallet": "Ṣe àpamọ́wọ́ títun",
253 - "wallet_list_edit_wallet" : "Ṣatunkọ apamọwọ",
254 - "wallet_list_wallet_name" : "Orukọ apamọwọ",
253 + "wallet_list_edit_wallet" : "Ṣatunkọ apamọwọ",
254 + "wallet_list_wallet_name" : "Orukọ apamọwọ",
255 "wallet_list_restore_wallet": "Restore àpamọ́wọ́",
256 "wallet_list_load_wallet": "Load àpamọ́wọ́",
257 "wallet_list_loading_wallet": "Ń ṣí àpamọ́wọ́ ${wallet_name}",
@@ -269,6 +269,7 @@
269 "error_text_address": "Àdírẹ́sì àpamọ́wọ́ gbọ́dọ̀ báramu irú owó",
270 "error_text_node_address": "Ẹ jọ̀wọ́ tẹ̀ àdírẹ́sì iPv4",
271 "error_text_node_port": "Ojú ìkànpọ̀ apẹka lè ni nìkan nínú òǹkà l'áàárín òdo àti márùn-úndínlógojí lé ní ẹ̀ẹ́dẹgbẹ̀ta lé ní ọ̀kẹ́ mẹ́ta lé ní ẹ̀ẹ́dẹ́gbàta",
272 + "error_text_node_proxy_address": "Jọwọ tẹ <IPv4 adirẹsi>:<port>, fun apẹẹrẹ 127.0.0.1:9050",
273 "error_text_payment_id": "Iye ẹyọ ọ̀rọ̀ nínú àmì ìdánimọ̀ àránṣẹ́ gbọ́dọ̀ wà l'áàárín aárùndínlógún dé ẹẹ́rinlélọ́gọ́ta.",
274 "error_text_xmr": "Iye XMR kò lè tóbi ju ìyókù.\nIye díjíìtì léyìn ẹsẹ kò gbọ́dọ̀ tóbi ju eéjìlá.",
275 "error_text_fiat": "Iye àránṣẹ́ kò tóbi ju ìyókù owó.\nIye díjíìtì léyìn ẹsẹ kò gbọ́dọ̀ tóbi ju eéjì.",
res/values/strings_zh.arb
+1
@@ -269,6 +269,7 @@
269 "error_text_address": "钱包地址必须与类型对应\n加密货币",
270 "error_text_node_address": "请输入一个IPv4地址",
271 "error_text_node_port": "节点端口只能包含0到65535之间的数字",
272 + "error_text_node_proxy_address": "请输入<IPv4地址>:<端口>,例如127.0.0.1:9050",
273 "error_text_payment_id": "付款ID只能包含16到64个字符(十六进制)",
274 "error_text_xmr": "XMR值不能超过可用余额.\n小数位数必须小于或等于12",
275 "error_text_fiat": "金额不能超过可用余额.\n小数位数必须小于或等于2",