CW-519 Enable built-in Tor (#1950)

* tor wip * Enable tor on iOS * Prevent app lag when node is exceptionally slow (usually over tor) * fix: logic in daemonBlockchainHeight refresh fix: storing tor state * Pin ledger_flutter_plus dependency to fix builds * bump arti version * wip * add single httpclient * route everything I was able to catch trough the built-in tor node * Enable proxy for http.Client [run tests] * add tor proxy support to cw_evm, cw_tron and cw_polygon [run tests] * remove log pollution, cleanup [skip slack] * fix tests not working in latest main [skip slack] [run tests] * remove cw_wownero import * fix build issues * migrate all remaining calls to use ProxyWrapper add a CI action to enforce using ProxyWrapper instead of http/http.dart to prevent leaks * fix tor background sync (will work on test builds after #2142 is merged and this PR is rebased on top) * wip [skip ci] * relicense to GPLv3 add socks5 license, build fixes * use ProxyWrapper instead of http in robinhood * Revert "relicense to GPLv3" * feat(cw_bitcoin): support socks proxy and CakeTor * fix(tor): migrate OCP and EVM over to ProxyWrapper() * chore: cleanup fix: show tor loading screen when app is starting * fix: tor switch properly dismisses fullscreen loading dialog fix: connectToNode after tor startup on app start * fix(tor): status check for xmr/wow/zano * fix(tor): onramper request fix * fix(api): ServicesResponse is now being cached and doesn't fetch data everytime DashboardViewModel is being rebuilt fix(tor): do not fallback to clearnet when tor failed. fix(tor): do not leak connections during app startup chore: refactor bootstrap() function to be separated into bootstrapOffline and bootstrapOnline fix(cw_bitcoin): migrate payjoin to use ProxyWrapper * [skip ci] remove print * address comments from review * fix: derusting tor implementation Instead of rust-based Arti I've moved back to the OG C++ tor implementation. This fixed all issues we had with Tor. - onion services now work - all requests are going through without random errors - we don't have to navigate a maze of multiple forks of multiple packages - fully working `torrc` config file (probably will be needed for Tari). - logging for Tor client - and so on. feat: network logging tab feat: use built-in proxy on Tails - this should resolve all issues for Tails users (needs testing though) * fix conflicts with main bump https to fix build issue relax store() call * fix(cw_wownero): tor connection fix(tor): connection issues * fix(cw_evm): add missing chainId fix(cw_core): solana rpc fix * feat: mark tor as experimental fix: drop anonpay onion authority fix: drop fiatapi onion authority fix: drop trocador onion authority fix: disable networkimage when tor is enabled fix: handle cakepay errors gracefully * fix re-formatting [skip ci] * changes from review * Delete android/.kotlin/sessions/kotlin-compiler-2468481326039681181.salive * fix missing imports * Update pubspec_base.yaml --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

cyan committed Jun 20, 2025 at 21:56 UTC 5082dc20f33ef8472bea8c5d904d2eff14dc334b
139 files changed +2755 -879
.github/workflows/no_http_imports.yaml new
+21
@@ -0,0 +1,21 @@
1 +name: No http imports
2 +
3 +on: [pull_request]
4 +
5 +jobs:
6 + PR_test_build:
7 + runs-on: ubuntu-24.04
8 +
9 + steps:
10 + - uses: actions/checkout@v4
11 + - name: Check for http package usage
12 + if: github.event_name == 'pull_request'
13 + run: |
14 + GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) || true)"
15 + [[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
16 + echo "$GIT_GREP_OUT"
17 + echo "There are .dart files which use http imports"
18 + echo "Using http package breaks proxy integration"
19 + echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart"
20 + exit 1
21 +
\ No newline at end of file
.github/workflows/no_print_in_dart.yaml
+1 -1
@@ -15,5 +15,5 @@ jobs:
15 [[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
16 echo "$GIT_GREP_OUT"
17 echo "There are .dart files which use print() statements"
18 - echo "Please use printV from package: cw_core/utils/print_verbose.dart"
18 + echo "Please use printV from package:cw_core/utils/print_verbose.dart"
19 exit 1
assets/images/tor_logo.svg new
+76
@@ -0,0 +1,76 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3 +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4 +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5 + viewBox="0 0 150.5 91.1" enable-background="new 0 0 150.5 91.1" xml:space="preserve">
6 +<g>
7 + <g>
8 + <g id="layer3_22_" transform="translate(-92,-63.999774)">
9 + <g id="layer5_22_">
10 + <g id="path2554_38_">
11 + <path fill="#68B044" d="M176.9,70.8l-2.4,9.6c3.4-6.8,8.9-11.9,15.2-16.4c-4.6,5.3-8.8,10.6-11.3,16
12 + c4.3-6.1,10.1-9.4,16.6-11.7c-8.7,7.7-15.6,16.1-20.8,24.4l-4.2-1.8C170.7,84.2,173.2,77.4,176.9,70.8L176.9,70.8z"/>
13 + </g>
14 + <g id="path2534_13_">
15 + <path fill="#F5F8DE" d="M165.8,89.1l7.9,3.3c0,2-0.2,8.2,1.1,10c13.2,17,11,51.2-2.7,52c-20.8,0-28.8-14.1-28.8-27.1
16 + c0-11.9,14.2-19.7,22.7-26.7C168.3,98.7,167.9,94.5,165.8,89.1L165.8,89.1z"/>
17 + </g>
18 + <g id="path2536_23_">
19 + <path fill="#7E4798" d="M173.7,92.3l2.9,1.5c-0.3,1.9,0.1,6.1,2,7.1c8.4,5.2,16.2,10.8,19.3,16.5c11,19.9-7.7,38.4-24,36.6
20 + c8.8-6.5,11.4-19.9,8.1-34.6c-1.3-5.7-3.4-10.9-7.1-16.8C173.3,99.9,173.9,96.3,173.7,92.3L173.7,92.3z"/>
21 + </g>
22 + </g>
23 + <g id="layer4_22_">
24 + <g id="path2540_23_">
25 + <path fill="#010101" d="M170.5,101.8c-0.6,3.1-1.3,8.7-4,10.8c-1.1,0.8-2.3,1.6-3.5,2.4c-4.9,3.3-9.7,6.4-11.9,14.3
26 + c-0.5,1.7-0.1,3.5,0.3,5.2c1.2,4.9,4.6,10.1,7.3,13.2c0,0.1,0.5,0.5,0.5,0.6c2.2,2.6,2.9,3.4,11.3,5.3l-0.2,0.9
27 + c-5.1-1.3-9.2-2.6-11.9-5.6c0-0.1-0.5-0.5-0.5-0.5c-2.8-3.2-6.3-8.6-7.5-13.7c-0.5-2-0.9-3.6-0.3-5.7
28 + c2.3-8.2,7.3-11.5,12.3-14.9c1.1-0.7,2.5-1.4,3.6-2.3C168.3,110.3,169.4,105.6,170.5,101.8L170.5,101.8z"/>
29 + </g>
30 + <g id="path2542_23_">
31 + <path fill="#010101" d="M172.7,114.8c0.1,3.5-0.3,5.3,0.6,7.8c0.5,1.5,2.4,3.5,2.9,5.5c0.7,2.6,1.5,5.5,1.5,7.3
32 + c0,2-0.1,5.8-1,9.8c-0.7,3.3-2.2,6.2-4.8,7.8c-2.7-0.5-5.8-1.5-7.6-3.1c-3.6-3.1-6.7-8.3-7.1-12.8c-0.3-3.7,3.1-9.2,7.9-11.9
33 + c4-2.4,5-5,5.9-9.4c-1.2,3.8-2.4,6.9-6.3,9c-5.7,3-8.6,7.9-8.3,12.7c0.4,6.1,2.8,10.2,7.6,13.5c2,1.4,5.8,2.9,8.2,3.3v-0.3
34 + c1.8-0.3,4.1-3.3,5.3-7.2c1-3.6,1.4-8.1,1.3-11c-0.1-1.7-0.8-5.3-2.2-8.6c-0.7-1.8-1.9-3.6-2.6-4.9
35 + C173.1,120.8,173.1,118,172.7,114.8z"/>
36 + </g>
37 + <g id="path2544_23_">
38 + <path fill="#010101" d="M172.1,128.5c0.1,2.4,1,5.4,1.4,8.5c0.3,2.3,0.2,4.6,0.1,6.6c-0.1,2.3-0.8,6.5-1.9,8.6
39 + c-1-0.5-1.4-1-2.1-1.8c-0.8-1.1-1.4-2.3-1.9-3.6c-0.4-1-0.9-2.2-1.1-3.5c-0.3-2-0.2-5.2,2.1-8.4c1.8-2.6,2.2-2.8,2.8-5.7
40 + c-0.8,2.6-1.4,2.9-3.3,5.1c-2.1,2.4-2.4,6-2.4,8.9c0,1.2,0.5,2.6,1,3.8c0.5,1.3,1,2.7,1.7,3.7c1.1,1.6,2.5,2.6,3.2,2.7
41 + c0,0,0,0,0,0c0,0,0,0,0,0v-0.1c1.3-1.5,2.1-2.9,2.4-4.4c0.3-1.8,0.4-3.5,0.6-5.6c0.2-1.8,0.1-4.1-0.4-6.5
42 + C173.7,133.8,172.6,130.7,172.1,128.5L172.1,128.5z"/>
43 + </g>
44 + <g id="path2550_23_">
45 + <path fill="#010101" d="M172.5,99c0.1,3.5,0.3,10,1.3,12.6c0.3,0.9,2.8,4.7,4.5,9.4c1.2,3.2,1.5,6.2,1.7,7.1
46 + c0.8,3.8-0.2,10.3-1.5,16.4c-0.7,3.3-3,7.4-5.6,9l-0.5,0.9c1.5-0.1,5.1-3.6,6.4-8.1c2.2-7.5,3-11,2-19.4
47 + c-0.1-0.8-0.5-3.6-1.8-6.5c-1.9-4.5-4.6-8.8-4.9-9.7C173.4,109.3,172.6,103.1,172.5,99L172.5,99z"/>
48 + </g>
49 + <g id="path2552_23_">
50 + <path fill="#010101" d="M173.7,92.6c-0.2,3.6-0.2,6.4,0.4,9.1c0.7,2.9,4.5,7.1,6.1,11.9c3,9.2,2.2,21.2,0.1,30.5
51 + c-0.8,3.3-4.6,8.1-8.5,9.6l2.8,0.7c1.5-0.1,5.5-3.8,7.1-8c2.5-6.7,3-14.6,2-23c-0.1-0.8-1.4-8-2.7-11
52 + c-1.8-4.5-4.7-7.7-5.7-10.5c-0.8-2.1-1.1-7.7-0.6-8.8L173.7,92.6z"/>
53 + </g>
54 + </g>
55 + </g>
56 + <g id="path2528_18_">
57 + <path fill="#7E4798" d="M3.8,8.8h54.1c2,0,3.8,1.7,3.8,3.8v16c0,2.1-1.8,3.8-3.8,3.8H46.8c-2.5,0-3.6,1.4-3.6,3v52.3
58 + c0,1.8-1.4,3.1-3.1,3.1H21.7c-1.7,0-3-1.3-3-3.1V34.9c0-1.6-1.5-2.6-2.6-2.6H3.8c-2.1,0-3.8-1.7-3.8-3.8v-16
59 + C0,10.5,1.7,8.8,3.8,8.8z"/>
60 + </g>
61 + <g id="path2532_18_">
62 + <path fill="#7E4798" d="M142.6,30h4.8c1.8,0,3.2,1.4,3.2,3.1v17.1c0,2.2,0.1,3.1-2.6,3.1c-5.3,0-7.7,2.8-7.7,5.9v28.9
63 + c0,1.3-1.3,2.5-2.8,2.5h-17.2c-1.5,0-2.8-1.1-2.8-2.5V55.2c0-0.6,0-1.4,0.1-1.9c0.9-12.2,10.5-21.9,22.6-23.2
64 + C140.5,30.1,141.9,30,142.6,30L142.6,30z"/>
65 + </g>
66 + </g>
67 + <g>
68 + <path fill="#010101" d="M101.7,46.3c-2.9-2.6-6.5-4.8-10.3-6.9c-1.7-0.9-6.9-5-5.1-10.8l-13.1-5.4l-0.9,0.7
69 + c4.4,7.9,2.1,12.1-0.1,13.5c-4.4,3-10.8,6.8-13.9,10.1c-6.1,6.3-7.9,12.3-7.3,20.1c0.6,10.1,7.9,18.5,17.8,21.8
70 + c4.3,1.4,8.3,1.6,12.7,1.6c7.1,0,14.5-1.9,19.8-6.3c5.7-4.7,9-11.8,9-19.1C110.3,58.3,107.2,51.3,101.7,46.3z M99.8,83.2
71 + c-4.9,4-13.7,6.8-18.4,6.6c-5.2-0.3-10.3-1.1-14.8-3.3c-7.9-3.8-13.1-12.1-13.5-18.8C52.4,54,59,50.1,65.1,45.1
72 + c3.4-2.8,8.2-4.2,10.9-9.2c0.5-1.1,0.8-3.5,0.2-6c-0.3-0.9-1.5-3.9-2-4.6l9.8,4.3c-1.2,4.5,2.5,9.2,5.5,10.9
73 + c3,1.7,7.7,4.9,10.6,7.5c5.1,4.5,7.7,10.9,7.7,17.6C107.8,72.3,105,78.9,99.8,83.2z"/>
74 + </g>
75 +</g>
76 +</svg>
cw_bitcoin/lib/electrum.dart
+6 -12
@@ -5,6 +5,8 @@ import 'dart:typed_data';
5 import 'package:bitcoin_base/bitcoin_base.dart';
6 import 'package:cw_bitcoin/bitcoin_amount_format.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/utils/proxy_socket/abstract.dart';
9 +import 'package:cw_core/utils/proxy_wrapper.dart';
10 import 'package:flutter/foundation.dart';
11 import 'package:rxdart/rxdart.dart';
12
@@ -42,7 +44,7 @@ class ElectrumClient {
44 static const aliveTimerDuration = Duration(seconds: 4);
45
46 bool get isConnected => _isConnected;
45 - Socket? socket;
47 + ProxySocket? socket;
48 void Function(ConnectionStatus)? onConnectionStatusChange;
49 int _id;
50 final Map<String, SocketTask> _tasks;
@@ -72,18 +74,11 @@ class ElectrumClient {
74 } catch (_) {}
75 socket = null;
76
77 + final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
78 try {
76 - if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
77 - socket = await Socket.connect(host, port, timeout: connectionTimeout);
78 - } else {
79 - socket = await SecureSocket.connect(
80 - host,
81 - port,
82 - timeout: connectionTimeout,
83 - onBadCertificate: (_) => true,
84 - );
85 - }
79 + socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
80 } catch (e) {
81 + printV("connect: $e");
82 if (e is HandshakeException) {
83 useSSL = !(useSSL ?? false);
84 }
@@ -105,7 +100,6 @@ class ElectrumClient {
100
101 // use ping to determine actual connection status since we could've just not timed out yet:
102 // _setConnectionStatus(ConnectionStatus.connected);
108 -
103 socket!.listen(
104 (Uint8List event) {
105 try {
cw_bitcoin/lib/electrum_wallet.dart
+23 -29
@@ -4,6 +4,7 @@ import 'dart:io';
4 import 'dart:isolate';
5
6 import 'package:bitcoin_base/bitcoin_base.dart';
7 +import 'package:cw_core/utils/proxy_wrapper.dart';
8 import 'package:cw_bitcoin/bitcoin_amount_format.dart';
9 import 'package:cw_core/format_amount.dart';
10 import 'package:cw_core/utils/print_verbose.dart';
@@ -49,7 +50,6 @@ import 'package:mobx/mobx.dart';
50 import 'package:rxdart/subjects.dart';
51 import 'package:sp_scanner/sp_scanner.dart';
52 import 'package:hex/hex.dart';
52 -import 'package:http/http.dart' as http;
53
54 part 'electrum_wallet.g.dart';
55
@@ -493,10 +493,9 @@ abstract class ElectrumWalletBase
493 Future<void> updateFeeRates() async {
494 if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
495 try {
496 - final response = await http
497 - .get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
498 - .timeout(Duration(seconds: 5));
499 -
496 + final response = await ProxyWrapper()
497 + .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
498 + .timeout(Duration(seconds: 15));
499 final result = json.decode(response.body) as Map<String, dynamic>;
500 final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
501 int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
@@ -1176,20 +1175,18 @@ abstract class ElectrumWalletBase
1175 }
1176 });
1177
1179 - return PendingBitcoinTransaction(
1180 - transaction,
1181 - type,
1182 - electrumClient: electrumClient,
1183 - amount: estimatedTx.amount,
1184 - fee: estimatedTx.fee,
1185 - feeRate: feeRateInt.toString(),
1186 - network: network,
1187 - hasChange: estimatedTx.hasChange,
1188 - isSendAll: estimatedTx.isSendAll,
1189 - hasTaprootInputs: hasTaprootInputs,
1190 - utxos: estimatedTx.utxos,
1191 - publicKeys: estimatedTx.publicKeys
1192 - )..addListener((transaction) async {
1178 + return PendingBitcoinTransaction(transaction, type,
1179 + electrumClient: electrumClient,
1180 + amount: estimatedTx.amount,
1181 + fee: estimatedTx.fee,
1182 + feeRate: feeRateInt.toString(),
1183 + network: network,
1184 + hasChange: estimatedTx.hasChange,
1185 + isSendAll: estimatedTx.isSendAll,
1186 + hasTaprootInputs: hasTaprootInputs,
1187 + utxos: estimatedTx.utxos,
1188 + publicKeys: estimatedTx.publicKeys)
1189 + ..addListener((transaction) async {
1190 transactionHistory.addOne(transaction);
1191 if (estimatedTx.spendsSilentPayment) {
1192 transactionHistory.transactions.values.forEach((tx) {
@@ -1880,20 +1877,17 @@ abstract class ElectrumWalletBase
1877
1878 if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
1879 try {
1883 - final blockHash = await http.get(
1884 - Uri.parse(
1885 - "https://mempool.cakewallet.com/api/v1/block-height/$height",
1886 - ),
1887 - );
1880 + final blockHash = await ProxyWrapper()
1881 + .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
1882 + .timeout(Duration(seconds: 15));
1883
1884 if (blockHash.statusCode == 200 &&
1885 blockHash.body.isNotEmpty &&
1886 jsonDecode(blockHash.body) != null) {
1892 - final blockResponse = await http.get(
1893 - Uri.parse(
1894 - "https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
1895 - ),
1896 - );
1887 + final blockResponse = await ProxyWrapper()
1888 + .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
1889 + .timeout(Duration(seconds: 15));
1890 +
1891 if (blockResponse.statusCode == 200 &&
1892 blockResponse.body.isNotEmpty &&
1893 jsonDecode(blockResponse.body)['timestamp'] != null) {
cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart
+14 -14
@@ -8,11 +8,12 @@ import 'package:cw_bitcoin/payjoin/manager.dart';
8 import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
9 import 'package:cw_bitcoin/psbt/signer.dart';
10 import 'package:cw_core/utils/print_verbose.dart';
11 -import 'package:http/http.dart' as http;
11 +import 'package:cw_core/utils/proxy_wrapper.dart';
12 import 'package:payjoin_flutter/bitcoin_ffi.dart';
13 import 'package:payjoin_flutter/common.dart';
14 import 'package:payjoin_flutter/receive.dart';
15 import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
16 +import 'package:http/http.dart' as very_insecure_http_do_not_use; // for errors
17
18 enum PayjoinReceiverRequestTypes {
19 processOriginalTx,
@@ -28,7 +29,7 @@ class PayjoinReceiverWorker {
29 final pendingRequests = <String, Completer<dynamic>>{};
30
31 PayjoinReceiverWorker._(this.sendPort);
31 -
32 + static final client = ProxyWrapper().getHttpIOClient();
33 static Future<void> run(List<Object> args) async {
34 await pj.core.init();
35
@@ -42,11 +43,10 @@ class PayjoinReceiverWorker {
43 receivePort.listen(worker.handleMessage);
44
45 try {
45 - final httpClient = http.Client();
46 final receiver = Receiver.fromJson(json: receiverJson);
47
48 final uncheckedProposal =
49 - await worker.receiveUncheckedProposal(httpClient, receiver);
49 + await worker.receiveUncheckedProposal(receiver);
50
51 final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
52 sendPort.send({
@@ -57,14 +57,14 @@ class PayjoinReceiverWorker {
57 final payjoinProposal = await worker.processPayjoinProposal(
58 uncheckedProposal,
59 );
60 - final psbt = await worker.sendFinalProposal(httpClient, payjoinProposal);
60 + final psbt = await worker.sendFinalProposal(payjoinProposal);
61 sendPort.send({
62 'type': PayjoinReceiverRequestTypes.proposalSent,
63 'psbt': psbt,
64 });
65 } catch (e) {
66 if (e is HttpException ||
67 - (e is http.ClientException &&
67 + (e is very_insecure_http_do_not_use.ClientException &&
68 e.message.contains("Software caused connection abort"))) {
69 sendPort.send(PayjoinSessionError.recoverable(e.toString()));
70 } else {
@@ -98,16 +98,16 @@ class PayjoinReceiverWorker {
98 return completer.future;
99 }
100
101 - Future<UncheckedProposal> receiveUncheckedProposal(
102 - http.Client httpClient, Receiver session) async {
101 + Future<UncheckedProposal> receiveUncheckedProposal(Receiver session) async {
102 while (true) {
103 printV("Polling for Proposal (${session.id()})");
104 final extractReq = await session.extractReq(
106 - ohttpRelay: PayjoinManager.randomOhttpRelayUrl());
105 + ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
106 + );
107 final request = extractReq.$1;
108
109 final url = Uri.parse(request.url.asString());
110 - final httpRequest = await httpClient.post(url,
110 + final httpRequest = await client.post(url,
111 headers: {'Content-Type': request.contentType}, body: request.body);
112
113 final proposal = await session.processRes(
@@ -116,14 +116,14 @@ class PayjoinReceiverWorker {
116 }
117 }
118
119 - Future<String> sendFinalProposal(
120 - http.Client httpClient, PayjoinProposal finalProposal) async {
119 + Future<String> sendFinalProposal(PayjoinProposal finalProposal) async {
120 final req = await finalProposal.extractReq(
122 - ohttpRelay: PayjoinManager.randomOhttpRelayUrl());
121 + ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
122 + );
123 final proposalReq = req.$1;
124 final proposalCtx = req.$2;
125
126 - final request = await httpClient.post(
126 + final request = await client.post(
127 Uri.parse(proposalReq.url.asString()),
128 headers: {"Content-Type": proposalReq.contentType},
129 body: proposalReq.body,
cw_bitcoin/lib/payjoin/payjoin_send_worker.dart
+10 -10
@@ -5,7 +5,7 @@ import 'dart:isolate';
5 import 'package:cw_bitcoin/payjoin/manager.dart';
6 import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
8 -import 'package:http/http.dart' as http;
8 +import 'package:cw_core/utils/proxy_wrapper.dart';
9 import 'package:payjoin_flutter/common.dart';
10 import 'package:payjoin_flutter/send.dart';
11 import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
@@ -44,17 +44,17 @@ class PayjoinSenderWorker {
44 sendPort.send(e);
45 }
46 }
47 + final client = ProxyWrapper().getHttpIOClient();
48
49 /// Run a payjoin sender (V2 protocol first, fallback to V1).
50 Future<String> runSender(Sender sender) async {
50 - final httpClient = http.Client();
51
52 try {
53 - return await _runSenderV2(sender, httpClient);
53 + return await _runSenderV2(sender);
54 } catch (e) {
55 printV(e);
56 if (e is pj_error.FfiCreateRequestError) {
57 - return await _runSenderV1(sender, httpClient);
57 + return await _runSenderV1(sender);
58 } else if (e is HttpException) {
59 printV(e);
60 throw Exception(PayjoinSessionError.recoverable(e.toString()));
@@ -65,14 +65,14 @@ class PayjoinSenderWorker {
65 }
66
67 /// Attempt to send payjoin using the V2 of the protocol.
68 - Future<String> _runSenderV2(Sender sender, http.Client httpClient) async {
68 + Future<String> _runSenderV2(Sender sender) async {
69 try {
70 final postRequest = await sender.extractV2(
71 ohttpProxyUrl:
72 await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
73 );
74
75 - final postResult = await _postRequest(httpClient, postRequest.$1);
75 + final postResult = await _postRequest(postRequest.$1);
76 final getContext =
77 await postRequest.$2.processResponse(response: postResult);
78
@@ -84,7 +84,7 @@ class PayjoinSenderWorker {
84 final getRequest = await getContext.extractReq(
85 ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
86 );
87 - final getRes = await _postRequest(httpClient, getRequest.$1);
87 + final getRes = await _postRequest(getRequest.$1);
88 final proposalPsbt = await getContext.processResponse(
89 response: getRes,
90 ohttpCtx: getRequest.$2,
@@ -98,10 +98,10 @@ class PayjoinSenderWorker {
98 }
99
100 /// Attempt to send payjoin using the V1 of the protocol.
101 - Future<String> _runSenderV1(Sender sender, http.Client httpClient) async {
101 + Future<String> _runSenderV1(Sender sender) async {
102 try {
103 final postRequest = await sender.extractV1();
104 - final response = await _postRequest(httpClient, postRequest.$1);
104 + final response = await _postRequest(postRequest.$1);
105
106 sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted});
107
@@ -111,7 +111,7 @@ class PayjoinSenderWorker {
111 }
112 }
113
114 - Future<List<int>> _postRequest(http.Client client, Request req) async {
114 + Future<List<int>> _postRequest(Request req) async {
115 final httpRequest = await client.post(Uri.parse(req.url.asString()),
116 headers: {'Content-Type': req.contentType}, body: req.body);
117
cw_bitcoin/pubspec.lock
+26 -7
@@ -948,11 +948,21 @@ packages:
948 socks5_proxy:
949 dependency: transitive
950 description:
951 - name: socks5_proxy
952 - sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
953 - url: "https://pub.dev"
954 - source: hosted
955 - version: "1.0.6"
951 + path: "."
952 + ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
953 + resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
954 + url: "https://github.com/LacticWhale/socks_dart"
955 + source: git
956 + version: "2.1.0"
957 + socks_socket:
958 + dependency: "direct main"
959 + description:
960 + path: "."
961 + ref: e6232c53c1595469931ababa878759a067c02e94
962 + resolved-ref: e6232c53c1595469931ababa878759a067c02e94
963 + url: "https://github.com/sneurlax/socks_socket"
964 + source: git
965 + version: "1.1.1"
966 source_gen:
967 dependency: transitive
968 description:
@@ -1038,10 +1048,19 @@ packages:
1048 dependency: transitive
1049 description:
1050 name: timing
1041 - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
1051 + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
1052 url: "https://pub.dev"
1053 source: hosted
1044 - version: "1.0.2"
1054 + version: "1.0.1"
1055 + tor_binary:
1056 + dependency: transitive
1057 + description:
1058 + path: "."
1059 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
1060 + resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
1061 + url: "https://github.com/MrCyjaneK/flutter-tor_binary"
1062 + source: git
1063 + version: "4.7.14"
1064 tuple:
1065 dependency: transitive
1066 description:
cw_bitcoin/pubspec.yaml
+4
@@ -54,6 +54,10 @@ dependencies:
54 git:
55 url: https://github.com/cake-tech/ledger-flutter-plus-plugins
56 path: packages/ledger-litecoin
57 + socks_socket:
58 + git:
59 + url: https://github.com/sneurlax/socks_socket
60 + ref: e6232c53c1595469931ababa878759a067c02e94
61
62 dev_dependencies:
63 flutter_test:
cw_core/lib/get_height_by_date.dart
+13 -9
@@ -1,7 +1,7 @@
1 +import 'package:cw_core/utils/proxy_wrapper.dart';
2 import 'package:cw_core/utils/print_verbose.dart';
3 import 'package:intl/intl.dart';
4 import 'dart:convert';
4 -import 'package:http/http.dart' as http;
5
6 // FIXME: Hardcoded values; Works only for monero
7
@@ -234,10 +234,14 @@ int getHavenHeightByDate({required DateTime date}) {
234 }
235
236 Future<int> getHavenCurrentHeight() async {
237 - final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'));
237 + final req = await ProxyWrapper().getHttpClient()
238 + .getUrl(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'))
239 + .timeout(Duration(seconds: 15));
240 + final response = await req.close();
241 + final stringResponse = await response.transform(utf8.decoder).join();
242
243 if (response.statusCode == 200) {
240 - final info = jsonDecode(response.body);
244 + final info = jsonDecode(stringResponse);
245 return info['data']['height'] as int;
246 } else {
247 throw Exception('Failed to load current blockchain height');
@@ -269,13 +273,13 @@ const bitcoinDates = {
273 };
274
275 Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
272 - final response = await http.get(
273 - Uri.parse(
274 - "https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}",
275 - ),
276 - );
276 + final req = await ProxyWrapper().getHttpClient()
277 + .getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}"))
278 + .timeout(Duration(seconds: 15));
279 + final response = await req.close();
280 + final stringResponse = await response.transform(utf8.decoder).join();
281
278 - return jsonDecode(response.body)['height'] as int;
282 + return jsonDecode(stringResponse)['height'] as int;
283 }
284
285 int getBitcoinHeightByDate({required DateTime date}) {
cw_core/lib/node.dart
+30 -41
@@ -1,12 +1,12 @@
1 import 'dart:io';
2 import 'package:cw_core/keyable.dart';
3 +import 'package:cw_core/utils/proxy_socket/abstract.dart';
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5 import 'package:cw_core/utils/print_verbose.dart';
6 import 'dart:convert';
5 -import 'package:http/http.dart' as http;
7 import 'package:hive/hive.dart';
8 import 'package:cw_core/hive_type_ids.dart';
9 import 'package:cw_core/wallet_type.dart';
9 -import 'package:http/io_client.dart' as ioc;
10 import 'dart:math' as math;
11 import 'package:convert/convert.dart';
12
@@ -184,23 +184,17 @@ class Node extends HiveObject with Keyable {
184 final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"};
185
186 try {
187 - final authenticatingClient = HttpClient();
188 - authenticatingClient.badCertificateCallback =
189 - ((X509Certificate cert, String host, int port) => true);
190 -
191 - final http.Client client = ioc.IOClient(authenticatingClient);
192 -
187 final jsonBody = json.encode(body);
188
195 - final response = await client.post(
196 - rpcUri,
189 + final response = await ProxyWrapper().post(
190 + clearnetUri: rpcUri,
191 headers: {'Content-Type': 'application/json'},
192 body: jsonBody,
193 );
194
201 - printV("node check response: ${response.body}");
202 -
195 +
196 final resBody = json.decode(response.body) as Map<String, dynamic>;
197 +
198 return resBody['result']['height'] != null;
199 } catch (e) {
200 printV("error: $e");
@@ -218,11 +212,7 @@ class Node extends HiveObject with Keyable {
212 final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
213
214 try {
221 - final authenticatingClient = HttpClient();
222 - authenticatingClient.badCertificateCallback =
223 - ((X509Certificate cert, String host, int port) => true);
224 -
225 - final http.Client client = ioc.IOClient(authenticatingClient);
215 + final client = ProxyWrapper().getHttpIOClient();
216
217 final jsonBody = json.encode(body);
218
@@ -242,15 +232,15 @@ class Node extends HiveObject with Keyable {
232 return !(response['offline'] as bool);
233 }
234
245 - printV("node check response: ${response.body}");
235 + final responseString = await response.body;
236
247 - if ((response.body.contains("400 Bad Request") // Some other generic error
237 + if ((responseString.contains("400 Bad Request") // Some other generic error
238 ||
249 - response.body.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
239 + responseString.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
240 ||
241 response.headers["location"] != null // Generic reverse proxy
242 ||
253 - response.body
243 + responseString
244 .contains("301 Moved Permanently") // Poorly configured generic reverse proxy
245 ) &&
246 !(useSSL ?? false)) {
@@ -277,15 +267,16 @@ class Node extends HiveObject with Keyable {
267 }
268
269 Future<bool> requestNodeWithProxy() async {
280 - if (!isValidProxyAddress /* && !Tor.instance.enabled*/) {
270 + if (!isValidProxyAddress && !CakeTor.instance.enabled) {
271 return false;
272 }
273
274 String? proxy = socksProxyAddress;
275
286 - // if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
287 - // proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
288 - // }
276 + if ((proxy?.isEmpty ?? true) && CakeTor.instance.enabled) {
277 + proxy = "${InternetAddress.loopbackIPv4.address}:${CakeTor.instance.port}";
278 + }
279 + printV("proxy: $proxy");
280 if (proxy == null) {
281 return false;
282 }
@@ -305,13 +296,9 @@ class Node extends HiveObject with Keyable {
296 // you try to communicate with it
297 Future<bool> requestElectrumServer() async {
298 try {
308 - final Socket socket;
309 - if (useSSL == true) {
310 - socket = await SecureSocket.connect(uri.host, uri.port,
311 - timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
312 - } else {
313 - socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
314 - }
299 + final ProxySocket socket;
300 + socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
301 +
302
303 socket.destroy();
304 return true;
@@ -322,8 +309,8 @@ class Node extends HiveObject with Keyable {
309
310 Future<bool> requestNanoNode() async {
311 try {
325 - final response = await http.post(
326 - uri,
312 + final response = await ProxyWrapper().post(
313 + clearnetUri: uri,
314 headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"},
315 body: jsonEncode(
316 {
@@ -332,7 +319,8 @@ class Node extends HiveObject with Keyable {
319 },
320 ),
321 );
335 - final data = await jsonDecode(response.body);
322 +
323 + final data = jsonDecode(response.body);
324 if (response.statusCode != 200 ||
325 data["error"] != null ||
326 data["balance"] == null ||
@@ -348,13 +336,14 @@ class Node extends HiveObject with Keyable {
336
337 Future<bool> requestEthereumServer() async {
338 try {
351 - final response = await http.get(
352 - uri,
353 - headers: {'Content-Type': 'application/json'},
354 - );
339 + final req = await ProxyWrapper().getHttpClient()
340 + .getUrl(uri,)
341 + .timeout(Duration(seconds: 15));
342 + final response = await req.close();
343
344 return response.statusCode >= 200 && response.statusCode < 300;
357 - } catch (_) {
345 + } catch (err) {
346 + printV("Failed to request ethereum server: $err");
347 return false;
348 }
349 }
@@ -462,7 +451,7 @@ class DaemonRpc {
451
452 /// Perform a JSON-RPC call with Digest Authentication.
453 Future<Map<String, dynamic>> call(String method, Map<String, dynamic> params) async {
465 - final http.Client client = http.Client();
454 + final client = ProxyWrapper().getHttpIOClient();
455 final DigestAuth digestAuth = DigestAuth(username, password);
456
457 // Initial request to get the `WWW-Authenticate` header.
cw_core/lib/solana_rpc_http_service.dart
+7 -8
@@ -1,20 +1,19 @@
1 import 'dart:convert';
2 -import 'package:http/http.dart';
2 +import 'package:cw_core/utils/proxy_wrapper.dart';
3 import 'package:on_chain/solana/solana.dart';
4
5 class SolanaRPCHTTPService implements SolanaJSONRPCService {
6 SolanaRPCHTTPService(
7 - {required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
8 - : client = client ?? Client();
7 + {required this.url,
8 + this.defaultRequestTimeout = const Duration(seconds: 30)});
9 @override
10 final String url;
11 - final Client client;
11 final Duration defaultRequestTimeout;
12
14 - @override
15 - Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
16 - final response = await client.post(
17 - Uri.parse(url),
13 + Future<Map<String, dynamic>> call(SolanaRequestDetails params,
14 + [Duration? timeout]) async {
15 + final response = await ProxyWrapper().post(
16 + clearnetUri: Uri.parse(url),
17 body: params.toRequestBody(),
18 headers: {
19 'Content-Type': 'application/json',
cw_core/lib/utils/proxy_logger/abstract.dart new
+29
@@ -0,0 +1,29 @@
1 +import 'dart:typed_data';
2 +import 'package:http/http.dart' as very_insecure_http_do_not_use;
3 +
4 +enum RequestNetwork {
5 + clearnet,
6 + tor,
7 +}
8 +
9 +enum RequestMethod {
10 + get,
11 + post,
12 + put,
13 + delete,
14 +
15 + newHttpClient,
16 + newHttpIOClient,
17 + newProxySocket,
18 +}
19 +
20 +abstract class ProxyLogger {
21 + void log({
22 + required Uri? uri,
23 + required RequestMethod method,
24 + required Uint8List body,
25 + required very_insecure_http_do_not_use.Response? response,
26 + required RequestNetwork network,
27 + required String? error,
28 + });
29 +}
\ No newline at end of file
cw_core/lib/utils/proxy_logger/memory_proxy_logger.dart new
+63
@@ -0,0 +1,63 @@
1 +import 'dart:typed_data';
2 +
3 +import 'package:cw_core/utils/proxy_logger/abstract.dart';
4 +import 'package:http/http.dart' as very_insecure_http_do_not_use;
5 +
6 +class MemoryProxyLoggerEntry {
7 + MemoryProxyLoggerEntry({
8 + required this.trace,
9 + required this.uri,
10 + required this.body,
11 + required this.network,
12 + required this.method,
13 + required this.response,
14 + required this.error,
15 + }) : time = DateTime.now();
16 +
17 + final StackTrace trace;
18 + final Uri? uri;
19 + final Uint8List body;
20 + final RequestNetwork network;
21 + final very_insecure_http_do_not_use.Response? response;
22 + final RequestMethod method;
23 + final String? error;
24 + final DateTime time;
25 + @override
26 + String toString() => """MemoryProxyLoggerEntry(
27 + uri: $uri,
28 + body: $body,
29 + network: $network,
30 + method: $method,
31 + response:
32 + code: ${response?.statusCode},
33 + headers: ${response?.headers},
34 + body: ${response?.body},
35 + error: $error,
36 + time: $time,
37 + trace: ${trace}
38 +);""";
39 +}
40 +
41 +class MemoryProxyLogger implements ProxyLogger {
42 + static List<MemoryProxyLoggerEntry> logs = [];
43 + @override
44 + void log({
45 + required Uri? uri,
46 + required RequestMethod method,
47 + required Uint8List body,
48 + required very_insecure_http_do_not_use.Response? response,
49 + required RequestNetwork network,
50 + required String? error,
51 + }) {
52 + final trace = StackTrace.current;
53 + logs.add(MemoryProxyLoggerEntry(
54 + method: method,
55 + trace: trace,
56 + uri: uri,
57 + body: body,
58 + network: network,
59 + response: response,
60 + error: error,),
61 + );
62 + }
63 +}
\ No newline at end of file
cw_core/lib/utils/proxy_logger/silent_logger.dart new
+17
@@ -0,0 +1,17 @@
1 +import 'dart:typed_data';
2 +
3 +import 'package:cw_core/utils/proxy_logger/abstract.dart';
4 +import 'package:http/http.dart' as very_insecure_http_do_not_use;
5 +
6 +// we are not doing anything
7 +class SilentProxyLogger implements ProxyLogger {
8 + @override
9 + void log({
10 + required Uri? uri,
11 + required RequestMethod method,
12 + required Uint8List body,
13 + required very_insecure_http_do_not_use.Response? response,
14 + required RequestNetwork network,
15 + required String? error,
16 + }) {}
17 +}
\ No newline at end of file
cw_core/lib/utils/proxy_socket/abstract.dart new
+47
@@ -0,0 +1,47 @@
1 +import 'dart:async';
2 +import 'dart:io';
3 +import 'dart:typed_data';
4 +
5 +import 'package:cw_core/utils/proxy_socket/insecure.dart';
6 +import 'package:cw_core/utils/proxy_socket/secure.dart';
7 +import 'package:cw_core/utils/proxy_socket/socks.dart';
8 +import 'package:cw_core/utils/proxy_wrapper.dart';
9 +import 'package:socks_socket/socks_socket.dart';
10 +
11 +class ProxyAddress {
12 + final String host;
13 + final int port;
14 +
15 + ProxyAddress({required this.host, required this.port});
16 +}
17 +
18 +abstract class ProxySocket {
19 + static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address, {Duration? connectionTimeout}) async {
20 + if (CakeTor.instance.started) {
21 + var socksSocket = await SOCKSSocket.create(
22 + proxyHost: InternetAddress.loopbackIPv4.address,
23 + proxyPort: CakeTor.instance.port,
24 + sslEnabled: sslEnabled,
25 + );
26 + await socksSocket.connect();
27 + await socksSocket.connectTo(address.host, address.port);
28 + return ProxySocketSocks(socksSocket);
29 + }
30 + if (sslEnabled == false) {
31 + return ProxySocketInsecure(await Socket.connect(address.host, address.port, timeout: connectionTimeout));
32 + } else {
33 + return ProxySocketSecure(await SecureSocket.connect(
34 + address.host,
35 + address.port,
36 + timeout: connectionTimeout,
37 + onBadCertificate: (_) => true,
38 + ));
39 + }
40 + }
41 +
42 + Future<void> close();
43 + Future<void> destroy();
44 + Future<void> write(String data);
45 + StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function (Object error)? onError, Function ()? onDone, bool cancelOnError = true});
46 + ProxyAddress get address;
47 +}
\ No newline at end of file
cw_core/lib/utils/proxy_socket/insecure.dart new
+34
@@ -0,0 +1,34 @@
1 +
2 +import 'package:cw_core/utils/proxy_socket/abstract.dart';
3 +import 'dart:async';
4 +import 'dart:typed_data';
5 +import 'dart:io';
6 +
7 +class ProxySocketInsecure implements ProxySocket {
8 + final Socket socket;
9 +
10 + ProxySocketInsecure(this.socket);
11 +
12 + ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
13 +
14 + @override
15 + Future<void> close() => socket.close();
16 +
17 + @override
18 + Future<void> destroy() async => socket.destroy();
19 +
20 + @override
21 + Future<void> write(String data) async => socket.write(data);
22 +
23 + @override
24 + StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
25 + return socket.listen(
26 + (data) {
27 + onData(Uint8List.fromList(data));
28 + },
29 + onError: onError,
30 + onDone: onDone,
31 + cancelOnError: cancelOnError,
32 + );
33 + }
34 +}
\ No newline at end of file
cw_core/lib/utils/proxy_socket/secure.dart new
+34
@@ -0,0 +1,34 @@
1 +import 'dart:async';
2 +import 'dart:io';
3 +import 'dart:typed_data';
4 +
5 +import 'package:cw_core/utils/proxy_socket/abstract.dart';
6 +
7 +class ProxySocketSecure implements ProxySocket {
8 + final SecureSocket socket;
9 +
10 + ProxySocketSecure(this.socket);
11 +
12 + ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
13 +
14 + @override
15 + Future<void> close() => socket.close();
16 +
17 + @override
18 + Future<void> destroy() async => socket.destroy();
19 +
20 + @override
21 + Future<void> write(String data) async => socket.write(data);
22 +
23 + @override
24 + StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
25 + return socket.listen(
26 + (data) {
27 + onData(Uint8List.fromList(data));
28 + },
29 + onError: onError,
30 + onDone: onDone,
31 + cancelOnError: cancelOnError,
32 + );
33 + }
34 +}
cw_core/lib/utils/proxy_socket/socks.dart new
+36
@@ -0,0 +1,36 @@
1 +import 'dart:async';
2 +import 'dart:typed_data';
3 +
4 +import 'package:cw_core/utils/proxy_socket/abstract.dart';
5 +import 'package:socks_socket/socks_socket.dart';
6 +
7 +class ProxySocketSocks implements ProxySocket {
8 + final SOCKSSocket socket;
9 +
10 + ProxySocketSocks(this.socket);
11 +
12 + @override
13 + ProxyAddress get address => ProxyAddress(host: socket.proxyHost, port: socket.proxyPort);
14 +
15 + @override
16 + Future<void> close() => socket.close();
17 +
18 + @override
19 + Future<void> destroy() => close();
20 +
21 + @override
22 + Future<void> write(String data) async => socket.write(data);
23 +
24 + @override
25 + StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
26 + return socket.listen(
27 + (data) {
28 + onData(Uint8List.fromList(data));
29 + },
30 + onError: onError,
31 + onDone: onDone,
32 + cancelOnError: cancelOnError,
33 + );
34 + }
35 +}
36 +
cw_core/lib/utils/proxy_wrapper.dart new
+447
@@ -0,0 +1,447 @@
1 +import 'dart:async';
2 +import 'dart:convert';
3 +import 'dart:io';
4 +import 'dart:typed_data';
5 +import 'package:cw_core/utils/proxy_logger/abstract.dart';
6 +import 'package:cw_core/utils/proxy_socket/abstract.dart';
7 +import 'package:cw_core/utils/tor/abstract.dart';
8 +import 'package:cw_core/utils/tor/android.dart';
9 +import 'package:cw_core/utils/tor/disabled.dart';
10 +import 'package:http/http.dart';
11 +import 'package:socks5_proxy/socks_client.dart';
12 +import 'package:http/io_client.dart' as ioc;
13 +
14 +class ProxyWrapper {
15 + static final ProxyWrapper _proxyWrapper = ProxyWrapper._internal();
16 + static ProxyLogger? logger;
17 +
18 + factory ProxyWrapper() {
19 + return _proxyWrapper;
20 + }
21 +
22 + ProxyWrapper._internal();
23 + Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port, {Duration? connectionTimeout}) async {
24 + logger?.log(
25 + uri: Uri(
26 + scheme: sslEnabled ? "https" : "http",
27 + host: host,
28 + port: port,
29 + ),
30 + method: RequestMethod.newProxySocket,
31 + body: Uint8List(0),
32 + response: null,
33 + network: requestNetwork(),
34 + error: null
35 + );
36 + return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port), connectionTimeout: connectionTimeout);
37 + }
38 +
39 + RequestNetwork requestNetwork() {
40 + return CakeTor.instance.started ? RequestNetwork.tor : RequestNetwork.clearnet;
41 + }
42 +
43 + ioc.IOClient getHttpIOClient({int? portOverride, bool internal = false}) {
44 + if (!internal) {
45 + logger?.log(
46 + uri: null,
47 + method: RequestMethod.newHttpIOClient,
48 + body: Uint8List(0),
49 + response: null,
50 + network: requestNetwork(),
51 + error: null,
52 + );
53 + }
54 + // ignore: deprecated_member_use_from_same_package
55 + final httpClient = ProxyWrapper().getHttpClient(portOverride: portOverride, internal: true);
56 + return ioc.IOClient(httpClient);
57 + }
58 +
59 + int getPort() => CakeTor.instance.port;
60 +
61 + @Deprecated('Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
62 + HttpClient getHttpClient({int? portOverride, bool internal = false}) {
63 + if (!internal) {
64 + logger?.log(
65 + uri: null,
66 + method: RequestMethod.newProxySocket,
67 + body: Uint8List(0),
68 + response: null,
69 + network: requestNetwork(),
70 + error: null
71 + );
72 + }
73 + if (CakeTor.instance.started) {
74 + // Assign connection factory.
75 + final client = HttpClient();
76 + SocksTCPClient.assignToHttpClient(client, [
77 + ProxySettings(
78 + InternetAddress.loopbackIPv4,
79 + CakeTor.instance.port,
80 + password: null,
81 + ),
82 + ]);
83 + return client;
84 + } else {
85 + return HttpClient();
86 + }
87 + }
88 +
89 +
90 +
91 + Future<Response> _make({
92 + required RequestMethod method,
93 + required ioc.IOClient client,
94 + required Uri uri,
95 + required Map<String, String>? headers,
96 + String? body,
97 + }) async {
98 + Object? error;
99 + Response? resp;
100 + try {
101 + switch (method) {
102 + case RequestMethod.get:
103 + resp = await client. get(
104 + uri,
105 + headers: headers,
106 + );
107 + break;
108 + case RequestMethod.delete:
109 + resp = await client.delete(
110 + uri,
111 + headers: headers,
112 + body: body,
113 + );
114 + break;
115 + case RequestMethod.post:
116 + resp = await client.post(
117 + uri,
118 + headers: headers,
119 + body: body,
120 + );
121 + break;
122 + case RequestMethod.put:
123 + resp = await client.put(
124 + uri,
125 + headers: headers,
126 + body: body,
127 + );
128 + break;
129 + case RequestMethod.newHttpClient:
130 + case RequestMethod.newHttpIOClient:
131 + case RequestMethod.newProxySocket:
132 + throw UnimplementedError();
133 + }
134 + return resp;
135 + } catch (e) {
136 + error = e;
137 + rethrow;
138 + } finally {
139 + logger?.log(
140 + uri: uri,
141 + method: RequestMethod.get,
142 + body: utf8.encode(body ?? ''),
143 + response: resp,
144 + network: requestNetwork(),
145 + error: error?.toString(),
146 + );
147 + }
148 + }
149 +
150 + Future<Response> get({
151 + Map<String, String>? headers,
152 + int? portOverride,
153 + Uri? clearnetUri,
154 + Uri? onionUri,
155 + }) async {
156 + ioc.IOClient? torClient;
157 + bool torEnabled = CakeTor.instance.started;
158 +
159 + if (CakeTor.instance.started) {
160 + torEnabled = true;
161 + } else {
162 + torEnabled = false;
163 + }
164 +
165 + // if tor is enabled, try to connect to the onion url first:
166 + if (torEnabled) {
167 + try {
168 + // ignore: deprecated_member_use_from_same_package
169 + torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
170 + } catch (_) {
171 + rethrow;
172 + }
173 +
174 + if (onionUri != null) {
175 + try {
176 + return await _make(
177 + method: RequestMethod.get,
178 + client: torClient,
179 + uri: onionUri,
180 + headers: headers,
181 + );
182 + } catch (_) {
183 + rethrow;
184 + }
185 + }
186 +
187 + if (clearnetUri != null) {
188 + try {
189 + return await _make(
190 + method: RequestMethod.get,
191 + client: torClient,
192 + uri: clearnetUri,
193 + headers: headers,
194 + );
195 + } catch (_) {
196 + rethrow;
197 + }
198 + }
199 + }
200 +
201 + if (clearnetUri != null) {
202 + try {
203 + return HttpOverrides.runZoned(
204 + () async {
205 + return await _make(
206 + method: RequestMethod.get,
207 + client: ioc.IOClient(),
208 + uri: clearnetUri,
209 + headers: headers,
210 + );
211 + },
212 + );
213 + } catch (_) {
214 + // we weren't able to get a response:
215 + rethrow;
216 + }
217 + }
218 +
219 + throw Exception("Unable to connect to server");
220 + }
221 +
222 +
223 + Future<Response> post({
224 + Map<String, String>? headers,
225 + int? portOverride,
226 + Uri? clearnetUri,
227 + Uri? onionUri,
228 + String? body,
229 + bool allowMitmMoneroBypassSSLCheck = false,
230 + }) async {
231 + HttpClient? torHttpClient;
232 + HttpClient cleatnetHttpClient = HttpClient();
233 + if (allowMitmMoneroBypassSSLCheck) {
234 + cleatnetHttpClient.badCertificateCallback =
235 + ((X509Certificate cert, String host, int port) => true);
236 + }
237 +
238 + ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
239 +
240 +
241 + bool torEnabled = CakeTor.instance.started;
242 +
243 + if (torEnabled) {
244 + try {
245 + // ignore: deprecated_member_use_from_same_package
246 + torHttpClient = await getHttpClient(portOverride: portOverride);
247 + } catch (_) {
248 + rethrow;
249 + }
250 + if (allowMitmMoneroBypassSSLCheck) {
251 + torHttpClient.badCertificateCallback =
252 + ((X509Certificate cert, String host, int port) => true);
253 + }
254 + if (onionUri != null) {
255 + try {
256 + return await _make(
257 + method: RequestMethod.post,
258 + client: ioc.IOClient(torHttpClient),
259 + uri: onionUri,
260 + headers: headers,
261 + body: body,
262 + );
263 + } catch (_) {
264 + rethrow;
265 + }
266 + }
267 +
268 + if (clearnetUri != null) {
269 + try {
270 + return await _make(
271 + method: RequestMethod.post,
272 + client: ioc.IOClient(torHttpClient),
273 + uri: clearnetUri,
274 + headers: headers,
275 + body: body,
276 + );
277 + } catch (_) {
278 + rethrow;
279 + }
280 + }
281 + }
282 +
283 + if (clearnetUri != null) {
284 + try {
285 + return HttpOverrides.runZoned(
286 + () async {
287 + return await _make(
288 + method: RequestMethod.post,
289 + client: clearnetClient,
290 + uri: clearnetUri,
291 + headers: headers,
292 + body: body,
293 + );
294 + },
295 + );
296 + } catch (_) {
297 + rethrow;
298 + }
299 + }
300 +
301 + throw Exception("Unable to connect to server");
302 + }
303 +
304 + Future<Response> put({
305 + Map<String, String>? headers,
306 + int? portOverride,
307 + Uri? clearnetUri,
308 + Uri? onionUri,
309 + String? body,
310 + }) async {
311 + ioc.IOClient? torClient;
312 + bool torEnabled = CakeTor.instance.started;
313 +
314 + if (torEnabled) {
315 + try {
316 + // ignore: deprecated_member_use_from_same_package
317 + torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
318 + } catch (_) {}
319 +
320 + if (onionUri != null) {
321 + try {
322 + return await _make(
323 + method: RequestMethod.put,
324 + client: torClient!,
325 + uri: onionUri,
326 + headers: headers,
327 + body: body,
328 + );
329 + } catch (_) {
330 + rethrow;
331 + }
332 + }
333 +
334 + if (clearnetUri != null) {
335 + try {
336 + return await _make(
337 + method: RequestMethod.put,
338 + client: torClient!,
339 + uri: clearnetUri,
340 + headers: headers,
341 + body: body,
342 + );
343 + } catch (_) {
344 + rethrow;
345 + }
346 + }
347 + }
348 +
349 + if (clearnetUri != null) {
350 + try {
351 + return HttpOverrides.runZoned(
352 + () async {
353 + return await _make(
354 + method: RequestMethod.put,
355 + client: ioc.IOClient(),
356 + uri: clearnetUri,
357 + headers: headers,
358 + body: body,
359 + );
360 + },
361 + );
362 + } catch (_) {
363 + // we weren't able to get a response:
364 + rethrow;
365 + }
366 + }
367 +
368 + throw Exception("Unable to connect to server");
369 + }
370 +
371 + Future<Response> delete({
372 + Map<String, String>? headers,
373 + int? portOverride,
374 + Uri? clearnetUri,
375 + Uri? onionUri,
376 + }) async {
377 + ioc.IOClient? torClient;
378 + bool torEnabled = CakeTor.instance.started;
379 +
380 + if (CakeTor.instance.started) {
381 + torEnabled = true;
382 + } else {
383 + torEnabled = false;
384 + }
385 +
386 + // if tor is enabled, try to connect to the onion url first:
387 + if (torEnabled) {
388 + try {
389 + // ignore: deprecated_member_use_from_same_package
390 + torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
391 + } catch (_) {
392 + rethrow;
393 + }
394 +
395 + if (onionUri != null) {
396 + try {
397 + return await _make(
398 + method: RequestMethod.delete,
399 + client: torClient,
400 + uri: onionUri,
401 + headers: headers,
402 + );
403 + } catch (_) {
404 + rethrow;
405 + }
406 + }
407 +
408 + if (clearnetUri != null) {
409 + try {
410 + return await _make(
411 + method: RequestMethod.delete,
412 + client: torClient,
413 + uri: clearnetUri,
414 + headers: headers,
415 + );
416 + } catch (_) {
417 + rethrow;
418 + }
419 + }
420 + }
421 +
422 + if (clearnetUri != null) {
423 + try {
424 + return HttpOverrides.runZoned(
425 + () async {
426 + return await _make(
427 + method: RequestMethod.delete,
428 + client: ioc.IOClient(),
429 + uri: clearnetUri,
430 + headers: headers,
431 + );
432 + },
433 + );
434 + } catch (_) {
435 + // we weren't able to get a response:
436 + rethrow;
437 + }
438 + }
439 +
440 + throw Exception("Unable to connect to server");
441 + }
442 +}
443 +
444 +
445 +class CakeTor {
446 + static final CakeTorInstance instance = CakeTorInstance.getInstance();
447 +}
cw_core/lib/utils/tor/abstract.dart new
+38
@@ -0,0 +1,38 @@
1 +import 'dart:io';
2 +
3 +import 'package:cw_core/utils/print_verbose.dart';
4 +import 'package:cw_core/utils/tor/android.dart';
5 +import 'package:cw_core/utils/tor/disabled.dart';
6 +import 'package:cw_core/utils/tor/tails.dart';
7 +
8 +abstract class CakeTorInstance {
9 + bool get started;
10 +
11 + int get port => -1;
12 +
13 + bool get enabled => false;
14 +
15 + bool get bootstrapped => false;
16 +
17 + Future<void> start();
18 + Future<void> stop();
19 +
20 + static CakeTorInstance getInstance() {
21 + if (Platform.isAndroid) {
22 + return CakeTorAndroid();
23 + }
24 + if (Platform.isLinux) {
25 + try {
26 + final os = File("/etc/os-release").readAsLinesSync();
27 + for (var line in os) {
28 + if (!line.startsWith("ID=")) continue;
29 + if (!line.contains("tails")) continue;
30 + return CakeTorTails();
31 + }
32 + } catch (e) {
33 + printV("Failed to identify linux version - /etc/os-release missing");
34 + }
35 + }
36 + return CakeTorDisabled();
37 + }
38 +}
\ No newline at end of file
cw_core/lib/utils/tor/android.dart new
+73
@@ -0,0 +1,73 @@
1 +import 'dart:convert';
2 +import 'dart:io';
3 +
4 +import 'package:cw_core/utils/print_verbose.dart';
5 +import 'package:cw_core/utils/tor/abstract.dart';
6 +import 'package:path_provider/path_provider.dart';
7 +import 'package:path/path.dart' as p;
8 +import 'package:tor_binary/tor_binary_platform_interface.dart';
9 +
10 +class CakeTorAndroid implements CakeTorInstance {
11 + @override
12 + bool get bootstrapped => _proc != null;
13 +
14 + @override
15 + bool get enabled => _proc != null;
16 +
17 + @override
18 + int get port => 42142;
19 +
20 + @override
21 + Future<void> start() async {
22 + await _runEmbeddedTor();
23 + }
24 +
25 + @override
26 + bool get started => _proc != null;
27 +
28 + @override
29 + Future<void> stop() async {
30 + _proc?.kill();
31 + await _proc?.exitCode;
32 + _proc = null;
33 + }
34 +
35 + static Process? _proc;
36 +
37 + Future<void> _runEmbeddedTor() async {
38 + final dir = await getApplicationCacheDirectory();
39 +
40 + final torBinPath = p.join((await TorBinaryPlatform.instance.getBinaryPath())!, "libtor.so");
41 + printV("torPath: $torBinPath");
42 +
43 + if (started) {
44 + printV("Proxy is running");
45 + return;
46 + }
47 +
48 + printV("Starting embedded tor");
49 + printV("app docs: $dir");
50 + final torrc = """
51 +SocksPort $port
52 +Log notice file ${p.join(dir.path, "tor.log")}
53 +RunAsDaemon 0
54 +DataDirectory ${p.join(dir.path, "tor-data")}
55 +""";
56 + final torrcPath = p.join(dir.absolute.path, "torrc");
57 + File(torrcPath).writeAsStringSync(torrc);
58 +
59 + if (_proc != null) {
60 + try {
61 + _proc?.kill();
62 + await _proc?.exitCode;
63 + _proc = null;
64 + } catch (e) {
65 + printV(e);
66 + }
67 + }
68 + printV("path: $torBinPath -f $torrcPath");
69 + _proc = await Process.start(torBinPath, ["-f", torrcPath]);
70 + _proc?.stdout.transform(utf8.decoder).forEach(printV);
71 + _proc?.stderr.transform(utf8.decoder).forEach(printV);
72 + }
73 +}
\ No newline at end of file
cw_core/lib/utils/tor/disabled.dart new
+21
@@ -0,0 +1,21 @@
1 +import 'package:cw_core/utils/tor/abstract.dart';
2 +
3 +class CakeTorDisabled implements CakeTorInstance {
4 + @override
5 + bool get bootstrapped => false;
6 +
7 + @override
8 + bool get enabled => false;
9 +
10 + @override
11 + int get port => -1;
12 +
13 + @override
14 + Future<void> start() => throw UnimplementedError();
15 +
16 + @override
17 + bool get started => false;
18 +
19 + @override
20 + Future<void> stop() => throw UnimplementedError();
21 +}
\ No newline at end of file
cw_core/lib/utils/tor/tails.dart new
+21
@@ -0,0 +1,21 @@
1 +import 'package:cw_core/utils/tor/abstract.dart';
2 +
3 +class CakeTorTails implements CakeTorInstance {
4 + @override
5 + bool get bootstrapped => true;
6 +
7 + @override
8 + bool get enabled => true;
9 +
10 + @override
11 + int get port => 9150;
12 +
13 + @override
14 + Future<void> start() async {}
15 +
16 + @override
17 + bool get started => true;
18 +
19 + @override
20 + Future<void> stop() async {}
21 +}
\ No newline at end of file
cw_core/pubspec.lock
+27 -8
@@ -479,7 +479,7 @@ packages:
479 description:
480 path: "."
481 ref: cake-update-v2
482 - resolved-ref: "01cbbacbb05d2113aafa8b7c4a2bb766f749d8d8"
482 + resolved-ref: "096865a8c6b89c260beadfec04f7e184c40a3273"
483 url: "https://github.com/cake-tech/on_chain.git"
484 source: git
485 version: "3.7.0"
@@ -635,11 +635,21 @@ packages:
635 socks5_proxy:
636 dependency: "direct main"
637 description:
638 - name: socks5_proxy
639 - sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
640 - url: "https://pub.dev"
641 - source: hosted
642 - version: "1.0.6"
638 + path: "."
639 + ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
640 + resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
641 + url: "https://github.com/LacticWhale/socks_dart"
642 + source: git
643 + version: "2.1.0"
644 + socks_socket:
645 + dependency: "direct main"
646 + description:
647 + path: "."
648 + ref: e6232c53c1595469931ababa878759a067c02e94
649 + resolved-ref: e6232c53c1595469931ababa878759a067c02e94
650 + url: "https://github.com/sneurlax/socks_socket"
651 + source: git
652 + version: "1.1.1"
653 source_gen:
654 dependency: transitive
655 description:
@@ -716,10 +726,19 @@ packages:
726 dependency: transitive
727 description:
728 name: timing
719 - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
729 + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
730 url: "https://pub.dev"
731 source: hosted
722 - version: "1.0.2"
732 + version: "1.0.1"
733 + tor_binary:
734 + dependency: "direct main"
735 + description:
736 + path: "."
737 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
738 + resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
739 + url: "https://github.com/MrCyjaneK/flutter-tor_binary"
740 + source: git
741 + version: "4.7.14"
742 tuple:
743 dependency: transitive
744 description:
cw_core/pubspec.yaml
+12 -5
@@ -25,16 +25,23 @@ dependencies:
25 url: https://github.com/cake-tech/cake_backup.git
26 ref: main
27 version: 1.0.0
28 - socks5_proxy: ^1.0.4
28 + socks5_proxy:
29 + git:
30 + url: https://github.com/LacticWhale/socks_dart
31 + ref: 27ad7c2efae8d7460325c74b90f660085cbd0685
32 unorm_dart: ^0.3.0
33 on_chain:
34 git:
35 url: https://github.com/cake-tech/on_chain.git
36 ref: cake-update-v2
34 -# tor:
35 -# git:
36 -# url: https://github.com/cake-tech/tor.git
37 -# ref: main
37 + socks_socket:
38 + git:
39 + url: https://github.com/sneurlax/socks_socket
40 + ref: e6232c53c1595469931ababa878759a067c02e94
41 + tor_binary:
42 + git:
43 + url: https://github.com/MrCyjaneK/flutter-tor_binary
44 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
45
46 dev_dependencies:
47 flutter_test:
cw_decred/pubspec.lock
+24 -5
@@ -666,11 +666,21 @@ packages:
666 socks5_proxy:
667 dependency: transitive
668 description:
669 - name: socks5_proxy
670 - sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
671 - url: "https://pub.dev"
672 - source: hosted
673 - version: "1.0.6"
669 + path: "."
670 + ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
671 + resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
672 + url: "https://github.com/LacticWhale/socks_dart"
673 + source: git
674 + version: "2.1.0"
675 + socks_socket:
676 + dependency: transitive
677 + description:
678 + path: "."
679 + ref: e6232c53c1595469931ababa878759a067c02e94
680 + resolved-ref: e6232c53c1595469931ababa878759a067c02e94
681 + url: "https://github.com/sneurlax/socks_socket"
682 + source: git
683 + version: "1.1.1"
684 source_gen:
685 dependency: transitive
686 description:
@@ -751,6 +761,15 @@ packages:
761 url: "https://pub.dev"
762 source: hosted
763 version: "1.0.2"
764 + tor_binary:
765 + dependency: transitive
766 + description:
767 + path: "."
768 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
769 + resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
770 + url: "https://github.com/MrCyjaneK/flutter-tor_binary"
771 + source: git
772 + version: "4.7.14"
773 tuple:
774 dependency: transitive
775 description:
cw_ethereum/lib/ethereum_client.dart
+2 -2
@@ -19,7 +19,7 @@ class EthereumClient extends EVMChainClient {
19 Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
20 {String? contractAddress}) async {
21 try {
22 - final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
22 + final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
23 "chainid": "$chainId",
24 "module": "account",
25 "action": contractAddress != null ? "tokentx" : "txlist",
@@ -51,7 +51,7 @@ class EthereumClient extends EVMChainClient {
51 @override
52 Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
53 try {
54 - final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
54 + final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
55 "chainid": "$chainId",
56 "module": "account",
57 "action": "txlistinternal",
cw_evm/lib/evm_chain_client.dart
+4 -4
@@ -5,6 +5,7 @@ import 'dart:developer';
5 import 'package:cw_core/crypto_currency.dart';
6 import 'package:cw_core/erc20_token.dart';
7 import 'package:cw_core/node.dart';
8 +import 'package:cw_core/utils/proxy_wrapper.dart';
9 import 'package:cw_evm/evm_chain_transaction_model.dart';
10 import 'package:cw_evm/evm_chain_transaction_priority.dart';
11 import 'package:cw_evm/evm_erc20_balance.dart';
@@ -12,13 +13,12 @@ import 'package:cw_evm/pending_evm_chain_transaction.dart';
13 import 'package:cw_evm/.secrets.g.dart' as secrets;
14 import 'package:flutter/foundation.dart';
15 import 'package:hex/hex.dart' as hex;
15 -import 'package:http/http.dart';
16 import 'package:web3dart/web3dart.dart';
17
18 import 'contract/erc20.dart';
19
20 abstract class EVMChainClient {
21 - final httpClient = Client();
21 + late final client = ProxyWrapper().getHttpIOClient();
22 Web3Client? _client;
23
24 //! To be overridden by all child classes
@@ -47,7 +47,7 @@ abstract class EVMChainClient {
47 }
48
49 _client =
50 - Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), httpClient);
50 + Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
51
52 return true;
53 } catch (e) {
@@ -353,7 +353,7 @@ abstract class EVMChainClient {
353 },
354 );
355
356 - final response = await httpClient.get(
356 + final response = await client.get(
357 uri,
358 headers: {
359 "Accept": "application/json",
cw_monero/lib/api/wallet.dart
+20 -3
@@ -146,7 +146,25 @@ int getUnlockedBalance({int accountIndex = 0}) =>
146
147 int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 0;
148
149 -int getNodeHeightSync() => currentWallet?.daemonBlockChainHeight() ?? 0;
149 +
150 +int cachedNodeHeight = 0;
151 +bool isHeightRefreshing = false;
152 +int getNodeHeightSync() {
153 + if (isHeightRefreshing == false) {
154 + (() async {
155 + try {
156 + isHeightRefreshing = true;
157 + final wptrAddress = currentWallet!.ffiAddress();
158 + cachedNodeHeight = await Isolate.run(() async {
159 + return monero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
160 + });
161 + } finally {
162 + isHeightRefreshing = false;
163 + }
164 + })();
165 + }
166 + return cachedNodeHeight;
167 +}
168
169 bool isConnectedSync() => currentWallet?.connected() != 0;
170
@@ -202,7 +220,6 @@ Future<bool> setupNodeSync(
220 }
221
222 void startRefreshSync() {
205 - currentWallet!.refreshAsync();
223 currentWallet!.startRefresh();
224 }
225
@@ -228,7 +245,7 @@ void storeSync({bool force = false}) async {
245 return monero.Wallet_synchronized(Pointer.fromAddress(addr));
246 });
247 if (lastStorePointer == addr &&
231 - lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
248 + lastStoreHeight + 75000 > currentWallet!.blockChainHeight() &&
249 !synchronized &&
250 !force) {
251 return;
cw_monero/lib/monero_wallet.dart
+10 -1
@@ -17,6 +17,7 @@ import 'package:cw_core/pending_transaction.dart';
17 import 'package:cw_core/sync_status.dart';
18 import 'package:cw_core/transaction_direction.dart';
19 import 'package:cw_core/unspent_coins_info.dart';
20 +import 'package:cw_core/utils/proxy_wrapper.dart';
21 import 'package:cw_core/utils/print_verbose.dart';
22 import 'package:cw_core/wallet_base.dart';
23 import 'package:cw_core/wallet_info.dart';
@@ -207,6 +208,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
208
209 @override
210 Future<void> connectToNode({required Node node}) async {
211 + String socksProxy = node.socksProxyAddress ?? '';
212 + printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
213 + printV(" enabled: ${CakeTor.instance.enabled}");
214 + printV(" port: ${CakeTor.instance.port}");
215 + printV(" started: ${CakeTor.instance.started}");
216 + if (CakeTor.instance.enabled) {
217 + socksProxy = "127.0.0.1:${CakeTor.instance.port}";
218 + }
219 try {
220 syncStatus = ConnectingSyncStatus();
221 await monero_wallet.setupNodeSync(
@@ -216,7 +225,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
225 useSSL: node.isSSL,
226 isLightWallet: false,
227 // FIXME: hardcoded value
219 - socksProxyAddress: node.socksProxyAddress);
228 + socksProxyAddress: socksProxy);
229
230 await monero_wallet.setTrustedDaemon(node.trusted);
231 syncStatus = ConnectedSyncStatus();
cw_monero/pubspec.lock
+31 -12
@@ -66,11 +66,11 @@ packages:
66 dependency: transitive
67 description:
68 path: "."
69 - ref: cake-update-v2
70 - resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
69 + ref: cake-update-v4
70 + resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
71 url: "https://github.com/cake-tech/blockchain_utils"
72 source: git
73 - version: "3.3.0"
73 + version: "4.3.0"
74 bluez:
75 dependency: transitive
76 description:
@@ -599,10 +599,10 @@ packages:
599 description:
600 path: "."
601 ref: cake-update-v2
602 - resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
602 + resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
603 url: "https://github.com/cake-tech/on_chain.git"
604 source: git
605 - version: "3.7.0"
605 + version: "6.2.0"
606 package_config:
607 dependency: transitive
608 description:
@@ -779,11 +779,21 @@ packages:
779 socks5_proxy:
780 dependency: transitive
781 description:
782 - name: socks5_proxy
783 - sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
784 - url: "https://pub.dev"
785 - source: hosted
786 - version: "1.0.6"
782 + path: "."
783 + ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
784 + resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
785 + url: "https://github.com/LacticWhale/socks_dart"
786 + source: git
787 + version: "2.1.0"
788 + socks_socket:
789 + dependency: transitive
790 + description:
791 + path: "."
792 + ref: e6232c53c1595469931ababa878759a067c02e94
793 + resolved-ref: e6232c53c1595469931ababa878759a067c02e94
794 + url: "https://github.com/sneurlax/socks_socket"
795 + source: git
796 + version: "1.1.1"
797 source_gen:
798 dependency: transitive
799 description:
@@ -860,10 +870,19 @@ packages:
870 dependency: transitive
871 description:
872 name: timing
863 - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
873 + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
874 url: "https://pub.dev"
875 source: hosted
866 - version: "1.0.2"
876 + version: "1.0.1"
877 + tor_binary:
878 + dependency: transitive
879 + description:
880 + path: "."
881 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
882 + resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
883 + url: "https://github.com/MrCyjaneK/flutter-tor_binary"
884 + source: git
885 + version: "4.7.14"
886 tuple:
887 dependency: transitive
888 description:
cw_nano/lib/nano_client.dart
+54 -45
@@ -3,11 +3,11 @@ import 'dart:convert';
3
4 import 'package:cw_core/nano_account_info_response.dart';
5 import 'package:cw_core/utils/print_verbose.dart';
6 +import 'package:cw_core/utils/proxy_wrapper.dart';
7 import 'package:cw_nano/nano_block_info_response.dart';
8 import 'package:cw_core/n2_node.dart';
9 import 'package:cw_nano/nano_balance.dart';
10 import 'package:cw_nano/nano_transaction_model.dart';
10 -import 'package:http/http.dart' as http;
11 import 'package:cw_core/node.dart';
12 import 'package:nanoutil/nanoutil.dart';
13 import 'package:shared_preferences/shared_preferences.dart';
@@ -66,8 +66,8 @@ class NanoClient {
66 }
67
68 Future<NanoBalance> getBalance(String address) async {
69 - final response = await http.post(
70 - _node!.uri,
69 + final response = await ProxyWrapper().post(
70 + clearnetUri: _node!.uri,
71 headers: getHeaders(_node!.uri.host),
72 body: jsonEncode(
73 {
@@ -76,7 +76,8 @@ class NanoClient {
76 },
77 ),
78 );
79 - final data = await jsonDecode(response.body);
79 +
80 + final data = jsonDecode(response.body) as Map<String, dynamic>;
81 if (response.statusCode != 200 ||
82 data["error"] != null ||
83 data["balance"] == null ||
@@ -93,8 +94,8 @@ class NanoClient {
94
95 Future<AccountInfoResponse?> getAccountInfo(String address) async {
96 try {
96 - final response = await http.post(
97 - _node!.uri,
97 + final response = await ProxyWrapper().post(
98 + clearnetUri: _node!.uri,
99 headers: getHeaders(_node!.uri.host),
100 body: jsonEncode(
101 {
@@ -104,8 +105,9 @@ class NanoClient {
105 },
106 ),
107 );
107 - final data = await jsonDecode(response.body);
108 - return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
108 +
109 + final data = jsonDecode(response.body) as Map<String, dynamic>;
110 + return AccountInfoResponse.fromJson(data);
111 } catch (e) {
112 printV("error while getting account info $e");
113 return null;
@@ -114,8 +116,8 @@ class NanoClient {
116
117 Future<BlockContentsResponse?> getBlockContents(String block) async {
118 try {
117 - final response = await http.post(
118 - _node!.uri,
119 + final response = await ProxyWrapper().post(
120 + clearnetUri: _node!.uri,
121 headers: getHeaders(_node!.uri.host),
122 body: jsonEncode(
123 {
@@ -125,7 +127,8 @@ class NanoClient {
127 },
128 ),
129 );
128 - final data = await jsonDecode(response.body);
130 +
131 + final data = jsonDecode(response.body) as Map<String, dynamic>;
132 return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
133 } catch (e) {
134 printV("error while getting block info $e");
@@ -181,8 +184,8 @@ class NanoClient {
184 }
185
186 Future<String> requestWork(String hash) async {
184 - final response = await http.post(
185 - _powNode!.uri,
187 + final response = await ProxyWrapper().post(
188 + clearnetUri: _powNode!.uri,
189 headers: getHeaders(_powNode!.uri.host),
190 body: json.encode(
191 {
@@ -191,8 +194,9 @@ class NanoClient {
194 },
195 ),
196 );
197 +
198 if (response.statusCode == 200) {
195 - final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
199 + final decoded = jsonDecode(response.body) as Map<String, dynamic>;
200 if (decoded.containsKey("error")) {
201 throw Exception("Received error ${decoded["error"]}");
202 }
@@ -224,13 +228,13 @@ class NanoClient {
228 "block": block,
229 });
230
227 - final processResponse = await http.post(
228 - _node!.uri,
231 + final processResponse = await ProxyWrapper().post(
232 + clearnetUri: _node!.uri,
233 headers: getHeaders(_node!.uri.host),
234 body: processBody,
235 );
236
233 - final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
237 + final Map<String, dynamic> decoded = jsonDecode(processResponse.body) as Map<String, dynamic>;
238 if (decoded.containsKey("error")) {
239 throw Exception("Received error ${decoded["error"]}");
240 }
@@ -423,12 +427,11 @@ class NanoClient {
427 "subtype": "receive",
428 "block": receiveBlock,
429 });
426 - final processResponse = await http.post(
427 - _node!.uri,
430 + final processResponse = await ProxyWrapper().post(
431 + clearnetUri: _node!.uri,
432 headers: getHeaders(_node!.uri.host),
433 body: processBody,
434 );
431 -
435 final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
436 if (decoded.containsKey("error")) {
437 throw Exception("Received error ${decoded["error"]}");
@@ -440,16 +443,17 @@ class NanoClient {
443 required String destinationAddress,
444 required String privateKey,
445 }) async {
443 - final receivableResponse = await http.post(_node!.uri,
444 - headers: getHeaders(_node!.uri.host),
445 - body: jsonEncode({
446 - "action": "receivable",
447 - "account": destinationAddress,
448 - "count": "-1",
449 - "source": true,
450 - }));
451 -
452 - final receivableData = await jsonDecode(receivableResponse.body);
446 + final receivableResponse = await ProxyWrapper().post(
447 + clearnetUri: _node!.uri,
448 + headers: getHeaders(_node!.uri.host),
449 + body: jsonEncode({
450 + "action": "receivable",
451 + "account": destinationAddress,
452 + "count": "-1",
453 + "source": true,
454 + }),
455 + );
456 + final receivableData = jsonDecode(receivableResponse.body) as Map<String, dynamic>;
457 if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
458 return 0;
459 }
@@ -492,15 +496,18 @@ class NanoClient {
496
497 Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
498 try {
495 - final response = await http.post(_node!.uri,
496 - headers: getHeaders(_node!.uri.host),
497 - body: jsonEncode({
498 - "action": "account_history",
499 - "account": address,
500 - "count": "100",
501 - // "raw": true,
502 - }));
503 - final data = await jsonDecode(response.body);
499 + final response = await ProxyWrapper().post(
500 + clearnetUri: _node!.uri,
501 + headers: getHeaders(_node!.uri.host),
502 + body: jsonEncode({
503 + "action": "account_history",
504 + "account": address,
505 + "count": "100",
506 + // "raw": true,
507 + }),
508 + );
509 +
510 + final data = jsonDecode(response.body) as Map<String, dynamic>;
511 final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
512
513 // Map the transactions list to NanoTransactionModel using the factory
@@ -516,13 +523,14 @@ class NanoClient {
523
524 Future<List<N2Node>> getN2Reps() async {
525 final uri = Uri.parse(N2_REPS_ENDPOINT);
519 - final response = await http.post(
520 - uri,
526 + final response = await ProxyWrapper().post(
527 + clearnetUri: uri,
528 headers: getHeaders(uri.host),
529 body: jsonEncode({"action": "reps"}),
530 );
531 try {
525 - final List<N2Node> nodes = (json.decode(response.body) as List<dynamic>)
532 +
533 + final List<N2Node> nodes = (jsonDecode(response.body) as List<dynamic>)
534 .map((dynamic e) => N2Node.fromJson(e as Map<String, dynamic>))
535 .toList();
536 return nodes;
@@ -533,8 +541,8 @@ class NanoClient {
541
542 Future<int> getRepScore(String rep) async {
543 final uri = Uri.parse(N2_REPS_ENDPOINT);
536 - final response = await http.post(
537 - uri,
544 + final response = await ProxyWrapper().post(
545 + clearnetUri: uri,
546 headers: getHeaders(uri.host),
547 body: jsonEncode({
548 "action": "rep_info",
@@ -542,7 +550,8 @@ class NanoClient {
550 }),
551 );
552 try {
545 - final N2Node node = N2Node.fromJson(json.decode(response.body) as Map<String, dynamic>);
553 +
554 + final N2Node node = N2Node.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
555 return node.score ?? 100;
556 } catch (error) {
557 return 100;
cw_nano/pubspec.lock
+26 -7
@@ -784,11 +784,21 @@ packages:
784 socks5_proxy:
785 dependency: transitive
786 description:
787 - name: socks5_proxy
788 - sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
789 - url: "https://pub.dev"
790 - source: hosted
791 - version: "1.0.6"
787 + path: "."
788 + ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
789 + resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
790 + url: "https://github.com/LacticWhale/socks_dart"
791 + source: git
792 + version: "2.1.0"
793 + socks_socket:
794 + dependency: transitive
795 + description:
796 + path: "."
797 + ref: e6232c53c1595469931ababa878759a067c02e94
798 + resolved-ref: e6232c53c1595469931ababa878759a067c02e94
799 + url: "https://github.com/sneurlax/socks_socket"
800 + source: git
801 + version: "1.1.1"
802 source_gen:
803 dependency: transitive
804 description:
@@ -865,10 +875,19 @@ packages:
875 dependency: transitive
876 description:
877 name: timing
868 - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
878 + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
879 url: "https://pub.dev"
880 source: hosted
871 - version: "1.0.2"
881 + version: "1.0.1"
882 + tor_binary:
883 + dependency: transitive
884 + description:
885 + path: "."
886 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
887 + resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
888 + url: "https://github.com/MrCyjaneK/flutter-tor_binary"
889 + source: git
890 + version: "4.7.14"
891 tuple:
892 dependency: transitive
893 description:
cw_polygon/lib/polygon_client.dart
+2 -2
@@ -40,7 +40,7 @@ class PolygonClient extends EVMChainClient {
40 Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
41 {String? contractAddress}) async {
42 try {
43 - final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
43 + final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
44 "chainid": "$chainId",
45 "module": "account",
46 "action": contractAddress != null ? "tokentx" : "txlist",
@@ -68,7 +68,7 @@ class PolygonClient extends EVMChainClient {
68 @override
69 Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
70 try {
71 - final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
71 + final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
72 "chainid": "$chainId",
73 "module": "account",
74 "action": "txlistinternal",
cw_solana/lib/solana_client.dart
+33 -21
@@ -2,8 +2,10 @@ import 'dart:async';
2 import 'dart:convert';
3 import 'dart:math' as math;
4
5 +import 'package:blockchain_utils/blockchain_utils.dart';
6 import 'package:cw_core/crypto_currency.dart';
7 import 'package:cw_core/node.dart';
8 +import 'package:cw_core/utils/proxy_wrapper.dart';
9 import 'package:cw_core/solana_rpc_http_service.dart';
10 import 'package:cw_core/utils/print_verbose.dart';
11 import 'package:cw_solana/pending_solana_transaction.dart';
@@ -11,7 +13,6 @@ import 'package:cw_solana/solana_balance.dart';
13 import 'package:cw_solana/solana_exceptions.dart';
14 import 'package:cw_solana/solana_transaction_model.dart';
15 import 'package:cw_solana/spl_token.dart';
14 -import 'package:http/http.dart' as http;
16 import 'package:on_chain/solana/solana.dart';
17 import 'package:on_chain/solana/src/instructions/associated_token_account/constant.dart';
18 import 'package:on_chain/solana/src/models/pda/pda.dart';
@@ -20,10 +21,10 @@ import 'package:on_chain/solana/src/rpc/models/models/confirmed_transaction_meta
21 import '.secrets.g.dart' as secrets;
22
23 class SolanaWalletClient {
23 - final httpClient = http.Client();
24 - SolanaRPC? _provider;
24 // Minimum amount in SOL to consider a transaction valid (to filter spam)
25 static const double minValidAmount = 0.00000003;
26 + final httpClient = ProxyWrapper().getHttpClient();
27 + SolanaRPC? _provider;
28
29 bool connect(Node node) {
30 try {
@@ -86,7 +87,8 @@ class SolanaWalletClient {
87 }
88 }
89
89 - Future<SolanaBalance?> getSplTokenBalance(String mintAddress, String walletAddress) async {
90 + Future<SolanaBalance?> getSplTokenBalance(
91 + String mintAddress, String walletAddress) async {
92 // Fetch the token accounts (a token can have multiple accounts for various uses)
93 final tokenAccounts = await getSPLTokenAccounts(mintAddress, walletAddress);
94
@@ -122,14 +124,16 @@ class SolanaWalletClient {
124 ),
125 );
126
125 - final fee = (feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
127 + final fee =
128 + (feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
129 return fee;
130 } catch (_) {
131 return 0.0;
132 }
133 }
134
132 - Future<double> getEstimatedFee(SolanaPublicKey publicKey, Commitment commitment) async {
135 + Future<double> getEstimatedFee(
136 + SolanaPublicKey publicKey, Commitment commitment) async {
137 final message = await _getMessageForNativeTransaction(
138 publicKey: publicKey,
139 destinationAddress: publicKey.toAddress().address,
@@ -429,17 +433,20 @@ class SolanaWalletClient {
433 }
434 }));
435
432 - final versionedBatchResponses = batchResponses.whereType<VersionedTransactionResponse>();
436 + final versionedBatchResponses =
437 + batchResponses.whereType<VersionedTransactionResponse>();
438
434 - final parsedTransactionsFutures = versionedBatchResponses.map((tx) => parseTransaction(
435 - txResponse: tx,
436 - splTokenSymbol: splTokenSymbol,
437 - walletAddress: walletAddress?.address ?? address.address,
438 - ));
439 + final parsedTransactionsFutures =
440 + versionedBatchResponses.map((tx) => parseTransaction(
441 + txResponse: tx,
442 + splTokenSymbol: splTokenSymbol,
443 + walletAddress: walletAddress?.address ?? address.address,
444 + ));
445
446 final parsedTransactions = await Future.wait(parsedTransactionsFutures);
447
442 - transactions.addAll(parsedTransactions.whereType<SolanaTransactionModel>().toList());
448 + transactions.addAll(
449 + parsedTransactions.whereType<SolanaTransactionModel>().toList());
450
451 // Only update UI if we have new valid transactions
452 if (parsedTransactions.isNotEmpty) {
@@ -508,8 +515,8 @@ class SolanaWalletClient {
515 }
516
517 Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
511 - final programAddress =
512 - MetaplexTokenMetaDataProgramUtils.findMetadataPda(mint: SolAddress(mintAddress));
518 + final programAddress = MetaplexTokenMetaDataProgramUtils.findMetadataPda(
519 + mint: SolAddress(mintAddress));
520
521 final token = await _provider!.request(
522 SolanaRPCGetMetadataAccount(
@@ -530,8 +537,9 @@ class SolanaWalletClient {
537 // iconPath = await _client.getIconImageFromTokenUri(metadata.uri);
538 // } catch (_) {}
539
533 - String filteredTokenSymbol =
534 - metadata.symbol.replaceFirst(RegExp('^\\\$'), '').replaceAll('\u0000', '');
540 + String filteredTokenSymbol = metadata.symbol
541 + .replaceFirst(RegExp('^\\\$'), '')
542 + .replaceAll('\u0000', '');
543
544 return SPLToken.fromMetadata(
545 name: metadata.name,
@@ -647,7 +655,8 @@ class SolanaWalletClient {
655 return message;
656 }
657
650 - Future<double> _getFeeFromCompiledMessage(Message message, Commitment commitment) async {
658 + Future<double> _getFeeFromCompiledMessage(
659 + Message message, Commitment commitment) async {
660 final base64Message = base64Encode(message.serialize());
661
662 final fee = await getFeeForMessage(base64Message, commitment);
@@ -786,7 +795,8 @@ class SolanaWalletClient {
795 required SolAddress mintAddress,
796 required bool shouldCreateATA,
797 }) async {
789 - final associatedTokenAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
798 + final associatedTokenAccount =
799 + AssociatedTokenAccountProgramUtils.associatedTokenAccount(
800 mint: mintAddress,
801 owner: ownerAddress,
802 );
@@ -857,7 +867,8 @@ class SolanaWalletClient {
867 final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();
868 ProgramDerivedAddress? associatedSenderAccount;
869 try {
860 - associatedSenderAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
870 + associatedSenderAccount =
871 + AssociatedTokenAccountProgramUtils.associatedTokenAccount(
872 mint: mintAddress,
873 owner: ownerPrivateKey.publicKey().toAddress(),
874 );
@@ -992,7 +1003,8 @@ class SolanaWalletClient {
1003 if (uri.isEmpty || uri == '…') return null;
1004
1005 try {
995 - final response = await httpClient.get(Uri.parse(uri));
1006 + final client = ProxyWrapper().getHttpIOClient();
1007 + final response = await client.get(Uri.parse(uri));
1008
1009 final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
1010
cw_tron/lib/tron_client.dart
+5 -4
@@ -5,6 +5,7 @@ import 'dart:developer';
5 import 'package:blockchain_utils/blockchain_utils.dart';
6 import 'package:cw_core/crypto_currency.dart';
7 import 'package:cw_core/node.dart';
8 +import 'package:cw_core/utils/proxy_wrapper.dart';
9 import 'package:cw_tron/pending_tron_transaction.dart';
10 import 'package:cw_tron/tron_abi.dart';
11 import 'package:cw_tron/tron_balance.dart';
@@ -13,12 +14,12 @@ import 'package:cw_tron/tron_token.dart';
14 import 'package:cw_tron/tron_transaction_model.dart';
15 import 'package:flutter/foundation.dart';
16 import 'package:flutter/services.dart';
16 -import 'package:http/http.dart';
17 import '.secrets.g.dart' as secrets;
18 import 'package:on_chain/on_chain.dart';
19
20 class TronClient {
21 - final httpClient = Client();
21 + late final client = ProxyWrapper().getHttpIOClient();
22 +
23 TronProvider? _provider;
24 // This is an internal tracker, so we don't have to "refetch".
25 int _nativeTxEstimatedFee = 0;
@@ -28,7 +29,7 @@ class TronClient {
29 Future<List<TronTransactionModel>> fetchTransactions(String address,
30 {String? contractAddress}) async {
31 try {
31 - final response = await httpClient.get(
32 + final response = await client.get(
33 Uri.https(
34 "api.trongrid.io",
35 "/v1/accounts/$address/transactions",
@@ -61,7 +62,7 @@ class TronClient {
62
63 Future<List<TronTRC20TransactionModel>> fetchTrc20ExcludedTransactions(String address) async {
64 try {
64 - final response = await httpClient.get(
65 + final response = await client.get(
66 Uri.https(
67 "api.trongrid.io",
68 "/v1/accounts/$address/transactions/trc20",
cw_tron/lib/tron_http_provider.dart
+12 -9
@@ -1,22 +1,22 @@
1 import 'dart:convert';
2
3 -import 'package:http/http.dart' as http;
3 +import 'package:cw_core/utils/proxy_wrapper.dart';
4 import 'package:on_chain/tron/tron.dart';
5 import '.secrets.g.dart' as secrets;
6
7 class TronHTTPProvider implements TronServiceProvider {
8 TronHTTPProvider(
9 {required this.url,
10 - http.Client? client,
11 - this.defaultRequestTimeout = const Duration(seconds: 30)})
12 - : client = client ?? http.Client();
10 + this.defaultRequestTimeout = const Duration(seconds: 30)});
11 +
12 @override
13 final String url;
15 - final http.Client client;
14 + late final client = ProxyWrapper().getHttpIOClient();
15 final Duration defaultRequestTimeout;
16
17 @override
19 - Future<Map<String, dynamic>> get(TronRequestDetails params, [Duration? timeout]) async {
18 + Future<Map<String, dynamic>> get(TronRequestDetails params,
19 + [Duration? timeout]) async {
20 final response = await client.get(Uri.parse(params.url(url)), headers: {
21 'Content-Type': 'application/json',
22 if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
@@ -27,13 +27,16 @@ class TronHTTPProvider implements TronServiceProvider {
27 }
28
29 @override
30 - Future<Map<String, dynamic>> post(TronRequestDetails params, [Duration? timeout]) async {
30 + Future<Map<String, dynamic>> post(TronRequestDetails params,
31 + [Duration? timeout]) async {
32 final response = await client
33 .post(Uri.parse(params.url(url)),
34 headers: {
35 'Content-Type': 'application/json',
35 - if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
36 - if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
36 + if (url.contains("trongrid"))
37 + 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
38 + if (url.contains("nownodes"))
39 + 'api-key': secrets.tronNowNodesApiKey,
40 },
41 body: params.toRequestBody())
42 .timeout(timeout ?? defaultRequestTimeout);
cw_wownero/lib/api/wallet.dart
+11 -2
@@ -110,7 +110,16 @@ int getUnlockedBalance({int accountIndex = 0}) =>
110
111 int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
112
113 -int getNodeHeightSync() => wownero.Wallet_daemonBlockChainHeight(wptr!);
113 +int cachedNodeHeight = 0;
114 +int getNodeHeightSync() {
115 + (() async {
116 + final wptrAddress = wptr!.address;
117 + cachedNodeHeight = await Isolate.run(() async {
118 + return wownero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
119 + });
120 + })();
121 + return cachedNodeHeight;
122 +}
123
124 bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
125
@@ -154,7 +163,7 @@ Future<bool> setupNodeSync(
163 }
164
165 void startRefreshSync() {
157 - wownero.Wallet_refreshAsync(wptr!);
166 + // wownero.Wallet_refreshAsync(wptr!);
167 wownero.Wallet_startRefresh(wptr!);
168 }
169
cw_wownero/lib/wownero_wallet.dart
+10 -1
@@ -16,6 +16,7 @@ import 'package:cw_core/transaction_direction.dart';
16 import 'package:cw_core/transaction_priority.dart';
17 import 'package:cw_core/unspent_coins_info.dart';
18 import 'package:cw_core/utils/print_verbose.dart';
19 +import 'package:cw_core/utils/proxy_wrapper.dart';
20 import 'package:cw_core/wallet_base.dart';
21 import 'package:cw_core/wallet_info.dart';
22 import 'package:cw_core/wownero_amount_format.dart';
@@ -183,6 +184,14 @@ abstract class WowneroWalletBase
184
185 @override
186 Future<void> connectToNode({required Node node}) async {
187 + String socksProxy = node.socksProxyAddress ?? '';
188 + printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
189 + printV(" enabled: ${CakeTor.instance.enabled}");
190 + printV(" port: ${CakeTor.instance.port}");
191 + printV(" started: ${CakeTor.instance.started}");
192 + if (CakeTor.instance.enabled) {
193 + socksProxy = "127.0.0.1:${CakeTor.instance.port}";
194 + }
195 try {
196 syncStatus = ConnectingSyncStatus();
197 await wownero_wallet.setupNode(
@@ -192,7 +201,7 @@ abstract class WowneroWalletBase
201 useSSL: node.isSSL,
202 isLightWallet: false,
203 // FIXME: hardcoded value
195 - socksProxyAddress: node.socksProxyAddress);
204 + socksProxyAddress: socksProxy);
205
206 wownero_wallet.setTrustedDaemon(node.trusted);
207 syncStatus = ConnectedSyncStatus();
cw_wownero/pubspec.lock
+26 -7
@@ -683,11 +683,21 @@ packages:
683 socks5_proxy:
684 dependency: transitive
685 description:
686 - name: socks5_proxy
687 - sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
688 - url: "https://pub.dev"
689 - source: hosted
690 - version: "1.0.6"
686 + path: "."
687 + ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
688 + resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
689 + url: "https://github.com/LacticWhale/socks_dart"
690 + source: git
691 + version: "2.1.0"
692 + socks_socket:
693 + dependency: transitive
694 + description:
695 + path: "."
696 + ref: e6232c53c1595469931ababa878759a067c02e94
697 + resolved-ref: e6232c53c1595469931ababa878759a067c02e94
698 + url: "https://github.com/sneurlax/socks_socket"
699 + source: git
700 + version: "1.1.1"
701 source_gen:
702 dependency: transitive
703 description:
@@ -764,10 +774,19 @@ packages:
774 dependency: transitive
775 description:
776 name: timing
767 - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
777 + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
778 url: "https://pub.dev"
779 source: hosted
770 - version: "1.0.2"
780 + version: "1.0.1"
781 + tor_binary:
782 + dependency: transitive
783 + description:
784 + path: "."
785 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
786 + resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
787 + url: "https://github.com/MrCyjaneK/flutter-tor_binary"
788 + source: git
789 + version: "4.7.14"
790 tuple:
791 dependency: transitive
792 description:
cw_zano/pubspec.lock
+24 -5
@@ -680,11 +680,21 @@ packages:
680 socks5_proxy:
681 dependency: transitive
682 description:
683 - name: socks5_proxy
684 - sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
685 - url: "https://pub.dev"
686 - source: hosted
687 - version: "1.0.6"
683 + path: "."
684 + ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
685 + resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
686 + url: "https://github.com/LacticWhale/socks_dart"
687 + source: git
688 + version: "2.1.0"
689 + socks_socket:
690 + dependency: transitive
691 + description:
692 + path: "."
693 + ref: e6232c53c1595469931ababa878759a067c02e94
694 + resolved-ref: e6232c53c1595469931ababa878759a067c02e94
695 + url: "https://github.com/sneurlax/socks_socket"
696 + source: git
697 + version: "1.1.1"
698 source_gen:
699 dependency: transitive
700 description:
@@ -765,6 +775,15 @@ packages:
775 url: "https://pub.dev"
776 source: hosted
777 version: "1.0.2"
778 + tor_binary:
779 + dependency: transitive
780 + description:
781 + path: "."
782 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
783 + resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
784 + url: "https://github.com/MrCyjaneK/flutter-tor_binary"
785 + source: git
786 + version: "4.7.14"
787 tuple:
788 dependency: transitive
789 description:
integration_test/robots/wallet_keys_robot.dart
+29 -13
@@ -70,7 +70,10 @@ class WalletKeysAndSeedPageRobot {
70 if (walletType == WalletType.bitcoin ||
71 walletType == WalletType.litecoin ||
72 walletType == WalletType.bitcoinCash) {
73 - commonTestCases.hasText(appStore.wallet!.seed!);
73 + final seedWords = appStore.wallet!.seed!.split(" ");
74 + for (var seedWord in seedWords) {
75 + commonTestCases.hasTextAtLestOnce(seedWord);
76 + }
77 tester.printToConsole('$walletName wallet has seeds properly displayed');
78 }
79
@@ -78,10 +81,14 @@ class WalletKeysAndSeedPageRobot {
81 walletType == WalletType.solana ||
82 walletType == WalletType.tron) {
83 if (hasSeed) {
81 - commonTestCases.hasText(appStore.wallet!.seed!);
84 + final seedWords = appStore.wallet!.seed!.split(" ");
85 + for (var seedWord in seedWords) {
86 + commonTestCases.hasTextAtLestOnce(seedWord);
87 + }
88 tester.printToConsole('$walletName wallet has seeds properly displayed');
89 }
90 if (hasPrivateKey) {
91 + await commonTestCases.tapItemByKey('wallet_keys_page_keys');
92 commonTestCases.hasText(appStore.wallet!.privateKey!);
93 tester.printToConsole('$walletName wallet has private key properly displayed');
94 }
@@ -89,14 +96,19 @@ class WalletKeysAndSeedPageRobot {
96
97 if (walletType == WalletType.nano || walletType == WalletType.banano) {
98 if (hasSeed) {
92 - commonTestCases.hasText(appStore.wallet!.seed!);
99 + final seedWords = appStore.wallet!.seed!.split(" ");
100 + for (var seedWord in seedWords) {
101 + commonTestCases.hasTextAtLestOnce(seedWord);
102 + }
103 tester.printToConsole('$walletName wallet has seeds properly displayed');
104 }
105 if (hasHexSeed) {
106 + await commonTestCases.tapItemByKey('wallet_keys_page_keys');
107 commonTestCases.hasText(appStore.wallet!.hexSeed!);
108 tester.printToConsole('$walletName wallet has hexSeed properly displayed');
109 }
110 if (hasPrivateKey) {
111 + await commonTestCases.tapItemByKey('wallet_keys_page_keys');
112 commonTestCases.hasText(appStore.wallet!.privateKey!);
113 tester.printToConsole('$walletName wallet has private key properly displayed');
114 }
@@ -129,35 +141,39 @@ class WalletKeysAndSeedPageRobot {
141 final hasSeedLegacy = Polyseed.isValidSeed(seed);
142
143 if (hasPublicSpendKey) {
144 + await commonTestCases.tapItemByKey('wallet_keys_page_keys');
145 commonTestCases.hasText(keys.publicSpendKey);
146 tester.printToConsole('$walletName wallet has public spend key properly displayed');
147 }
148 if (hasPrivateSpendKey) {
149 + await commonTestCases.tapItemByKey('wallet_keys_page_keys');
150 commonTestCases.hasText(keys.privateSpendKey);
151 tester.printToConsole('$walletName wallet has private spend key properly displayed');
152 }
153 if (hasPublicViewKey) {
154 + await commonTestCases.tapItemByKey('wallet_keys_page_keys');
155 commonTestCases.hasText(keys.publicViewKey);
156 tester.printToConsole('$walletName wallet has public view key properly displayed');
157 }
158 if (hasPrivateViewKey) {
159 + await commonTestCases.tapItemByKey('wallet_keys_page_keys');
160 commonTestCases.hasText(keys.privateViewKey);
161 tester.printToConsole('$walletName wallet has private view key properly displayed');
162 }
163 if (hasSeeds) {
148 - await commonTestCases.dragUntilVisible(
149 - '${walletName}_wallet_seed_item_key',
150 - 'wallet_keys_page_credentials_list_view_key',
151 - );
152 - commonTestCases.hasText(seed);
164 + await commonTestCases.tapItemByKey('wallet_keys_page_seed');
165 + final seedWords = seed.split(" ");
166 + for (var seedWord in seedWords) {
167 + commonTestCases.hasTextAtLestOnce(seedWord);
168 + }
169 tester.printToConsole('$walletName wallet has seeds properly displayed');
170 }
171 if (hasSeedLegacy) {
156 - await commonTestCases.dragUntilVisible(
157 - '${walletName}_wallet_seed_legacy_item_key',
158 - 'wallet_keys_page_credentials_list_view_key',
159 - );
160 - commonTestCases.hasText(legacySeed);
172 + await commonTestCases.tapItemByKey('wallet_keys_page_seed_legacy');
173 + final seedWords = legacySeed.split(" ");
174 + for (var seedWord in seedWords) {
175 + commonTestCases.hasTextAtLestOnce(seedWord);
176 + }
177 tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
178 }
179 }
lib/anonpay/anonpay_api.dart
+19 -28
@@ -6,8 +6,8 @@ import 'package:cake_wallet/anonpay/anonpay_status_response.dart';
6 import 'package:cake_wallet/core/fiat_conversion_service.dart';
7 import 'package:cake_wallet/entities/fiat_currency.dart';
8 import 'package:cake_wallet/exchange/limits.dart';
9 +import 'package:cw_core/utils/proxy_wrapper.dart';
10 import 'package:cw_core/wallet_base.dart';
10 -import 'package:http/http.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 import 'package:cake_wallet/.secrets.g.dart' as secrets;
13
@@ -20,8 +20,9 @@ class AnonPayApi {
20 final WalletBase wallet;
21
22 static const anonpayRef = secrets.anonPayReferralCode;
23 - static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
23 static const clearNetAuthority = 'trocador.app';
24 + // static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
25 + static const onionApiAuthority = clearNetAuthority;
26 static const markup = secrets.trocadorExchangeMarkup;
27 static const anonPayPath = '/anonpay';
28 static const anonPayStatus = '/anonpay/status';
@@ -29,8 +30,11 @@ class AnonPayApi {
30 static const apiKey = secrets.trocadorApiKey;
31
32 Future<AnonpayStatusResponse> paymentStatus(String id) async {
32 - final authority = await _getAuthority();
33 - final response = await get(Uri.https(authority, "$anonPayStatus/$id"));
33 + final response = await ProxyWrapper().get(
34 + clearnetUri: Uri.https(clearNetAuthority, "$anonPayStatus/$id"),
35 + onionUri: Uri.https(onionApiAuthority, "$anonPayStatus/$id"),
36 + );
37 +
38 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
39 final status = responseJSON['Status'] as String;
40 final fiatAmount = responseJSON['Fiat_Amount'] as double?;
@@ -69,10 +73,11 @@ class AnonPayApi {
73 if (request.fiatEquivalent != null) {
74 body['fiat_equiv'] = request.fiatEquivalent;
75 }
72 - final authority = await _getAuthority();
73 -
74 - final response = await get(Uri.https(authority, anonPayPath, body));
75 -
76 + final response = await ProxyWrapper().get(
77 + clearnetUri: Uri.https(clearNetAuthority, anonPayPath, body),
78 + onionUri: Uri.https(onionApiAuthority, anonPayPath, body),
79 + );
80 +
81 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
82 final id = responseJSON['ID'] as String;
83 final url = responseJSON['url'] as String;
@@ -146,17 +151,16 @@ class AnonPayApi {
151 'name': cryptoCurrency.name,
152 };
153
149 - final String apiAuthority = await _getAuthority();
150 - final uri = Uri.https(apiAuthority, coinPath, params);
151 -
152 - final response = await get(uri);
153 -
154 + final response = await ProxyWrapper().get(
155 + clearnetUri: Uri.https(clearNetAuthority, coinPath, params),
156 + onionUri: Uri.https(onionApiAuthority, coinPath, params),
157 + );
158 +
159 + final responseJSON = json.decode(response.body) as List<dynamic>;
160 if (response.statusCode != 200) {
161 throw Exception('Unexpected http status: ${response.statusCode}');
162 }
163
158 - final responseJSON = json.decode(response.body) as List<dynamic>;
159 -
164 if (responseJSON.isEmpty) {
165 throw Exception('No data');
166 }
@@ -197,17 +201,4 @@ class AnonPayApi {
201 return tag.toLowerCase();
202 }
203 }
200 -
201 - Future<String> _getAuthority() async {
202 - try {
203 - if (useTorOnly) {
204 - return onionApiAuthority;
205 - }
206 - final uri = Uri.https(onionApiAuthority, '/anonpay');
207 - await get(uri);
208 - return onionApiAuthority;
209 - } catch (e) {
210 - return clearNetAuthority;
211 - }
212 - }
204 }
lib/anypay/anypay_api.dart
+13 -4
@@ -1,8 +1,8 @@
1 import 'dart:convert';
2 import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
3 import 'package:cake_wallet/utils/exception_handler.dart';
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5 import 'package:flutter/foundation.dart';
5 -import 'package:http/http.dart';
6 import 'package:cw_core/crypto_currency.dart';
7 import 'package:cake_wallet/anypay/any_pay_payment.dart';
8 import 'package:cake_wallet/anypay/any_pay_trasnaction.dart';
@@ -53,8 +53,12 @@ class AnyPayApi {
53 final body = <String, dynamic>{
54 'chain': chainByScheme(scheme),
55 'currency': currencyByScheme(scheme).title};
56 - final response = await post(url, headers: headers, body: utf8.encode(json.encode(body)));
57 -
56 + final response = await ProxyWrapper().post(
57 + clearnetUri: url,
58 + headers: headers,
59 + body: json.encode(body),
60 + );
61 +
62 if (response.statusCode != 200) {
63 await ExceptionHandler.onError(FlutterErrorDetails(exception: response));
64 throw Exception('Unexpected response http code: ${response.statusCode}');
@@ -79,7 +83,12 @@ class AnyPayApi {
83 'chain': chain,
84 'currency': currency,
85 'transactions': transactions.map((tx) => {'tx': tx.tx, 'tx_hash': tx.id, 'tx_key': tx.key}).toList()};
82 - final response = await post(Uri.parse(uri), headers: headers, body: utf8.encode(json.encode(body)));
86 + final response = await ProxyWrapper().post(
87 + clearnetUri: Uri.parse(uri),
88 + headers: headers,
89 + body: json.encode(body),
90 + );
91 +
92 if (response.statusCode == 400) {
93 final decodedBody = json.decode(response.body) as Map<String, dynamic>;
94 throw Exception(decodedBody['message'] as String? ?? 'Unexpected response\nError code: 400');
lib/buy/dfx/dfx_buy_provider.dart
+16 -8
@@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart';
10 import 'package:cake_wallet/routes.dart';
11 import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
12 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
13 +import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cake_wallet/utils/show_pop_up.dart';
15 import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
16 import 'package:cw_core/crypto_currency.dart';
@@ -17,7 +18,6 @@ import 'package:cw_core/utils/print_verbose.dart';
18 import 'package:cw_core/wallet_base.dart';
19 import 'package:cw_core/wallet_type.dart';
20 import 'package:flutter/material.dart';
20 -import 'package:http/http.dart' as http;
21 import 'package:url_launcher/url_launcher.dart';
22
23 class DFXBuyProvider extends BuyProvider {
@@ -100,11 +100,12 @@ class DFXBuyProvider extends BuyProvider {
100 });
101
102 final uri = Uri.https(_baseUrl, _authPath);
103 - var response = await http.post(
104 - uri,
103 + final response = await ProxyWrapper().post(
104 + clearnetUri: uri,
105 headers: {'Content-Type': 'application/json'},
106 body: requestBody,
107 );
108 +
109
110 if (response.statusCode == 201) {
111 final responseBody = jsonDecode(response.body);
@@ -137,8 +138,10 @@ class DFXBuyProvider extends BuyProvider {
138 final url = Uri.https(_baseUrl, '/v1/fiat');
139
140 try {
140 - final response = await http.get(url, headers: {'accept': 'application/json'});
141 -
141 + final response = await ProxyWrapper().get(
142 + clearnetUri: url,
143 + headers: {'accept': 'application/json'});
144 +
145 if (response.statusCode == 200) {
146 final data = jsonDecode(response.body) as List<dynamic>;
147 for (final item in data) {
@@ -160,8 +163,8 @@ class DFXBuyProvider extends BuyProvider {
163 final url = Uri.https(_baseUrl, '/v1/asset', {'blockchains': blockchain});
164
165 try {
163 - final response = await http.get(url, headers: {'accept': 'application/json'});
164 -
166 + final response = await ProxyWrapper().get(clearnetUri: url, headers: {'accept': 'application/json'});
167 +
168 if (response.statusCode == 200) {
169 final responseData = jsonDecode(response.body);
170
@@ -271,7 +274,12 @@ class DFXBuyProvider extends BuyProvider {
274 });
275
276 try {
274 - final response = await http.put(url, headers: headers, body: body);
277 + final response = await ProxyWrapper().put(
278 + clearnetUri: url,
279 + headers: headers,
280 + body: body,
281 + );
282 +
283 final responseData = jsonDecode(response.body);
284
285 if (response.statusCode == 200) {
lib/buy/kryptonim/kryptonim.dart
+7 -2
@@ -9,10 +9,10 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
9 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
10 import 'package:cake_wallet/utils/show_pop_up.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 +import 'package:cw_core/utils/proxy_wrapper.dart';
13 import 'package:cw_core/wallet_base.dart';
14 import 'package:flutter/material.dart';
15 import 'dart:developer';
15 -import 'package:http/http.dart' as http;
16 import 'package:url_launcher/url_launcher.dart';
17
18 class KryptonimBuyProvider extends BuyProvider {
@@ -74,9 +74,14 @@ class KryptonimBuyProvider extends BuyProvider {
74 });
75
76 try {
77 - final response = await http.post(url, headers: headers, body: body);
77 + final response = await ProxyWrapper().post(
78 + clearnetUri: url,
79 + headers: headers,
80 + body: body,
81 + );
82
83 if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 401) {
84 +
85 return jsonDecode(response.body) as Map<String, dynamic>;
86 } else {
87 return {};
lib/buy/meld/meld_buy_provider.dart
+10 -4
@@ -8,13 +8,13 @@ import 'package:cake_wallet/buy/payment_method.dart';
8 import 'package:cake_wallet/entities/fiat_currency.dart';
9 import 'package:cake_wallet/generated/i18n.dart';
10 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
11 +import 'package:cw_core/utils/proxy_wrapper.dart';
12 import 'package:cake_wallet/utils/show_pop_up.dart';
13 import 'package:cw_core/crypto_currency.dart';
14 import 'package:cw_core/utils/print_verbose.dart';
15 import 'package:cw_core/wallet_base.dart';
16 import 'package:flutter/material.dart';
17 import 'dart:developer';
17 -import 'package:http/http.dart' as http;
18 import 'package:url_launcher/url_launcher.dart';
19
20 class MeldBuyProvider extends BuyProvider {
@@ -71,8 +71,8 @@ class MeldBuyProvider extends BuyProvider {
71 final url = Uri.https(_baseUrl, path, params);
72
73 try {
74 - final response = await http.get(
75 - url,
74 + final response = await ProxyWrapper().get(
75 + clearnetUri: url,
76 headers: {
77 'Authorization': _isProduction ? '' : _testApiKey,
78 'Meld-Version': '2023-12-19',
@@ -80,6 +80,7 @@ class MeldBuyProvider extends BuyProvider {
80 'content-type': 'application/json',
81 },
82 );
83 +
84
85 if (response.statusCode == 200) {
86 final data = jsonDecode(response.body) as List<dynamic>;
@@ -130,7 +131,12 @@ class MeldBuyProvider extends BuyProvider {
131 });
132
133 try {
133 - final response = await http.post(url, headers: headers, body: body);
134 + final response = await ProxyWrapper().post(
135 + clearnetUri: url,
136 + headers: headers,
137 + body: body,
138 + );
139 +
140
141 if (response.statusCode == 200) {
142 final data = jsonDecode(response.body) as Map<String, dynamic>;
lib/buy/moonpay/moonpay_provider.dart
+16 -8
@@ -16,12 +16,12 @@ import 'package:cake_wallet/palette.dart';
16 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
17 import 'package:cake_wallet/store/app_store.dart';
18 import 'package:cake_wallet/themes/core/material_base_theme.dart';
19 +import 'package:cw_core/utils/proxy_wrapper.dart';
20 import 'package:cw_core/crypto_currency.dart';
21 import 'package:cw_core/wallet_base.dart';
22 import 'package:cw_core/wallet_type.dart';
23 import 'package:cw_core/utils/print_verbose.dart';
24 import 'package:flutter/material.dart';
24 -import 'package:http/http.dart';
25 import 'package:url_launcher/url_launcher.dart';
26
27 class MoonPayProvider extends BuyProvider {
@@ -98,9 +98,12 @@ class MoonPayProvider extends BuyProvider {
98 Future<String> getMoonpaySignature(String query) async {
99 final uri = Uri.https(_cIdBaseUrl, "/api/moonpay");
100
101 - final response = await post(uri,
102 - headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
103 - body: json.encode({'query': query}));
101 + final response = await ProxyWrapper().post(
102 + clearnetUri: uri,
103 + headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
104 + body: json.encode({'query': query}),
105 + );
106 +
107
108 if (response.statusCode == 200) {
109 return (jsonDecode(response.body) as Map<String, dynamic>)['signature'] as String;
@@ -120,7 +123,11 @@ class MoonPayProvider extends BuyProvider {
123 final url = Uri.https(_baseUrl, path, params);
124
125 try {
123 - final response = await get(url, headers: {'accept': 'application/json'});
126 + final response = await ProxyWrapper().get(
127 + clearnetUri: url,
128 + headers: {'accept': 'application/json'},
129 + );
130 +
131 if (response.statusCode == 200) {
132 return jsonDecode(response.body) as Map<String, dynamic>;
133 } else {
@@ -191,8 +198,8 @@ class MoonPayProvider extends BuyProvider {
198 final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath';
199 final url = Uri.https(_baseUrl, path, params);
200 try {
194 - final response = await get(url);
195 -
201 + final response = await ProxyWrapper().get(clearnetUri: url);
202 +
203 if (response.statusCode == 200) {
204 final data = jsonDecode(response.body) as Map<String, dynamic>;
205
@@ -306,7 +313,8 @@ class MoonPayProvider extends BuyProvider {
313 Future<Order> findOrderById(String id) async {
314 final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
315 final uri = Uri.parse(url);
309 - final response = await get(uri);
316 + final response = await ProxyWrapper().get(clearnetUri: uri);
317 +
318
319 if (response.statusCode != 200) {
320 throw BuyException(title: providerDescription, content: 'Transaction $id is not found!');
lib/buy/onramper/onramper_buy_provider.dart
+12 -8
@@ -9,11 +9,12 @@ import 'package:cake_wallet/buy/payment_method.dart';
9 import 'package:cake_wallet/entities/fiat_currency.dart';
10 import 'package:cake_wallet/generated/i18n.dart';
11 import 'package:cake_wallet/themes/core/theme_store.dart';
12 +import 'package:cake_wallet/store/settings_store.dart';
13 +import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cw_core/crypto_currency.dart';
15 import 'package:cw_core/utils/print_verbose.dart';
16 import 'package:cw_core/wallet_base.dart';
17 import 'package:flutter/material.dart';
16 -import 'package:http/http.dart' as http;
18 import 'package:url_launcher/url_launcher.dart';
19
20 class OnRamperBuyProvider extends BuyProvider {
@@ -66,8 +67,10 @@ class OnRamperBuyProvider extends BuyProvider {
67 final url = Uri.https(_baseApiUrl, '$supported$defaultsAll', params);
68
69 try {
69 - final response =
70 - await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
70 + final response = await ProxyWrapper().get(
71 + clearnetUri: url,
72 + headers: {'Authorization': _apiKey, 'accept': 'application/json'},
73 + );
74
75 if (response.statusCode == 200) {
76 final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@@ -102,8 +105,8 @@ class OnRamperBuyProvider extends BuyProvider {
105
106 try {
107 final response =
105 - await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
106 -
108 + await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
109 +
110 if (response.statusCode == 200) {
111 final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
112 final List<dynamic> message = data['message'] as List<dynamic>;
@@ -132,7 +135,8 @@ class OnRamperBuyProvider extends BuyProvider {
135
136 try {
137 final response =
135 - await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
138 + await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
139 +
140
141 if (response.statusCode == 200) {
142 final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@@ -195,8 +199,8 @@ class OnRamperBuyProvider extends BuyProvider {
199 final headers = {'Authorization': _apiKey, 'accept': 'application/json'};
200
201 try {
198 - final response = await http.get(url, headers: headers);
199 -
202 + final response = await ProxyWrapper().get(clearnetUri: url, headers: headers);
203 +
204 if (response.statusCode == 200) {
205 final data = jsonDecode(response.body) as List<dynamic>;
206 if (data.isEmpty) return null;
lib/buy/robinhood/robinhood_buy_provider.dart
+11 -7
@@ -11,6 +11,7 @@ import 'package:cake_wallet/generated/i18n.dart';
11 import 'package:cake_wallet/routes.dart';
12 import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
13 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
14 +import 'package:cw_core/utils/proxy_wrapper.dart';
15 import 'package:cake_wallet/utils/show_pop_up.dart';
16 import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
17 import 'package:cw_core/crypto_currency.dart';
@@ -18,7 +19,6 @@ import 'package:cw_core/utils/print_verbose.dart';
19 import 'package:cw_core/wallet_base.dart';
20 import 'package:cw_core/wallet_type.dart';
21 import 'package:flutter/material.dart';
21 -import 'package:http/http.dart' as http;
22 import 'package:url_launcher/url_launcher.dart';
23
24 class RobinhoodBuyProvider extends BuyProvider {
@@ -69,7 +69,8 @@ class RobinhoodBuyProvider extends BuyProvider {
69 final uri = Uri.https(_apiBaseUrl, '$_assetsPath', {'applicationId': _applicationId});
70
71 try {
72 - final response = await http.get(uri, headers: {'accept': 'application/json'});
72 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
73 +
74
75 if (response.statusCode == 200) {
76 final responseData = jsonDecode(response.body) as Map<String, dynamic>;
@@ -122,12 +123,14 @@ class RobinhoodBuyProvider extends BuyProvider {
123
124 final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
125
125 - var response = await http.post(uri,
126 - headers: {'Content-Type': 'application/json'},
127 - body: json
128 - .encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}));
126 + var response = await ProxyWrapper().post(
127 + clearnetUri: uri,
128 + headers: {'Content-Type': 'application/json'},
129 + body: json.encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}),
130 + );
131
132 if (response.statusCode == 200) {
133 +
134 return (jsonDecode(response.body) as Map<String, dynamic>)['connectId'] as String;
135 } else {
136 throw Exception('Provider currently unavailable. Status: ${response.statusCode}');
@@ -219,7 +222,8 @@ class RobinhoodBuyProvider extends BuyProvider {
222 Uri.https('api.robinhood.com', '/catpay/v1/${cryptoCurrency.title}/quote/', queryParams);
223
224 try {
222 - final response = await http.get(uri, headers: {'accept': 'application/json'});
225 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
226 +
227 final responseData = jsonDecode(response.body) as Map<String, dynamic>;
228
229 if (response.statusCode == 200) {
lib/buy/wyre/wyre_buy_provider.dart
+23 -19
@@ -3,7 +3,7 @@ import 'package:cake_wallet/buy/buy_exception.dart';
3 import 'package:cake_wallet/buy/pairs_utils.dart';
4 import 'package:cake_wallet/entities/fiat_currency.dart';
5 import 'package:cw_core/crypto_currency.dart';
6 -import 'package:http/http.dart';
6 +import 'package:cw_core/utils/proxy_wrapper.dart';
7 import 'package:cake_wallet/buy/buy_amount.dart';
8 import 'package:cake_wallet/buy/buy_provider.dart';
9 import 'package:cake_wallet/buy/buy_provider_description.dart';
@@ -73,18 +73,21 @@ class WyreBuyProvider extends BuyProvider {
73 'referrerAccountId': _accountId,
74 'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
75 };
76 - final response = await post(uri,
77 - headers: {
78 - 'Authorization': 'Bearer $_secretKey',
79 - 'Content-Type': 'application/json',
80 - 'cache-control': 'no-cache'
81 - },
82 - body: json.encode(body));
76 + final response = await ProxyWrapper().post(
77 + clearnetUri: uri,
78 + headers: {
79 + 'Authorization': 'Bearer $_secretKey',
80 + 'Content-Type': 'application/json',
81 + 'cache-control': 'no-cache'
82 + },
83 + body: json.encode(body),
84 + );
85
86 if (response.statusCode != 200) {
87 throw BuyException(title: providerDescription, content: 'Url $url is not found!');
88 }
89
90 +
91 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
92 final urlFromResponse = responseJSON['url'] as String;
93 return urlFromResponse;
@@ -101,18 +104,21 @@ class WyreBuyProvider extends BuyProvider {
104 'country': _countryCode
105 };
106 final uri = Uri.parse(quoteUrl);
104 - final response = await post(uri,
105 - headers: {
106 - 'Authorization': 'Bearer $_secretKey',
107 - 'Content-Type': 'application/json',
108 - 'cache-control': 'no-cache'
109 - },
110 - body: json.encode(body));
107 + final response = await ProxyWrapper().post(
108 + clearnetUri: uri,
109 + headers: {
110 + 'Authorization': 'Bearer $_secretKey',
111 + 'Content-Type': 'application/json',
112 + 'cache-control': 'no-cache'
113 + },
114 + body: json.encode(body),
115 + );
116
117 if (response.statusCode != 200) {
118 throw BuyException(title: providerDescription, content: 'Quote is not found!');
119 }
120
121 +
122 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
123 final sourceAmount = responseJSON['sourceAmount'] as double;
124 final destAmount = responseJSON['destAmount'] as double;
@@ -125,8 +131,7 @@ class WyreBuyProvider extends BuyProvider {
131 Future<Order> findOrderById(String id) async {
132 final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
133 final orderUri = Uri.parse(orderUrl);
128 - final orderResponse = await get(orderUri);
129 -
134 + final orderResponse = await ProxyWrapper().get(clearnetUri: orderUri);
135 if (orderResponse.statusCode != 200) {
136 throw BuyException(title: providerDescription, content: 'Order $id is not found!');
137 }
@@ -142,8 +147,7 @@ class WyreBuyProvider extends BuyProvider {
147
148 final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix;
149 final transferUri = Uri.parse(transferUrl);
145 - final transferResponse = await get(transferUri);
146 -
150 + final transferResponse = await ProxyWrapper().get(clearnetUri: transferUri);
151 if (transferResponse.statusCode != 200) {
152 throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!');
153 }
lib/cake_pay/cake_pay_api.dart
+32 -10
@@ -3,9 +3,9 @@ import 'dart:convert';
3 import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
4 import 'package:cake_wallet/cake_pay/cake_pay_user_credentials.dart';
5 import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
6 +import 'package:cw_core/utils/proxy_wrapper.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
8 import 'package:cake_wallet/entities/country.dart';
8 -import 'package:http/http.dart' as http;
9
10 class CakePayApi {
11 static const testBaseUri = false;
@@ -32,12 +32,17 @@ class CakePayApi {
32 'Content-Type': 'application/json',
33 'Authorization': 'Api-Key $apiKey',
34 };
35 - final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
35 + final response = await ProxyWrapper().post(
36 + clearnetUri: uri,
37 + headers: headers,
38 + body: json.encode({'email': email}),
39 + );
40
41 if (response.statusCode != 200) {
42 throw Exception('Unexpected http status: ${response.statusCode}');
43 }
44
45 +
46 final bodyJson = json.decode(response.body) as Map<String, dynamic>;
47
48 if (bodyJson.containsKey('user') && bodyJson['user']['email'] != null) {
@@ -64,12 +69,17 @@ class CakePayApi {
69 };
70 final query = <String, String>{'email': email, 'otp': code};
71
67 - final response = await http.post(uri, headers: headers, body: json.encode(query));
72 + final response = await ProxyWrapper().post(
73 + clearnetUri: uri,
74 + headers: headers,
75 + body: json.encode(query),
76 + );
77
78 if (response.statusCode != 200) {
79 throw Exception('Unexpected http status: ${response.statusCode}');
80 }
81
82 +
83 final bodyJson = json.decode(response.body) as Map<String, dynamic>;
84
85 if (bodyJson.containsKey('error')) {
@@ -116,9 +126,14 @@ class CakePayApi {
126 };
127
128 try {
119 - final response = await http.post(uri, headers: headers, body: json.encode(query));
129 + final response = await ProxyWrapper().post(
130 + clearnetUri: uri,
131 + headers: headers,
132 + body: json.encode(query),
133 + );
134
135 if (response.statusCode != 201) {
136 +
137 final responseBody = json.decode(response.body);
138 if (responseBody is List) {
139 throw '${responseBody[0]}';
@@ -127,6 +142,7 @@ class CakePayApi {
142 }
143 }
144
145 +
146 final bodyJson = json.decode(response.body) as Map<String, dynamic>;
147 return CakePayOrder.fromMap(bodyJson);
148 } catch (e) {
@@ -145,7 +161,8 @@ class CakePayApi {
161 'X-CSRFToken': CSRFToken,
162 };
163
148 - final response = await http.get(uri, headers: headers);
164 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
165 +
166
167 printV('Response: ${response.statusCode}');
168
@@ -168,7 +185,11 @@ class CakePayApi {
185 };
186
187 try {
171 - final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
188 + final response = await ProxyWrapper().post(
189 + clearnetUri: uri,
190 + headers: headers,
191 + body: json.encode({'email': email}),
192 + );
193
194 if (response.statusCode != 200) {
195 throw Exception('Unexpected http status: ${response.statusCode}');
@@ -187,8 +208,8 @@ class CakePayApi {
208 'Authorization': 'Api-Key $apiKey',
209 };
210
190 - final response = await http.get(uri, headers: headers);
191 -
211 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
212 +
213 if (response.statusCode != 200) {
214 throw Exception('Unexpected http status: ${response.statusCode}');
215 }
@@ -234,14 +255,15 @@ class CakePayApi {
255 'Authorization': 'Api-Key $apiKey',
256 };
257
237 - var response = await http.get(uri, headers: headers);
258 + var response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
259 +
260
261 if (response.statusCode != 200) {
262 throw Exception(
263 'Failed to fetch vendors: statusCode - ${response.statusCode}, queryParams -$queryParams, response - ${response.body}');
264 }
265
244 - final bodyJson = json.decode(utf8.decode(response.bodyBytes));
266 + final bodyJson = json.decode(response.body);
267
268 if (bodyJson is List<dynamic> && bodyJson.isEmpty) {
269 return [];
lib/core/background_sync.dart
+7 -1
@@ -7,6 +7,7 @@ import 'package:cake_wallet/di.dart';
7 import 'package:cake_wallet/entities/preferences_key.dart';
8 import 'package:cake_wallet/store/settings_store.dart';
9 import 'package:cake_wallet/utils/feature_flag.dart';
10 +import 'package:cake_wallet/utils/tor.dart';
11 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
12 import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
13 import 'package:cw_core/sync_status.dart';
@@ -15,6 +16,7 @@ import 'package:cw_core/utils/print_verbose.dart';
16 import 'package:cw_core/wallet_type.dart';
17 import 'package:flutter_local_notifications/flutter_local_notifications.dart';
18 import 'package:shared_preferences/shared_preferences.dart';
19 +import 'package:flutter/foundation.dart';
20
21 class BackgroundSync {
22 final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
@@ -90,6 +92,11 @@ class BackgroundSync {
92 }
93
94 Future<void> sync() async {
95 + final settingsStore = getIt.get<SettingsStore>();
96 + if (settingsStore.currentBuiltinTor) {
97 + printV("Starting Tor");
98 + await ensureTorStarted(context: null);
99 + }
100 printV("Background sync started");
101 await _syncWallets();
102 printV("Background sync completed");
@@ -100,7 +107,6 @@ class BackgroundSync {
107 final walletListViewModel = getIt.get<WalletListViewModel>();
108 final settingsStore = getIt.get<SettingsStore>();
109
103 -
110 final List<WalletListItem> moneroWallets = walletListViewModel.wallets
111 .where((element) => !element.isHardware)
112 .where((element) => ![WalletType.haven, WalletType.decred].contains(element.type))
lib/core/fiat_conversion_service.dart
+12 -22
@@ -1,18 +1,15 @@
1 +import 'package:cw_core/utils/proxy_wrapper.dart';
2 import 'package:cw_core/crypto_currency.dart';
3 import 'package:cake_wallet/entities/fiat_currency.dart';
4 import 'dart:convert';
4 -import 'package:flutter/foundation.dart';
5 -import 'package:http/http.dart';
5 import 'package:cake_wallet/.secrets.g.dart' as secrets;
6
7 const _fiatApiClearNetAuthority = 'fiat-api.cakewallet.com';
9 -const _fiatApiOnionAuthority = 'n4z7bdcmwk2oyddxvzaap3x2peqcplh3pzdy7tpkk5ejz5n4mhfvoxqd.onion';
8 +// const _fiatApiOnionAuthority = 'n4z7bdcmwk2oyddxvzaap3x2peqcplh3pzdy7tpkk5ejz5n4mhfvoxqd.onion';
9 +const _fiatApiOnionAuthority = _fiatApiClearNetAuthority;
10 const _fiatApiPath = '/v2/rates';
11
12 -Future<double> _fetchPrice(Map<String, dynamic> args) async {
13 - final crypto = args['crypto'] as String;
14 - final fiat = args['fiat'] as String;
15 - final torOnly = args['torOnly'] as bool;
12 +Future<double> _fetchPrice(String crypto, String fiat, bool torOnly) async {
13
14 final Map<String, String> queryParams = {
15 'interval_count': '1',
@@ -24,14 +21,14 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
21 num price = 0.0;
22
23 try {
27 - late final Uri uri;
28 - if (torOnly) {
29 - uri = Uri.http(_fiatApiOnionAuthority, _fiatApiPath, queryParams);
30 - } else {
31 - uri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams);
32 - }
24 + final onionUri = Uri.http(_fiatApiOnionAuthority, _fiatApiPath, queryParams);
25 + final clearnetUri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams);
26
34 - final response = await get(uri);
27 + final response = await ProxyWrapper().get(
28 + onionUri: onionUri,
29 + clearnetUri: torOnly ? onionUri : clearnetUri,
30 + );
31 +
32
33 if (response.statusCode != 200) {
34 return 0.0;
@@ -50,18 +47,11 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
47 }
48 }
49
53 -Future<double> _fetchPriceAsync(CryptoCurrency crypto, FiatCurrency fiat, bool torOnly) async =>
54 - compute(_fetchPrice, {
55 - 'fiat': fiat.toString(),
56 - 'crypto': crypto.toString(),
57 - 'torOnly': torOnly,
58 - });
59 -
50 class FiatConversionService {
51 static Future<double> fetchPrice({
52 required CryptoCurrency crypto,
53 required FiatCurrency fiat,
54 required bool torOnly,
55 }) async =>
66 - await _fetchPriceAsync(crypto, fiat, torOnly);
56 + await _fetchPrice(crypto.toString(), fiat.toString(), torOnly);
57 }
lib/core/open_crypto_pay/open_cryptopay_service.dart
+9 -9
@@ -5,15 +5,13 @@ import 'package:cake_wallet/core/open_crypto_pay/exceptions.dart';
5 import 'package:cake_wallet/core/open_crypto_pay/lnurl.dart';
6 import 'package:cake_wallet/core/open_crypto_pay/models.dart';
7 import 'package:cw_core/crypto_currency.dart';
8 -import 'package:http/http.dart';
8 +import 'package:cw_core/utils/proxy_wrapper.dart';
9
10 class OpenCryptoPayService {
11 static bool isOpenCryptoPayQR(String value) =>
12 value.toLowerCase().contains("lightning=lnurl") ||
13 value.toLowerCase().startsWith("lnurl");
14
15 - final Client _httpClient = Client();
16 -
15 Future<String> commitOpenCryptoPayRequest(
16 String txHex, {
17 required String txId,
@@ -31,7 +29,8 @@ class OpenCryptoPayService {
29 queryParams['tx'] = txId;
30
31 final response =
34 - await _httpClient.get(Uri.https(uri.authority, uri.path, queryParams));
32 + await ProxyWrapper().get(clearnetUri: Uri.https(uri.authority, uri.path, queryParams));
33 +
34
35 if (response.statusCode == 200) {
36 final body = jsonDecode(response.body) as Map;
@@ -40,13 +39,13 @@ class OpenCryptoPayService {
39 throw OpenCryptoPayException(body.toString());
40 }
41 throw OpenCryptoPayException(
43 - "Unexpected status code ${response.statusCode} ${response.body}");
42 + "Unexpected status code ${response.statusCode} ${response}");
43 }
44
45 Future<void> cancelOpenCryptoPayRequest(OpenCryptoPayRequest request) async {
46 final uri = Uri.parse(request.callbackUrl.replaceAll("/cb/", "/cancel/"));
47
49 - await _httpClient.delete(uri);
48 + await ProxyWrapper().delete(clearnetUri: uri);
49 }
50
51 Future<OpenCryptoPayRequest> getOpenCryptoPayInvoice(String lnUrl) async {
@@ -73,7 +72,8 @@ class OpenCryptoPayService {
72
73 Future<(_OpenCryptoPayQuote, Map<String, List<OpenCryptoPayQuoteAsset>>)>
74 _getOpenCryptoPayParams(Uri uri) async {
76 - final response = await _httpClient.get(uri);
75 + final response = await ProxyWrapper().get(clearnetUri: uri);
76 +
77
78 if (response.statusCode == 200) {
79 final responseBody = jsonDecode(response.body) as Map;
@@ -119,8 +119,8 @@ class OpenCryptoPayService {
119 queryParams['asset'] = asset.title;
120 queryParams['method'] = _getMethod(asset);
121
122 - final response =
123 - await _httpClient.get(Uri.https(uri.authority, uri.path, queryParams));
122 + final response = await ProxyWrapper().get(clearnetUri: Uri.https(uri.authority, uri.path, queryParams));
123 +
124
125 if (response.statusCode == 200) {
126 final responseBody = jsonDecode(response.body) as Map;
lib/core/yat_service.dart
+4 -3
@@ -1,7 +1,7 @@
1 import 'dart:convert';
2
3 import 'package:cake_wallet/entities/yat_record.dart';
4 -import 'package:http/http.dart';
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5
6 class YatService {
7 static bool isDevMode = false;
@@ -33,7 +33,8 @@ class YatService {
33 final yatRecords = <YatRecord>[];
34
35 try {
36 - final response = await get(uri);
36 + final response = await ProxyWrapper().get(clearnetUri: uri);
37 +
38 final resBody = json.decode(response.body) as Map<String, dynamic>;
39 final results = resBody["result"] as Map<dynamic, dynamic>;
40 // Favour a subaddress over a standard address.
@@ -42,7 +43,7 @@ class YatService {
43 results[MONERO_STD_ADDRESS] ??
44 results[tag]) as Map<String, dynamic>;
45
45 - if (yatRecord != null) {
46 + if (yatRecord.isNotEmpty) {
47 yatRecords.add(YatRecord.fromJson(yatRecord));
48 }
49
lib/di.dart
+8 -3
@@ -33,10 +33,12 @@ import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
33 import 'package:cake_wallet/haven/cw_haven.dart';
34 import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
35 import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart';
36 +import 'package:cake_wallet/src/screens/dev/network_requests.dart';
37 import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart';
38 import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart';
39 import 'package:cake_wallet/src/screens/integrations/deuro/savings_page.dart';
40 import 'package:cake_wallet/src/screens/settings/background_sync_page.dart';
41 +import 'package:cake_wallet/src/screens/start_tor/start_tor_page.dart';
42 import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
43 import 'package:cake_wallet/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart';
44 import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart';
@@ -48,6 +50,7 @@ import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
50 import 'package:cake_wallet/view_model/link_view_model.dart';
51 import 'package:cake_wallet/tron/tron.dart';
52 import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
53 +import 'package:cake_wallet/view_model/start_tor_view_model.dart';
54 import 'package:cw_core/receive_page_option.dart';
55 import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
56 import 'package:cake_wallet/entities/wallet_manager.dart';
@@ -138,7 +141,6 @@ import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
141 import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
142 import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
143 import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
141 -import 'package:cake_wallet/src/screens/settings/tor_page.dart';
144 import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
145 import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
146 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
@@ -690,7 +692,6 @@ Future<void> setup({
692 return walletKitService;
693 });
694
693 - getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>()));
695 getIt.registerFactory(() => BalancePage(
696 nftViewModel: getIt.get<NFTViewModel>(),
697 dashboardViewModel: getIt.get<DashboardViewModel>(),
@@ -1495,7 +1496,7 @@ Future<void> setup({
1496 () => WalletConnectConnectionsView(walletKitService: getIt.get<WalletKitService>()),
1497 );
1498
1498 - getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>()));
1499 + getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>()));
1500
1501 getIt.registerFactory(() => SignViewModel(getIt.get<AppStore>().wallet!));
1502
@@ -1512,7 +1513,11 @@ Future<void> setup({
1513 getIt.registerFactory(() => BackgroundSyncLogsViewModel());
1514
1515 getIt.registerFactory(() => DevBackgroundSyncLogsPage(getIt.get<BackgroundSyncLogsViewModel>()));
1516 +
1517 + getIt.registerFactory(() => DevNetworkRequests());
1518
1519 + getIt.registerFactory(() => StartTorPage(StartTorViewModel(),));
1520 +
1521 getIt.registerFactory(() => DEuroViewModel(getIt<AppStore>()));
1522
1523 getIt.registerFactory(() => DEuroSavingsPage(getIt<DEuroViewModel>()));
lib/entities/ens_record.dart
+6 -2
@@ -1,14 +1,16 @@
1 import 'package:cake_wallet/ethereum/ethereum.dart';
2 import 'package:cake_wallet/polygon/polygon.dart';
3 +import 'package:cw_core/utils/proxy_wrapper.dart';
4 import 'package:cw_core/utils/print_verbose.dart';
5 import 'package:cw_core/wallet_base.dart';
6 import 'package:cw_core/wallet_type.dart';
7 import 'package:ens_dart/ens_dart.dart';
7 -import 'package:http/http.dart';
8 import 'package:web3dart/web3dart.dart';
9
10 class EnsRecord {
11 +
12 static Future<String> fetchEnsAddress(String name, {WalletBase? wallet}) async {
13 +
14 Web3Client? _client;
15
16 if (wallet != null && wallet.type == WalletType.ethereum) {
@@ -20,7 +22,9 @@ class EnsRecord {
22 }
23
24 if (_client == null) {
23 - _client = Web3Client("https://ethereum-rpc.publicnode.com", Client());
25 + late final client = ProxyWrapper().getHttpIOClient();
26 +
27 + _client = Web3Client("https://ethereum-rpc.publicnode.com", client);
28 }
29
30 try {
lib/entities/fio_address_provider.dart
+13 -5
@@ -1,6 +1,6 @@
1 import 'dart:convert';
2
3 -import 'package:http/http.dart' as http;
3 +import 'package:cw_core/utils/proxy_wrapper.dart';
4
5 class FioAddressProvider {
6 static const apiAuthority = 'fio.blockpane.com';
@@ -13,13 +13,17 @@ class FioAddressProvider {
13 final body = <String, String>{"fio_name": fioAddress};
14
15 final uri = Uri.https(apiAuthority, availCheck);
16 - final response =
17 - await http.post(uri, headers: headers, body: json.encode(body));
16 + final response = await ProxyWrapper().post(
17 + clearnetUri: uri,
18 + headers: headers,
19 + body: json.encode(body),
20 + );
21
22 if (response.statusCode != 200) {
23 return isFioRegistered;
24 }
25
26 +
27 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
28 isFioRegistered = responseJSON['is_registered'] as int == 1;
29
@@ -35,9 +39,13 @@ class FioAddressProvider {
39 };
40
41 final uri = Uri.https(apiAuthority, getAddress);
38 - final response =
39 - await http.post(uri, headers: headers, body: json.encode(body));
42 + final response = await ProxyWrapper().post(
43 + clearnetUri: uri,
44 + headers: headers,
45 + body: json.encode(body),
46 + );
47
48 +
49 if (response.statusCode == 400) {
50 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
51 final error = responseJSON['error'] as String;
lib/entities/preferences_key.dart
+1
@@ -66,6 +66,7 @@ class PreferencesKey {
66 static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
67 static const syncModeKey = 'sync_mode';
68 static const syncAllKey = 'sync_all';
69 + static const builtinTorKey = 'builtin_tor';
70 static const lastPopupDate = 'last_popup_date';
71 static const lastAppReviewDate = 'last_app_review_date';
72 static const sortBalanceBy = 'sort_balance_by';
lib/entities/unstoppable_domain_address.dart
+4 -3
@@ -1,15 +1,16 @@
1 import 'dart:convert';
2
3 import 'package:cw_core/utils/print_verbose.dart';
4 -import 'package:http/http.dart' as http;
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5
6 Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
7 var address = '';
8
9 try {
10 final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records");
11 - final jsonString = await http.read(uri);
12 - final jsonParsed = json.decode(jsonString) as Map<String, dynamic>;
11 + final response = await ProxyWrapper().get(clearnetUri: uri);
12 +
13 + final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
14 if (jsonParsed["records"] == null) {
15 throw Exception(".records response from $uri is empty");
16 };
lib/entities/wellknown_record.dart
+4 -3
@@ -2,7 +2,7 @@ import 'dart:convert';
2
3 import 'package:cw_core/crypto_currency.dart';
4 import 'package:cw_core/utils/print_verbose.dart';
5 -import 'package:http/http.dart' as http;
5 +import 'package:cw_core/utils/proxy_wrapper.dart';
6
7 class WellKnownRecord {
8 WellKnownRecord({
@@ -40,14 +40,15 @@ class WellKnownRecord {
40 }
41
42 // lookup domain/.well-known/nano-currency.json and check if it has a nano address:
43 - final http.Response response = await http.get(
44 - Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"),
43 + final response = await ProxyWrapper().get(
44 + clearnetUri: Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"),
45 headers: <String, String>{"Accept": "application/json"},
46 );
47
48 if (response.statusCode != 200) {
49 return null;
50 }
51 +
52 final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
53
54 // Access the first element in the names array and retrieve its address
lib/entities/zano_alias.dart
+4 -3
@@ -1,14 +1,14 @@
1 import 'dart:convert';
2
3 import 'package:cw_core/utils/print_verbose.dart';
4 -import 'package:http/http.dart' as http;
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5
6 class ZanoAlias {
7 static Future<String?> fetchZanoAliasAddress(String alias) async {
8 try {
9 final uri = Uri.parse("http://zano.cakewallet.com:11211/json_rpc");
10 - final response = await http.post(
11 - uri,
10 + final response = await ProxyWrapper().post(
11 + clearnetUri: uri,
12 body: json.encode({
13 "id": 0,
14 "jsonrpc": "2.0",
@@ -16,6 +16,7 @@ class ZanoAlias {
16 "params": {"alias": alias}
17 }),
18 );
19 +
20 final jsonParsed = json.decode(response.body) as Map<String, dynamic>;
21
22 return jsonParsed['result']['alias_details']['address'] as String?;
lib/exchange/provider/chainflip_exchange_provider.dart
+5 -3
@@ -12,7 +12,7 @@ import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 import 'package:cw_core/crypto_currency.dart';
13 import 'package:cw_core/utils/print_verbose.dart';
14 import 'package:hive/hive.dart';
15 -import 'package:http/http.dart' as http;
15 +import 'package:cw_core/utils/proxy_wrapper.dart';
16
17 class ChainflipExchangeProvider extends ExchangeProvider {
18 ChainflipExchangeProvider({required this.tradesStore})
@@ -275,7 +275,8 @@ class ChainflipExchangeProvider extends ExchangeProvider {
275 Future<Map<String, dynamic>> _getRequest(String path, Map<String, String> params) async {
276 final uri = Uri.https(_baseURL, path, params);
277
278 - final response = await http.get(uri);
278 + final response = await ProxyWrapper().get(clearnetUri: uri);
279 +
280
281 if ((response.statusCode != 200) || (response.body.contains('error'))) {
282 throw Exception('Unexpected response: ${response.statusCode} / ${uri.toString()} / ${response.body}');
@@ -287,7 +288,8 @@ class ChainflipExchangeProvider extends ExchangeProvider {
288 Future<Map<String, dynamic>?> _getStatus(Map<String, String> params) async {
289 final uri = Uri.https(_baseURL, _txInfoPath, params);
290
290 - final response = await http.get(uri);
291 + final response = await ProxyWrapper().get(clearnetUri: uri);
292 +
293
294 if (response.statusCode == 404) return null;
295
lib/exchange/provider/changenow_exchange_provider.dart
+14 -6
@@ -13,11 +13,11 @@ import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
13 import 'package:cake_wallet/store/settings_store.dart';
14 import 'package:cake_wallet/utils/device_info.dart';
15 import 'package:cake_wallet/utils/distribution_info.dart';
16 +import 'package:cw_core/utils/proxy_wrapper.dart';
17 import 'package:cake_wallet/wallet_type_utils.dart';
18 import 'package:cw_core/crypto_currency.dart';
19 import 'package:cw_core/utils/print_verbose.dart';
19 -import 'package:http/http.dart';
20 -
20 +import 'package:cw_core/utils/proxy_wrapper.dart';
21 class ChangeNowExchangeProvider extends ExchangeProvider {
22 ChangeNowExchangeProvider({required SettingsStore settingsStore})
23 : _settingsStore = settingsStore,
@@ -73,7 +73,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
73 'flow': _getFlow(isFixedRateMode)
74 };
75 final uri = Uri.https(apiAuthority, rangePath, params);
76 - final response = await get(uri, headers: headers);
76 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
77 +
78
79 if (response.statusCode == 400) {
80 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -118,7 +119,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
119 params['fromAmount'] = amount.toString();
120
121 final uri = Uri.https(apiAuthority, estimatedAmountPath, params);
121 - final response = await get(uri, headers: headers);
122 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
123 +
124 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
125 final fromAmount = double.parse(responseJSON['fromAmount'].toString());
126 final toAmount = double.parse(responseJSON['toAmount'].toString());
@@ -177,7 +179,12 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
179 }
180
181 final uri = Uri.https(apiAuthority, createTradePath);
180 - final response = await post(uri, headers: headers, body: json.encode(body));
182 + final response = await ProxyWrapper().post(
183 + clearnetUri: uri,
184 + headers: headers,
185 + body: json.encode(body),
186 + );
187 +
188
189 if (response.statusCode == 400) {
190 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -220,7 +227,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
227 final headers = {apiHeaderKey: apiKey};
228 final params = <String, String>{'id': id};
229 final uri = Uri.https(apiAuthority, findTradeByIdPath, params);
223 - final response = await get(uri, headers: headers);
230 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
231 +
232
233 if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
234
lib/exchange/provider/exolix_exchange_provider.dart
+14 -7
@@ -9,9 +9,9 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
9 import 'package:cake_wallet/exchange/trade_request.dart';
10 import 'package:cake_wallet/exchange/trade_state.dart';
11 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 +import 'package:cw_core/utils/proxy_wrapper.dart';
13 import 'package:cw_core/crypto_currency.dart';
14 import 'package:cw_core/utils/print_verbose.dart';
14 -import 'package:http/http.dart';
15
16 class ExolixExchangeProvider extends ExchangeProvider {
17 ExolixExchangeProvider() : super(pairList: supportedPairs(_notSupported));
@@ -86,8 +86,9 @@ class ExolixExchangeProvider extends ExchangeProvider {
86 // Maximum of 2 attempts to fetch limits
87 for (int i = 0; i < 2; i++) {
88 final uri = Uri.https(apiBaseUrl, ratePath, params);
89 - final response = await get(uri);
90 -
89 + final response = await ProxyWrapper().get(clearnetUri: uri);
90 +
91 +
92 if (response.statusCode == 200) {
93 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
94 final minAmount = responseJSON['minAmount'];
@@ -133,7 +134,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
134 params['amount'] = amount.toString();
135
136 final uri = Uri.https(apiBaseUrl, ratePath, params);
136 - final response = await get(uri);
137 + final response = await ProxyWrapper().get(clearnetUri: uri);
138 +
139 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
140
141 if (response.statusCode != 200) {
@@ -172,7 +174,12 @@ class ExolixExchangeProvider extends ExchangeProvider {
174 body['amount'] = request.fromAmount;
175
176 final uri = Uri.https(apiBaseUrl, transactionsPath);
175 - final response = await post(uri, headers: headers, body: json.encode(body));
177 + final response = await ProxyWrapper().post(
178 + clearnetUri: uri,
179 + headers: headers,
180 + body: json.encode(body),
181 + );
182 +
183
184 if (response.statusCode == 400) {
185 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -214,8 +221,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
221 Future<Trade> findTradeById({required String id}) async {
222 final findTradeByIdPath = '$transactionsPath/$id';
223 final uri = Uri.https(apiBaseUrl, findTradeByIdPath);
217 - final response = await get(uri);
218 -
224 + final response = await ProxyWrapper().get(clearnetUri: uri);
225 +
226 if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
227
228 if (response.statusCode == 400) {
lib/exchange/provider/letsexchange_exchange_provider.dart
+13 -4
@@ -10,9 +10,9 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
13 +import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cw_core/crypto_currency.dart';
15 import 'package:cw_core/utils/print_verbose.dart';
15 -import 'package:http/http.dart' as http;
16
17 class LetsExchangeExchangeProvider extends ExchangeProvider {
18 LetsExchangeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
@@ -152,7 +152,11 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
152
153 final uri = Uri.https(_baseUrl,
154 isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath, tradeParams);
155 - final response = await http.post(uri, headers: headers);
155 + final response = await ProxyWrapper().post(
156 + clearnetUri: uri,
157 + headers: headers,
158 + );
159 +
160
161 if (response.statusCode != 200) {
162 throw Exception('LetsExchange create trade failed: ${response.body}');
@@ -218,7 +222,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
222 };
223
224 final url = Uri.https(_baseUrl, '$_getTransactionPath/$id');
221 - final response = await http.get(url, headers: headers);
225 + final response = await ProxyWrapper().get(clearnetUri: url, headers: headers);
226 +
227
228 if (response.statusCode != 200) {
229 throw Exception('LetsExchange fetch trade failed: ${response.body}');
@@ -266,7 +271,11 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
271
272 try {
273 final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath, params);
269 - final response = await http.post(uri, headers: headers);
274 + final response = await ProxyWrapper().post(
275 + clearnetUri: uri,
276 + headers: headers,
277 + );
278 +
279 if (response.statusCode != 200) {
280 throw Exception('LetsExchange fetch info failed: ${response.body}');
281 }
lib/exchange/provider/sideshift_exchange_provider.dart
+21 -10
@@ -1,5 +1,4 @@
1 import 'dart:convert';
2 -import 'dart:developer';
2
3 import 'package:cake_wallet/.secrets.g.dart' as secrets;
4 import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
@@ -11,9 +10,9 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
13 +import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cw_core/crypto_currency.dart';
15 import 'package:cw_core/utils/print_verbose.dart';
16 -import 'package:http/http.dart';
16
17 class SideShiftExchangeProvider extends ExchangeProvider {
18 SideShiftExchangeProvider() : super(pairList: supportedPairs(_notSupported));
@@ -60,8 +59,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
59 Future<bool> checkIsAvailable() async {
60 const url = apiBaseUrl + permissionPath;
61 final uri = Uri.parse(url);
63 - final response = await get(uri);
64 -
62 + final response = await ProxyWrapper().get(clearnetUri: uri);
63 +
64 if (response.statusCode == 500) {
65 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
66 final error = responseJSON['error']['message'] as String;
@@ -90,7 +89,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
89 "$apiBaseUrl$rangePath/${fromCurrency.title.toLowerCase()}-$fromNetwork/${toCurrency.title.toLowerCase()}-$toNetwork";
90
91 final uri = Uri.parse(url);
93 - final response = await get(uri);
92 + final response = await ProxyWrapper().get(clearnetUri: uri);
93 +
94
95 if (response.statusCode == 500) {
96 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -137,7 +137,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
137 "$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork?amount=$amount";
138
139 final uri = Uri.parse(url);
140 - final response = await get(uri);
140 + final response = await ProxyWrapper().get(clearnetUri: uri);
141 +
142 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
143
144 if (response.statusCode == 500) {
@@ -186,7 +187,12 @@ class SideShiftExchangeProvider extends ExchangeProvider {
187 final headers = {'Content-Type': 'application/json'};
188
189 final uri = Uri.parse(url);
189 - final response = await post(uri, headers: headers, body: json.encode(body));
190 + final response = await ProxyWrapper().post(
191 + clearnetUri: uri,
192 + headers: headers,
193 + body: json.encode(body),
194 + );
195 +
196
197 if (response.statusCode != 201) {
198 if (response.statusCode == 400) {
@@ -227,8 +233,8 @@ class SideShiftExchangeProvider extends ExchangeProvider {
233 Future<Trade> findTradeById({required String id}) async {
234 final url = apiBaseUrl + orderPath + '/' + id;
235 final uri = Uri.parse(url);
230 - final response = await get(uri);
231 -
236 + final response = await ProxyWrapper().get(clearnetUri: uri);
237 +
238 if (response.statusCode == 404) {
239 throw TradeNotFoundException(id, provider: description);
240 }
@@ -281,7 +287,12 @@ class SideShiftExchangeProvider extends ExchangeProvider {
287 'depositNetwork': _networkFor(request.fromCurrency),
288 };
289 final uri = Uri.parse(url);
284 - final response = await post(uri, headers: headers, body: json.encode(body));
290 + final response = await ProxyWrapper().post(
291 + clearnetUri: uri,
292 + headers: headers,
293 + body: json.encode(body),
294 + );
295 +
296
297 if (response.statusCode != 201) {
298 if (response.statusCode == 400) {
lib/exchange/provider/simpleswap_exchange_provider.dart
+15 -8
@@ -11,8 +11,8 @@ import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
13 import 'package:cake_wallet/utils/device_info.dart';
14 +import 'package:cw_core/utils/proxy_wrapper.dart';
15 import 'package:cw_core/crypto_currency.dart';
15 -import 'package:http/http.dart';
16
17 class SimpleSwapExchangeProvider extends ExchangeProvider {
18 SimpleSwapExchangeProvider() : super(pairList: supportedPairs(_notSupported));
@@ -48,7 +48,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
48 @override
49 Future<bool> checkIsAvailable() async {
50 final uri = Uri.https(apiAuthority, getEstimatePath, <String, String>{'api_key': apiKey});
51 - final response = await get(uri);
51 + final response = await ProxyWrapper().get(clearnetUri: uri);
52
53 return !(response.statusCode == 403);
54 }
@@ -66,7 +66,8 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
66 };
67 final uri = Uri.https(apiAuthority, rangePath, params);
68
69 - final response = await get(uri);
69 + final response = await ProxyWrapper().get(clearnetUri: uri);
70 +
71
72 if (response.statusCode == 500) {
73 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -104,10 +105,10 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
105 'fixed': isFixedRateMode.toString()
106 };
107 final uri = Uri.https(apiAuthority, getEstimatePath, params);
107 - final response = await get(uri);
108 + final response = await ProxyWrapper().get(clearnetUri: uri);
109 +
110
111 if (response.body == "null") return 0.00;
110 -
112 final data = json.decode(response.body) as String;
113
114 return double.parse(data) / amount;
@@ -134,7 +135,12 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
135 };
136 final uri = Uri.https(apiAuthority, createExchangePath, params);
137
137 - final response = await post(uri, headers: headers, body: json.encode(body));
138 + final response = await ProxyWrapper().post(
139 + clearnetUri: uri,
140 + headers: headers,
141 + body: json.encode(body),
142 + );
143 +
144
145 if (response.statusCode != 200 && response.statusCode != 201) {
146 if (response.statusCode == 400) {
@@ -176,8 +182,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
182 Future<Trade> findTradeById({required String id}) async {
183 final params = {'api_key': apiKey, 'id': id};
184 final uri = Uri.https(apiAuthority, getExchangePath, params);
179 - final response = await get(uri);
180 -
185 + final response = await ProxyWrapper().get(clearnetUri: uri);
186 +
187 +
188 if (response.statusCode == 404) {
189 throw TradeNotFoundException(id, provider: description);
190 }
lib/exchange/provider/stealth_ex_exchange_provider.dart
+22 -9
@@ -10,8 +10,8 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
13 +import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cw_core/crypto_currency.dart';
14 -import 'package:http/http.dart' as http;
15
16 class StealthExExchangeProvider extends ExchangeProvider {
17 StealthExExchangeProvider() : super(pairList: supportedPairs(_notSupported));
@@ -63,8 +63,12 @@ class StealthExExchangeProvider extends ExchangeProvider {
63 };
64
65 try {
66 - final response = await http.post(Uri.parse(_baseUrl + _rangePath),
67 - headers: headers, body: json.encode(body));
66 + final response = await ProxyWrapper().post(
67 + clearnetUri: Uri.parse(_baseUrl + _rangePath),
68 + headers: headers,
69 + body: json.encode(body),
70 + );
71 +
72 if (response.statusCode != 200) {
73 throw Exception('StealthEx fetch limits failed: ${response.body}');
74 }
@@ -134,8 +138,12 @@ class StealthExExchangeProvider extends ExchangeProvider {
138 'additional_fee_percent': _additionalFeePercent,
139 };
140
137 - final response = await http.post(Uri.parse(_baseUrl + _exchangesPath),
138 - headers: headers, body: json.encode(body));
141 + final response = await ProxyWrapper().post(
142 + clearnetUri: Uri.parse(_baseUrl + _exchangesPath),
143 + headers: headers,
144 + body: json.encode(body),
145 + );
146 +
147
148 if (response.statusCode != 201) {
149 throw Exception('StealthEx create trade failed: ${response.body}');
@@ -202,8 +210,9 @@ class StealthExExchangeProvider extends ExchangeProvider {
210 final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
211
212 final uri = Uri.parse('$_baseUrl$_exchangesPath/$id');
205 - final response = await http.get(uri, headers: headers);
206 -
213 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
214 +
215 +
216 if (response.statusCode != 200) {
217 throw Exception('StealthEx fetch trade failed: ${response.body}');
218 }
@@ -260,8 +269,12 @@ class StealthExExchangeProvider extends ExchangeProvider {
269 };
270
271 try {
263 - final response = await http.post(Uri.parse(_baseUrl + _amountPath),
264 - headers: headers, body: json.encode(body));
272 + final response = await ProxyWrapper().post(
273 + clearnetUri: Uri.parse(_baseUrl + _amountPath),
274 + headers: headers,
275 + body: json.encode(body),
276 + );
277 +
278 if (response.statusCode != 200) return {};
279 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
280 final rate = responseJSON['rate'] as Map<String, dynamic>?;
lib/exchange/provider/swaptrade_exchange_provider.dart
+21 -5
@@ -10,9 +10,9 @@ import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
13 +import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cw_core/crypto_currency.dart';
15 import 'package:cw_core/utils/print_verbose.dart';
15 -import 'package:http/http.dart';
16
17 class SwapTradeExchangeProvider extends ExchangeProvider {
18 SwapTradeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
@@ -69,7 +69,8 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
69 }) async {
70 try {
71 final uri = Uri.https(apiAuthority, getCoins);
72 - final response = await get(uri);
72 + final response = await ProxyWrapper().get(clearnetUri: uri);
73 +
74
75 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
76
@@ -116,7 +117,12 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
117 };
118
119 final uri = Uri.https(apiAuthority, getRate, params);
119 - final response = await post(uri, body: body, headers: headers);
120 + final response = await ProxyWrapper().post(
121 + clearnetUri: uri,
122 + body: json.encode(body),
123 + headers: headers,
124 + );
125 +
126 final responseBody = json.decode(response.body) as Map<String, dynamic>;
127
128 if (response.statusCode != 200)
@@ -153,7 +159,12 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
159 };
160
161 final uri = Uri.https(apiAuthority, createOrder, params);
156 - final response = await post(uri, body: body, headers: headers);
162 + final response = await ProxyWrapper().post(
163 + clearnetUri: uri,
164 + body: json.encode(body),
165 + headers: headers,
166 + );
167 +
168 final responseBody = json.decode(response.body) as Map<String, dynamic>;
169
170 if (response.statusCode == 400 || responseBody["success"] == false) {
@@ -196,7 +207,12 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
207 };
208
209 final uri = Uri.https(apiAuthority, order, params);
199 - final response = await post(uri, body: body, headers: headers);
210 + final response = await ProxyWrapper().post(
211 + clearnetUri: uri,
212 + body: json.encode(body),
213 + headers: headers,
214 + );
215 +
216 final responseBody = json.decode(response.body) as Map<String, dynamic>;
217
218 if (response.statusCode == 400 || responseBody["success"] == false) {
lib/exchange/provider/thorchain_exchange.provider.dart
+7 -6
@@ -7,10 +7,10 @@ import 'package:cake_wallet/exchange/trade.dart';
7 import 'package:cake_wallet/exchange/trade_request.dart';
8 import 'package:cake_wallet/exchange/trade_state.dart';
9 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
10 +import 'package:cw_core/utils/proxy_wrapper.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 import 'package:cw_core/utils/print_verbose.dart';
13 import 'package:hive/hive.dart';
13 -import 'package:http/http.dart' as http;
14
15 class ThorChainExchangeProvider extends ExchangeProvider {
16 ThorChainExchangeProvider({required this.tradesStore})
@@ -164,7 +164,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
164 if (id.isEmpty) throw Exception('Trade id is empty');
165 final formattedId = id.startsWith('0x') ? id.substring(2) : id;
166 final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId');
167 - final response = await http.get(uri);
167 + final response = await ProxyWrapper().get(clearnetUri: uri);
168 +
169
170 if (response.statusCode == 404) {
171 throw Exception('Trade not found for id: $formattedId');
@@ -217,8 +218,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
218
219 static Future<Map<String, String>?>? lookupAddressByName(String name) async {
220 final uri = Uri.https(_baseURL, '$_nameLookUpPath$name');
220 - final response = await http.get(uri);
221 -
221 + final response = await ProxyWrapper().get(clearnetUri: uri);
222 +
223 if (response.statusCode != 200) {
224 return null;
225 }
@@ -244,8 +245,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
245 Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
246 Uri uri = Uri.https(_baseNodeURL, _quotePath, params);
247
247 - final response = await http.get(uri);
248 -
248 + final response = await ProxyWrapper().get(clearnetUri: uri);
249 +
250 if (response.statusCode != 200) {
251 throw Exception('Unexpected HTTP status: ${response.statusCode}');
252 }
lib/exchange/provider/trocador_exchange_provider.dart
+15 -12
@@ -8,9 +8,9 @@ import 'package:cake_wallet/exchange/trade.dart';
8 import 'package:cake_wallet/exchange/trade_request.dart';
9 import 'package:cake_wallet/exchange/trade_state.dart';
10 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
11 +import 'package:cw_core/utils/proxy_wrapper.dart';
12 import 'package:cw_core/crypto_currency.dart';
13 import 'package:cw_core/utils/print_verbose.dart';
13 -import 'package:http/http.dart';
14
15 class TrocadorExchangeProvider extends ExchangeProvider {
16 TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}})
@@ -52,8 +52,9 @@ class TrocadorExchangeProvider extends ExchangeProvider {
52 ];
53
54 static const apiKey = secrets.trocadorApiKey;
55 - static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion';
55 static const clearNetAuthority = 'api.trocador.app';
56 + static const onionApiAuthority = clearNetAuthority;
57 + // static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion';
58 static const markup = secrets.trocadorExchangeMarkup;
59 static const newRatePath = '/new_rate';
60 static const createTradePath = '/new_trade';
@@ -97,7 +98,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
98 };
99
100 final uri = await _getUri(coinPath, params);
100 - final response = await get(uri, headers: {'API-Key': apiKey});
101 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey});
102 +
103
104 if (response.statusCode != 200)
105 throw Exception('Unexpected http status: ${response.statusCode}');
@@ -138,12 +140,10 @@ class TrocadorExchangeProvider extends ExchangeProvider {
140 };
141
142 final uri = await _getUri(newRatePath, params);
141 - final response = await get(uri, headers: {'API-Key': apiKey});
143 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey});
144 +
145
146 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
144 -
145 - if (responseJSON['error'] != null) throw Exception(responseJSON['error']);
146 -
147 final fromAmount = double.parse(responseJSON['amount_from'].toString());
148 final toAmount = double.parse(responseJSON['amount_to'].toString());
149 final rateId = responseJSON['trade_id'] as String? ?? '';
@@ -206,8 +206,9 @@ class TrocadorExchangeProvider extends ExchangeProvider {
206 params['provider'] = _provider.first as String;
207
208 final uri = await _getUri(createTradePath, params);
209 - final response = await get(uri, headers: {'API-Key': apiKey});
210 -
209 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey});
210 +
211 +
212 if (response.statusCode == 400) {
213 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
214 final error = responseJSON['error'] as String;
@@ -255,9 +256,10 @@ class TrocadorExchangeProvider extends ExchangeProvider {
256 @override
257 Future<Trade> findTradeById({required String id}) async {
258 final uri = await _getUri(tradePath, {'id': id});
258 - return get(uri, headers: {'API-Key': apiKey}).then((response) {
259 + return ProxyWrapper().get(clearnetUri: uri, headers: {'API-Key': apiKey}).then((response) async {
260 if (response.statusCode != 200)
261 throw Exception('Unexpected http status: ${response.statusCode}');
262 +
263
264 final responseListJson = json.decode(response.body) as List;
265 final responseJSON = responseListJson.first;
@@ -292,7 +294,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
294
295 Future<List<TrocadorPartners>> fetchProviders() async {
296 final uri = await _getUri(providersListPath, {'api_key': apiKey});
295 - final response = await get(uri);
297 + final response = await ProxyWrapper().get(clearnetUri: uri);
298 +
299
300 if (response.statusCode != 200)
301 throw Exception('Unexpected http status: ${response.statusCode}');
@@ -355,7 +358,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
358 if (useTorOnly) return uri;
359
360 try {
358 - await get(uri);
361 + await ProxyWrapper().get(clearnetUri: uri);
362
363 return uri;
364 } catch (e) {
lib/exchange/provider/xoswap_exchange_provider.dart
+13 -6
@@ -10,8 +10,7 @@ import 'package:cake_wallet/exchange/trade_state.dart';
10 import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 import 'package:cw_core/utils/print_verbose.dart';
13 -import 'package:http/http.dart' as http;
14 -
13 +import 'package:cw_core/utils/proxy_wrapper.dart';
14 class XOSwapExchangeProvider extends ExchangeProvider {
15 XOSwapExchangeProvider() : super(pairList: supportedPairs(_notSupported));
16
@@ -72,7 +71,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
71 final uri = Uri.https(_apiAuthority, _apiPath + _assets,
72 {'networks': normalizedNetwork, 'query': currency.title});
73
75 - final response = await http.get(uri, headers: _headers);
74 + final response = await ProxyWrapper().get(clearnetUri: uri);
75 +
76 if (response.statusCode != 200) {
77 throw Exception('Failed to fetch assets for ${currency.title} on ${currency.tag}');
78 }
@@ -102,7 +102,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
102 if (curFrom == null || curTo == null) return [];
103 final pairId = curFrom + '_' + curTo;
104 final uri = Uri.https(_apiAuthority, '$_apiPath$_pairsPath/$pairId$_ratePath');
105 - final response = await http.get(uri, headers: _headers);
105 + final response = await ProxyWrapper().get(clearnetUri: uri);
106 +
107 if (response.statusCode != 200) return [];
108 return json.decode(response.body) as List<dynamic>;
109 } catch (e) {
@@ -205,7 +206,12 @@ class XOSwapExchangeProvider extends ExchangeProvider {
206 'pairId': pairId,
207 };
208
208 - final response = await http.post(uri, headers: _headers, body: json.encode(payload));
209 + final response = await ProxyWrapper().post(
210 + clearnetUri: uri,
211 + headers: _headers,
212 + body: json.encode(payload),
213 + );
214 +
215 if (response.statusCode != 201) {
216 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
217 final error = responseJSON['error'] ?? 'Unknown error';
@@ -254,7 +260,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
260 Future<Trade> findTradeById({required String id}) async {
261 try {
262 final uri = Uri.https(_apiAuthority, '$_apiPath$_orders/$id');
257 - final response = await http.get(uri, headers: _headers);
263 + final response = await ProxyWrapper().get(clearnetUri: uri);
264 +
265 if (response.statusCode != 200) {
266 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
267 if (responseJSON.containsKey('code') && responseJSON['code'] == 'NOT_FOUND') {
lib/main.dart
+31 -12
@@ -25,9 +25,12 @@ import 'package:cake_wallet/routes.dart';
25 import 'package:cake_wallet/src/screens/root/root.dart';
26 import 'package:cake_wallet/store/app_store.dart';
27 import 'package:cake_wallet/store/authentication_store.dart';
28 +import 'package:cake_wallet/themes/core/material_base_theme.dart';
29 import 'package:cake_wallet/themes/utils/theme_provider.dart';
30 +import 'package:cake_wallet/store/settings_store.dart';
31 import 'package:cake_wallet/utils/device_info.dart';
32 import 'package:cake_wallet/utils/exception_handler.dart';
33 +import 'package:cake_wallet/utils/feature_flag.dart';
34 import 'package:cake_wallet/view_model/link_view_model.dart';
35 import 'package:cake_wallet/utils/responsive_layout_util.dart';
36 import 'package:cw_core/address_info.dart';
@@ -38,6 +41,8 @@ import 'package:cw_core/node.dart';
41 import 'package:cw_core/payjoin_session.dart';
42 import 'package:cw_core/unspent_coins_info.dart';
43 import 'package:cw_core/utils/print_verbose.dart';
44 +import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
45 +import 'package:cw_core/utils/proxy_wrapper.dart';
46 import 'package:cw_core/wallet_info.dart';
47 import 'package:cw_core/wallet_type.dart';
48 import 'package:flutter/foundation.dart';
@@ -87,6 +92,9 @@ Future<void> runAppWithZone({Key? topLevelKey}) async {
92 ledgerFile.writeAsStringSync("$content\n${event.message}");
93 });
94 }
95 + if (FeatureFlag.hasDevOptions) {
96 + ProxyWrapper.logger = MemoryProxyLogger();
97 + }
98
99 runApp(App(key: topLevelKey));
100 isAppRunning = true;
@@ -194,8 +202,8 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
202 final powNodes =
203 await CakeHive.openBox<Node>(Node.boxName + "pow"); // must be different from Node.boxName
204 final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
197 - TransactionDescription.boxName,
198 - encryptionKey: transactionDescriptionsBoxKey);
205 + TransactionDescription.boxName,
206 + encryptionKey: transactionDescriptionsBoxKey);
207 final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
208 final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
209 final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
@@ -279,7 +287,11 @@ Future<void> initialSetup({
287 navigatorKey: navigatorKey,
288 secureStorage: secureStorage,
289 );
282 - await bootstrap(navigatorKey, loadWallet: loadWallet);
290 + await bootstrapOffline();
291 + final settingsStore = getIt<SettingsStore>();
292 + if (!settingsStore.currentBuiltinTor) {
293 + bootstrapOnline(navigatorKey, loadWallet: loadWallet);
294 + }
295 }
296
297 class App extends StatefulWidget {
@@ -293,20 +305,24 @@ class App extends StatefulWidget {
305 class AppState extends State<App> with SingleTickerProviderStateMixin {
306 @override
307 Widget build(BuildContext context) {
296 - return Observer(
297 - builder: (BuildContext context) {
308 + return Observer(builder: (BuildContext context) {
309 final appStore = getIt.get<AppStore>();
310 final authService = getIt.get<AuthService>();
311 final linkViewModel = getIt.get<LinkViewModel>();
312 final tradeMonitor = getIt.get<TradeMonitor>();
313 + final settingsStore = appStore.settingsStore;
314 final statusBarColor = Colors.transparent;
315 final authenticationStore = getIt.get<AuthenticationStore>();
316 final initialRoute = authenticationStore.state == AuthenticationState.uninitialized
305 - ? Routes.welcome
306 - : Routes.login;
317 + ? Routes.welcome
318 + : settingsStore.currentBuiltinTor ? Routes.startTor : Routes.login;
319 final currentTheme = appStore.themeStore.currentTheme;
308 - final statusBarBrightness = currentTheme.isDark ? Brightness.light : Brightness.dark;
309 - final statusBarIconBrightness = currentTheme.isDark ? Brightness.light : Brightness.dark;
320 + final statusBarBrightness = currentTheme.type == currentTheme.isDark
321 + ? Brightness.light
322 + : Brightness.dark;
323 + final statusBarIconBrightness = currentTheme.type == currentTheme.isDark
324 + ? Brightness.light
325 + : Brightness.dark;
326 SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
327 statusBarColor: statusBarColor,
328 statusBarBrightness: statusBarBrightness,
@@ -322,7 +338,8 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
338 tradeMonitor: tradeMonitor,
339 child: ThemeProvider(
340 themeStore: appStore.themeStore,
325 - materialAppBuilder: (context, theme, darkTheme, themeMode) => MaterialApp(
341 + materialAppBuilder: (context, theme, darkTheme, themeMode) =>
342 + MaterialApp(
343 navigatorObservers: [routeObserver],
344 navigatorKey: navigatorKey,
345 debugShowCheckedModeBanner: false,
@@ -365,8 +382,10 @@ class _HomeState extends State<_Home> {
382 SystemChrome.setPreferredOrientations(
383 [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
384 } else {
368 - SystemChrome.setPreferredOrientations(
369 - [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
385 + SystemChrome.setPreferredOrientations([
386 + DeviceOrientation.landscapeLeft,
387 + DeviceOrientation.landscapeRight
388 + ]);
389 }
390 }
391 }
lib/mastodon/mastodon_api.dart
+5 -3
@@ -1,6 +1,6 @@
1 import 'dart:convert';
2 +import 'package:cw_core/utils/proxy_wrapper.dart';
3 import 'package:cw_core/utils/print_verbose.dart';
3 -import 'package:http/http.dart' as http;
4 import 'package:cake_wallet/mastodon/mastodon_user.dart';
5
6 class MastodonAPI {
@@ -20,7 +20,8 @@ class MastodonAPI {
20 queryParameters: queryParams,
21 );
22
23 - final response = await http.get(uri);
23 + final response = await ProxyWrapper().get(clearnetUri: uri);
24 +
25
26 if (response.statusCode != 200) return null;
27
@@ -47,7 +48,8 @@ class MastodonAPI {
48 queryParameters: queryParams,
49 );
50
50 - final response = await http.get(uri);
51 + final response = await ProxyWrapper().get(clearnetUri: uri);
52 +
53
54 if (response.statusCode != 200) {
55 throw Exception('Unexpected HTTP status: ${response.statusCode}');
lib/reactions/bootstrap.dart
+10 -5
@@ -15,24 +15,29 @@ import 'package:cake_wallet/store/settings_store.dart';
15 import 'package:cake_wallet/store/authentication_store.dart';
16 import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
17
18 -Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey, {required bool loadWallet}) async {
19 - final appStore = getIt.get<AppStore>();
18 +Future<void> bootstrapOffline() async {
19 final authenticationStore = getIt.get<AuthenticationStore>();
21 - final settingsStore = getIt.get<SettingsStore>();
22 - final fiatConversionStore = getIt.get<FiatConversionStore>();
20
21 final currentWalletName =
22 getIt.get<SharedPreferences>().getString(PreferencesKey.currentWalletName);
23 if (currentWalletName != null) {
24 authenticationStore.installed();
25 }
26 +}
27 +
28 +void bootstrapOnline(GlobalKey<NavigatorState> navigatorKey, {required bool loadWallet}) {
29 + final appStore = getIt.get<AppStore>();
30 + final authenticationStore = getIt.get<AuthenticationStore>();
31 + final settingsStore = getIt.get<SettingsStore>();
32 + final fiatConversionStore = getIt.get<FiatConversionStore>();
33
34 if (loadWallet) {
35 startAuthenticationStateChange(authenticationStore, navigatorKey);
36 }
37 +
38 startCurrentWalletChangeReaction(appStore, settingsStore, fiatConversionStore);
39 startCurrentFiatChangeReaction(appStore, settingsStore, fiatConversionStore);
40 startCurrentFiatApiModeChangeReaction(appStore, settingsStore, fiatConversionStore);
41 startOnCurrentNodeChangeReaction(appStore);
42 startFiatRateUpdate(appStore, settingsStore, fiatConversionStore);
38 -}
43 +}
\ No newline at end of file
lib/reactions/check_connection.dart
+8
@@ -1,11 +1,15 @@
1 import 'dart:async';
2
3 +import 'package:cake_wallet/src/screens/base_page.dart';
4 +import 'package:cake_wallet/utils/tor.dart';
5 import 'package:connectivity_plus/connectivity_plus.dart';
6 import 'package:cw_core/utils/print_verbose.dart';
7 import 'package:cw_core/wallet_base.dart';
8 import 'package:cw_core/sync_status.dart';
9 import 'package:cw_core/wallet_type.dart';
10 import 'package:cake_wallet/store/settings_store.dart';
11 +import 'package:flutter/material.dart';
12 +import 'package:get_it/get_it.dart';
13
14 Timer? _checkConnectionTimer;
15
@@ -36,6 +40,10 @@ void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore
40 final alive = await settingsStore.getCurrentNode(wallet.type).requestNode();
41
42 if (alive) {
43 + if (settingsStore.currentBuiltinTor) {
44 + await ensureTorStarted(context: null);
45 + }
46 +
47 await wallet.connectToNode(node: settingsStore.getCurrentNode(wallet.type));
48 }
49 }
lib/reactions/on_current_wallet_change.dart
+5
@@ -7,6 +7,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
7 import 'package:cake_wallet/polygon/polygon.dart';
8 import 'package:cake_wallet/solana/solana.dart';
9 import 'package:cake_wallet/tron/tron.dart';
10 +import 'package:cake_wallet/utils/tor.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 import 'package:cw_core/transaction_history.dart';
13 import 'package:cw_core/balance.dart';
@@ -83,6 +84,10 @@ void startCurrentWalletChangeReaction(
84 bitcoin!.updatePayjoinState(wallet, settingsStore.usePayjoin);
85 }
86
87 + if (settingsStore.currentBuiltinTor) {
88 + await ensureTorStarted(context: null);
89 + }
90 +
91 await wallet.connectToNode(node: node);
92 if (wallet.type == WalletType.nano || wallet.type == WalletType.banano) {
93 final powNode = settingsStore.getCurrentPowNode(wallet.type);
lib/router.dart
+13 -5
@@ -38,6 +38,7 @@ import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
38 import 'package:cake_wallet/src/screens/dashboard/sign_page.dart';
39 import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
40 import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart';
41 +import 'package:cake_wallet/src/screens/dev/network_requests.dart';
42 import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart';
43 import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart';
44 import 'package:cake_wallet/src/screens/dev/background_sync_logs_page.dart';
@@ -93,7 +94,6 @@ import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
94 import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
95 import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
96 import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
96 -import 'package:cake_wallet/src/screens/settings/tor_page.dart';
97 import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
98 import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
99 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
@@ -101,6 +101,7 @@ import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart
101 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
102 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
103 import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
104 +import 'package:cake_wallet/src/screens/start_tor/start_tor_page.dart';
105 import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
106 import 'package:cake_wallet/src/screens/support/support_page.dart';
107 import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
@@ -857,9 +858,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
858 ),
859 );
860
860 - case Routes.torPage:
861 - return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
862 -
861 case Routes.signPage:
862 return MaterialPageRoute<void>(
863 builder: (_) => SignPage(
@@ -910,7 +908,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
908 return MaterialPageRoute<void>(
909 builder: (_) => getIt.get<DevBackgroundSyncLogsPage>(),
910 );
913 -
911 +
912 + case Routes.devNetworkRequests:
913 + return MaterialPageRoute<void>(
914 + builder: (_) => getIt.get<DevNetworkRequests>(),
915 + );
916 +
917 case Routes.devMoneroCallProfiler:
918 return MaterialPageRoute<void>(
919 builder: (_) => getIt.get<DevMoneroCallProfilerPage>(),
@@ -920,6 +923,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
923 return MaterialPageRoute<void>(
924 builder: (_) => getIt.get<DevSecurePreferencesPage>(),
925 );
926 +
927 + case Routes.startTor:
928 + return MaterialPageRoute<void>(
929 + builder: (_) => getIt.get<StartTorPage>(),
930 + );
931
932 case Routes.dEuroSavings:
933 return MaterialPageRoute<void>(
lib/routes.dart
+3 -1
@@ -110,14 +110,16 @@ class Routes {
110 static const walletConnectConnectionsListing = '/wallet-connect-connections-listing';
111 static const nftDetailsPage = '/nft_details_page';
112 static const importNFTPage = '/import_nft_page';
113 - static const torPage = '/tor_page';
113 static const backgroundSync = '/background_sync';
114 + static const startTor = '/start_tor';
115
116 static const devMoneroBackgroundSync = '/dev/monero_background_sync';
117 static const devMoneroCallProfiler = '/dev/monero_call_profiler';
118 +
119 static const devSharedPreferences = '/dev/shared_preferences';
120 static const devSecurePreferences = '/dev/secure_preferences';
121 static const devBackgroundSyncLogs = '/dev/background_sync_logs';
122 + static const devNetworkRequests = '/dev/network_requests';
123
124 static const signPage = '/sign_page';
125 static const connectDevices = '/device/connect';
lib/src/screens/dev/network_requests.dart new
+155
@@ -0,0 +1,155 @@
1 +import 'dart:convert';
2 +import 'dart:typed_data';
3 +
4 +import 'package:cake_wallet/src/screens/base_page.dart';
5 +import 'package:cake_wallet/view_model/dev/network_requests_view_model.dart';
6 +import 'package:cw_core/utils/proxy_logger/abstract.dart';
7 +import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
8 +import 'package:flutter/material.dart';
9 +import 'package:flutter_mobx/flutter_mobx.dart';
10 +
11 +class DevNetworkRequests extends BasePage {
12 + final NetworkRequestsViewModel viewModel = NetworkRequestsViewModel();
13 +
14 + DevNetworkRequests() {
15 + viewModel.loadLogs();
16 + }
17 +
18 + @override
19 + String? get title => "[dev] network requests";
20 +
21 + @override
22 + Widget? trailing(BuildContext context) {
23 + return IconButton(
24 + icon: Icon(Icons.refresh),
25 + onPressed: () => viewModel.loadLogs(),
26 + );
27 + }
28 +
29 + @override
30 + Widget body(BuildContext context) {
31 + return Observer(
32 + builder: (_) {
33 + if (viewModel.logs.isEmpty) {
34 + return Center(
35 + child: Column(
36 + mainAxisAlignment: MainAxisAlignment.center,
37 + children: [
38 + Text("No logs loaded"),
39 + SizedBox(height: 16),
40 + ElevatedButton(
41 + onPressed: () => viewModel.loadLogs(),
42 + child: Text("Load Logs"),
43 + ),
44 + ],
45 + ),
46 + );
47 + } else {
48 + return ListView.builder(
49 + itemCount: viewModel.logs.length,
50 + itemBuilder: (BuildContext context, int i) {
51 + final item = viewModel.logs[i];
52 + return ListTile(
53 + onTap: () {
54 + Navigator.of(context).push(
55 + MaterialPageRoute(
56 + builder: (BuildContext context) {
57 + return DevRequestDetails(item);
58 + },
59 + ),
60 + );
61 + },
62 + leading: switch (item.network) {
63 + RequestNetwork.clearnet => Text("C"),
64 + RequestNetwork.tor => Text("T"),
65 + },
66 + trailing: switch (item.method) {
67 + RequestMethod.get => Text("GET"),
68 + RequestMethod.post => Text("POST"),
69 + RequestMethod.put => Text("PUT"),
70 + RequestMethod.delete => Text("DELETE"),
71 + RequestMethod.newHttpClient ||
72 + RequestMethod.newHttpIOClient ||
73 + RequestMethod.newProxySocket => null,
74 + },
75 + title: Text(item.time.toIso8601String()),
76 + subtitle: switch (item.method) {
77 + RequestMethod.get ||
78 + RequestMethod.post ||
79 + RequestMethod.put ||
80 + RequestMethod.delete => Text("${item.uri}"),
81 + RequestMethod.newHttpClient => Text("newHttpClient"),
82 + RequestMethod.newHttpIOClient => Text("newHttpIOClient"),
83 + RequestMethod.newProxySocket => Text("newProxySocket"),
84 + },
85 + tileColor: item.error != null ? Colors.red : null,
86 + );
87 + },
88 + );
89 + }
90 + },
91 + );
92 + }
93 +}
94 +
95 +class DevRequestDetails extends BasePage {
96 + final MemoryProxyLoggerEntry req;
97 + DevRequestDetails(this.req);
98 +
99 + @override
100 + Widget body(BuildContext context) {
101 + return SingleChildScrollView(
102 + padding: const EdgeInsets.all(16),
103 + child: _body(context),
104 + );
105 + }
106 +
107 + Widget _body(BuildContext context) {
108 + return Column(
109 + crossAxisAlignment: CrossAxisAlignment.start,
110 + children: [
111 + _sectionTitle("Time"),
112 + SelectableText(req.time.toString()),
113 +
114 + _sectionTitle("Method"),
115 + SelectableText(req.method.toString()),
116 +
117 + _sectionTitle("URI"),
118 + SelectableText(req.uri?.toString() ?? "null"),
119 +
120 + _sectionTitle("Network"),
121 + SelectableText(req.network.toString()),
122 +
123 + _sectionTitle("Body (as UTF-8)"),
124 + SelectableText(_tryDecodeBody(req.body)),
125 +
126 + _sectionTitle("Response"),
127 + SelectableText(req.response?.body ?? "null"),
128 +
129 + _sectionTitle("Error"),
130 + SelectableText(req.error ?? "No error"),
131 +
132 + _sectionTitle("Stack Trace"),
133 + SelectableText(req.trace.toString()),
134 + ],
135 + );
136 + }
137 +
138 + Widget _sectionTitle(String title) {
139 + return Padding(
140 + padding: const EdgeInsets.symmetric(vertical: 8.0),
141 + child: Text(
142 + title,
143 + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
144 + ),
145 + );
146 + }
147 +
148 + String _tryDecodeBody(Uint8List body) {
149 + try {
150 + return utf8.decode(body);
151 + } catch (_) {
152 + return 'Binary data (${body.length} bytes)';
153 + }
154 + }
155 +}
lib/src/screens/nodes/widgets/node_form.dart
+20 -1
@@ -196,8 +196,27 @@ class NodeForm extends StatelessWidget {
196 Observer(
197 builder: (_) => Column(
198 children: [
199 + if (nodeViewModel.usesEmbeddedProxy) ...[
200 + Padding(
201 + padding: EdgeInsets.only(top: 10),
202 + child: Row(
203 + mainAxisAlignment: MainAxisAlignment.start,
204 + mainAxisSize: MainAxisSize.max,
205 + children: [
206 + StandardCheckbox(
207 + value: nodeViewModel.usesEmbeddedProxy,
208 + gradientBackground: false,
209 + borderColor: Theme.of(context).dividerColor,
210 + iconColor: Theme.of(context).colorScheme.primary,
211 + onChanged: null,
212 + caption: 'Embedded Tor SOCKS Proxy',
213 + ),
214 + ],
215 + ),
216 + ),
217 + ],
218 Padding(
200 - padding: EdgeInsets.only(top: 20),
219 + padding: EdgeInsets.only(top: 10),
220 child: Row(
221 mainAxisAlignment: MainAxisAlignment.start,
222 mainAxisSize: MainAxisSize.max,
lib/src/screens/settings/connection_sync_page.dart
+23 -4
@@ -5,6 +5,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
5 import 'package:cake_wallet/routes.dart';
6 import 'package:cake_wallet/src/screens/base_page.dart';
7 import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
8 +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
9 import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart';
10 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
11 import 'package:cake_wallet/utils/feature_flag.dart';
@@ -71,10 +72,28 @@ class ConnectionSyncPage extends BasePage {
72 ),
73 ],
74 if (FeatureFlag.isInAppTorEnabled)
74 - SettingsCellWithArrow(
75 - title: S.current.tor_connection,
76 - handler: (context) => Navigator.of(context).pushNamed(Routes.torPage),
77 - ),
75 + Observer(builder: (context) {
76 + return SettingsSwitcherCell(
77 + leading: Container(
78 + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 1),
79 + decoration: BoxDecoration(
80 + color: Colors.red.shade100,
81 + borderRadius: BorderRadius.circular(3),
82 + ),
83 + child: Text(
84 + 'Alpha',
85 + style: TextStyle(
86 + color: Colors.red.shade700,
87 + fontSize: 8,
88 + fontWeight: FontWeight.bold,
89 + ),
90 + ),
91 + ),
92 + title: S.current.enable_builtin_tor,
93 + value: dashboardViewModel.builtinTor,
94 + onValueChange: (_, bool value) => dashboardViewModel.setBuiltinTor(value, context),
95 + );
96 + }),
97 ],
98 ),
99 );
lib/src/screens/settings/other_settings_page.dart
+6
@@ -93,6 +93,12 @@ class OtherSettingsPage extends BasePage {
93 handler: (BuildContext context) =>
94 Navigator.of(context).pushNamed(Routes.devBackgroundSyncLogs),
95 ),
96 + if (FeatureFlag.hasDevOptions)
97 + SettingsCellWithArrow(
98 + title: '[dev] network requests logs',
99 + handler: (BuildContext context) =>
100 + Navigator.of(context).pushNamed(Routes.devNetworkRequests),
101 + ),
102 Spacer(),
103 SettingsVersionCell(
104 title: S.of(context).version(_otherSettingsViewModel.currentVersion)),
lib/src/screens/settings/tor_page.dart deleted
-269
@@ -1,269 +0,0 @@
1 -import 'dart:async';
2 -
3 -import 'package:cake_wallet/src/screens/base_page.dart';
4 -import 'package:cake_wallet/store/app_store.dart';
5 -import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
6 -import 'package:cw_core/utils/print_verbose.dart';
7 -import 'package:flutter/material.dart';
8 -// import 'package:tor/tor.dart';
9 -
10 -class TorPage extends BasePage {
11 - final AppStore appStore;
12 -
13 - TorPage(this.appStore);
14 -
15 - @override
16 - Widget body(BuildContext context) {
17 - return TorPageBody(appStore);
18 - }
19 -}
20 -
21 -class TorPageBody extends StatefulWidget {
22 - final AppStore appStore;
23 -
24 - const TorPageBody(this.appStore, {Key? key}) : super(key: key);
25 -
26 - @override
27 - State<TorPageBody> createState() => _TorPageBodyState();
28 -}
29 -
30 -class _TorPageBodyState extends State<TorPageBody> {
31 - bool torEnabled = false;
32 - bool connecting = false;
33 -
34 - // Set the default text for the host input field.
35 - final hostController = TextEditingController(text: 'https://icanhazip.com/');
36 -
37 - // https://check.torproject.org is another good option.
38 -
39 - Future<void> startTor() async {
40 - setState(() {
41 - connecting = true; // Update flag
42 - });
43 -
44 - // await Tor.init();
45 - //
46 - // // Start the proxy
47 - // await Tor.instance.start();
48 - //
49 - // // Toggle started flag.
50 - // setState(() {
51 - // torEnabled = Tor.instance.enabled; // Update flag
52 - // connecting = false;
53 - // });
54 - //
55 - // final node = widget.appStore.settingsStore.getCurrentNode(widget.appStore.wallet!.type);
56 - // if (node.socksProxyAddress?.isEmpty ?? true) {
57 - // node.socksProxyAddress = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
58 - // }
59 - // widget.appStore.wallet!.connectToNode(node: node);
60 -
61 - printV('Done awaiting; tor should be running');
62 - }
63 -
64 - Future<void> endTor() async {
65 - // // Start the proxy
66 - // Tor.instance.disable();
67 - //
68 - // // Toggle started flag.
69 - // setState(() {
70 - // torEnabled = Tor.instance.enabled; // Update flag
71 - // });
72 - //
73 - // printV('Done awaiting; tor should be stopped');
74 - }
75 - //
76 - // @override
77 - // void initState() {
78 - // super.initState();
79 - //
80 - // torEnabled = Tor.instance.enabled;
81 - // }
82 -
83 - @override
84 - void dispose() {
85 - // Clean up the controller when the widget is disposed.
86 - hostController.dispose();
87 - super.dispose();
88 - }
89 -
90 - @override
91 - Widget build(BuildContext context) {
92 - return SingleChildScrollView(
93 - child: Container(
94 - padding: const EdgeInsets.all(10),
95 - child: connecting
96 - ? ConnectingScreen()
97 - : torEnabled
98 - ? DisconnectScreen(disconnect: endTor)
99 - : ConnectScreen(connect: startTor),
100 - ),
101 - );
102 - }
103 -}
104 -
105 -class ConnectScreen extends StatelessWidget {
106 - final Function() connect;
107 -
108 - const ConnectScreen({super.key, required this.connect});
109 -
110 - @override
111 - Widget build(BuildContext context) {
112 - return Center(
113 - child: Padding(
114 - padding: const EdgeInsets.symmetric(horizontal: 16),
115 - child: Column(
116 - mainAxisAlignment: MainAxisAlignment.center,
117 - children: <Widget>[
118 - Container(
119 - width: 200,
120 - height: 200,
121 - decoration: BoxDecoration(
122 - shape: BoxShape.circle,
123 - color: Theme.of(context).colorScheme.primary,
124 - ),
125 - child: Icon(
126 - Icons.lock,
127 - color: Theme.of(context).colorScheme.onPrimary,
128 - size: 100,
129 - ),
130 - ),
131 - SizedBox(height: 48),
132 - Text(
133 - 'Connect to Tor',
134 - style: Theme.of(context).textTheme.titleLarge?.copyWith(
135 - fontSize: 24,
136 - fontWeight: FontWeight.bold,
137 - ),
138 - ),
139 - SizedBox(height: 16),
140 - Text(
141 - 'Your connection to the Tor network ensures privacy and security.',
142 - style: Theme.of(context).textTheme.bodyMedium?.copyWith(
143 - fontSize: 16,
144 - color: Theme.of(context).colorScheme.onSurfaceVariant,
145 - ),
146 - textAlign: TextAlign.center,
147 - ),
148 - SizedBox(height: 48),
149 - ElevatedButton(
150 - onPressed: connect,
151 - style: ElevatedButton.styleFrom(
152 - padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
153 - shape: RoundedRectangleBorder(
154 - borderRadius: BorderRadius.circular(12),
155 - ),
156 - ),
157 - child: Text(
158 - 'Connect',
159 - style: Theme.of(context).textTheme.bodyMedium?.copyWith(
160 - fontSize: 18,
161 - color: Theme.of(context).colorScheme.onPrimary,
162 - ),
163 - ),
164 - ),
165 - ],
166 - ),
167 - ),
168 - );
169 - }
170 -}
171 -
172 -class DisconnectScreen extends StatelessWidget {
173 - final Function() disconnect;
174 -
175 - const DisconnectScreen({super.key, required this.disconnect});
176 -
177 - @override
178 - Widget build(BuildContext context) {
179 - return Center(
180 - child: Column(
181 - mainAxisAlignment: MainAxisAlignment.center,
182 - children: <Widget>[
183 - Container(
184 - width: 200,
185 - height: 200,
186 - decoration: BoxDecoration(
187 - shape: BoxShape.circle,
188 - color: CustomThemeColors.syncGreen,
189 - ),
190 - child: Icon(
191 - Icons.check,
192 - color: Theme.of(context).colorScheme.onPrimary,
193 - size: 100,
194 - ),
195 - ),
196 - SizedBox(height: 20),
197 - Text(
198 - 'Connected to Tor',
199 - style: Theme.of(context).textTheme.titleLarge?.copyWith(
200 - fontSize: 24,
201 - fontWeight: FontWeight.bold,
202 - ),
203 - ),
204 - SizedBox(height: 10),
205 - Text(
206 - 'You are currently connected to the Tor network.',
207 - style: Theme.of(context).textTheme.bodyMedium?.copyWith(
208 - fontSize: 16,
209 - color: Theme.of(context).colorScheme.onSurfaceVariant,
210 - ),
211 - textAlign: TextAlign.center,
212 - ),
213 - SizedBox(height: 30),
214 - ElevatedButton(
215 - onPressed: disconnect,
216 - style: ElevatedButton.styleFrom(
217 - padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
218 - shape: RoundedRectangleBorder(
219 - borderRadius: BorderRadius.circular(12),
220 - ),
221 - ),
222 - child: Text(
223 - 'Disconnect',
224 - style: Theme.of(context).textTheme.bodyMedium?.copyWith(
225 - fontSize: 18,
226 - color: Theme.of(context).colorScheme.onPrimary,
227 - ),
228 - ),
229 - ),
230 - ],
231 - ),
232 - );
233 - }
234 -}
235 -
236 -class ConnectingScreen extends StatelessWidget {
237 - @override
238 - Widget build(BuildContext context) {
239 - return Center(
240 - child: Column(
241 - mainAxisAlignment: MainAxisAlignment.center,
242 - children: <Widget>[
243 - Container(
244 - width: 200,
245 - height: 200,
246 - decoration: BoxDecoration(
247 - shape: BoxShape.circle,
248 - color: CustomThemeColors.syncYellow,
249 - ),
250 - child: Icon(
251 - Icons.hourglass_bottom,
252 - color: Theme.of(context).colorScheme.onPrimary,
253 - size: 100,
254 - ),
255 - ),
256 - SizedBox(height: 20),
257 - Text(
258 - 'Connecting...',
259 - style: Theme.of(context).textTheme.titleLarge?.copyWith(
260 - fontSize: 24,
261 - fontWeight: FontWeight.bold,
262 - ),
263 - ),
264 - SizedBox(height: 10),
265 - ],
266 - ),
267 - );
268 - }
269 -}
lib/src/screens/start_tor/start_tor_page.dart new
+96
@@ -0,0 +1,96 @@
1 +import 'package:flutter/material.dart';
2 +import 'package:flutter_mobx/flutter_mobx.dart';
3 +import 'package:cake_wallet/generated/i18n.dart';
4 +import 'package:cake_wallet/src/screens/base_page.dart';
5 +import 'package:cake_wallet/src/widgets/primary_button.dart';
6 +import 'package:cake_wallet/view_model/start_tor_view_model.dart';
7 +
8 +class StartTorPage extends BasePage {
9 + StartTorPage(this.startTorViewModel);
10 +
11 + final StartTorViewModel startTorViewModel;
12 +
13 + @override
14 + String get title => S.current.tor_connection;
15 +
16 + @override
17 + Widget leading(BuildContext context) {
18 + return Container();
19 + }
20 +
21 + @override
22 + Widget body(BuildContext context) {
23 + startTorViewModel.startTor(context);
24 + return Container(
25 + padding: EdgeInsets.all(24),
26 + child: Column(
27 + mainAxisAlignment: MainAxisAlignment.center,
28 + crossAxisAlignment: CrossAxisAlignment.center,
29 + children: [
30 + Column(
31 + children: [
32 + SizedBox(width: double.maxFinite),
33 + if (startTorViewModel.isLoading) ...[
34 + CircularProgressIndicator(),
35 + SizedBox(height: 20),
36 + _buildWaitingText(context),
37 + ],
38 + if (startTorViewModel.showOptions) ...[
39 + _buildOptionsButtons(context),
40 + ],
41 + ],
42 + ),
43 + ],
44 + ),
45 + );
46 + }
47 +
48 + Widget _buildWaitingText(BuildContext context) {
49 + return Observer(
50 + builder: (_) {
51 + return Column(
52 + children: [
53 + Text(
54 + S.current.establishing_tor_connection,
55 + style: TextStyle(
56 + fontSize: 16,
57 + fontWeight: FontWeight.bold,
58 + ),
59 + textAlign: TextAlign.center,
60 + ),
61 + if (startTorViewModel.showOptions) _buildOptionsButtons(context),
62 + ],
63 + );
64 + },
65 + );
66 + }
67 +
68 + Widget _buildOptionsButtons(BuildContext context) {
69 + return Column(
70 + children: [
71 + Text(
72 + S.current.tor_connection_timeout,
73 + style: TextStyle(
74 + fontSize: 16,
75 + fontWeight: FontWeight.bold,
76 + ),
77 + textAlign: TextAlign.center,
78 + ),
79 + SizedBox(height: 24),
80 + PrimaryButton(
81 + onPressed: () => startTorViewModel.disableTor(context),
82 + text: S.current.disable_tor,
83 + color: Theme.of(context).colorScheme.primary,
84 + textColor: Colors.white,
85 + ),
86 + SizedBox(height: 16),
87 + PrimaryButton(
88 + onPressed: () => startTorViewModel.ignoreAndLaunchApp(context),
89 + text: S.current.ignor,
90 + color: Theme.of(context).colorScheme.secondary,
91 + textColor: Colors.white,
92 + ),
93 + ],
94 + );
95 + }
96 +}
\ No newline at end of file
lib/src/screens/wallet_connect/services/chain_service/eth/evm_chain_service.dart
+5 -4
@@ -2,10 +2,10 @@ import 'dart:convert';
2
3 import 'package:cake_wallet/generated/i18n.dart';
4 import 'package:cake_wallet/reactions/wallet_connect.dart';
5 +import 'package:cw_core/utils/proxy_wrapper.dart';
6 import 'package:eth_sig_util/eth_sig_util.dart';
7 import 'package:eth_sig_util/util/utils.dart';
8 import 'package:flutter/material.dart';
8 -import 'package:http/http.dart' as http;
9 import 'package:reown_walletkit/reown_walletkit.dart';
10
11 import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
@@ -41,7 +41,7 @@ class EvmChainServiceImpl {
41 }) : ethClient = web3Client ??
42 Web3Client(
43 appStore.settingsStore.getCurrentNode(appStore.wallet!.type).uri.toString(),
44 - http.Client(),
44 + ProxyWrapper().getHttpIOClient(),
45 ) {
46 for (final event in EventsConstants.allEvents) {
47 walletKit.registerEventEmitter(
@@ -563,14 +563,15 @@ class EvmChainServiceImpl {
563 },
564 );
565
566 - final response = await http.get(
567 - uri,
566 + final response = await ProxyWrapper().get(
567 + clearnetUri: uri,
568 headers: {
569 "Accept": "application/json",
570 "X-API-Key": secrets.moralisApiKey,
571 },
572 );
573
574 +
575 final decodedResponse = jsonDecode(response.body)[0] as Map<String, dynamic>;
576
577 final symbol = (decodedResponse['symbol'] ?? '') as String;
lib/src/screens/wallet_connect/wc_pairing_detail_page.dart
+5 -1
@@ -4,6 +4,7 @@ import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_servic
4 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
5 import 'package:cake_wallet/src/widgets/primary_button.dart';
6 import 'package:cake_wallet/utils/show_pop_up.dart';
7 +import 'package:cw_core/utils/proxy_wrapper.dart';
8 import 'package:flutter/material.dart';
9 import 'package:reown_walletkit/reown_walletkit.dart';
10
@@ -153,7 +154,10 @@ class WCCDetailsWidget extends BasePage {
154 child: CircleAvatar(
155 backgroundImage: (pairing.peerMetadata!.icons.isNotEmpty
156 ? NetworkImage(pairing.peerMetadata!.icons[0])
156 - : const AssetImage('assets/images/app_logo.png')) as ImageProvider<Object>,
157 + : AssetImage(
158 + CakeTor.instance.enabled
159 + ? 'assets/images/tor_logo.svg'
160 + : 'assets/images/app_logo.png')) as ImageProvider<Object>,
161 ),
162 ),
163 const SizedBox(height: 20.0),
lib/src/screens/wallet_keys/wallet_keys_page.dart
+3 -3
@@ -126,9 +126,9 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
126 dividerColor: Colors.transparent,
127 padding: EdgeInsets.zero,
128 tabs: [
129 - Tab(text: S.of(context).widgets_seed),
130 - if (showKeyTab) Tab(text: S.of(context).keys),
131 - if (showLegacySeedTab) Tab(text: S.of(context).legacy),
129 + Tab(text: S.of(context).widgets_seed, key: ValueKey('wallet_keys_page_seed')),
130 + if (showKeyTab) Tab(text: S.of(context).keys, key: ValueKey('wallet_keys_page_keys'),),
131 + if (showLegacySeedTab) Tab(text: S.of(context).legacy, key: ValueKey('wallet_keys_page_seed_legacy')),
132 ],
133 ),
134 ),
lib/src/widgets/provider_optoin_tile.dart
+6 -1
@@ -1,3 +1,4 @@
1 +import 'package:cw_core/utils/proxy_wrapper.dart';
2 import 'package:flutter/material.dart';
3 import 'package:flutter_svg/svg.dart';
4
@@ -265,7 +266,11 @@ class Badge extends StatelessWidget {
266 }
267
268 Widget getImage(String imagePath, {double? height, double? width, Color? imageColor}) {
268 - final bool isNetworkImage = imagePath.startsWith('http') || imagePath.startsWith('https');
269 + bool isNetworkImage = imagePath.startsWith('http') || imagePath.startsWith('https');
270 + if (CakeTor.instance.enabled && isNetworkImage) {
271 + imagePath = "assets/images/tor_logo.svg";
272 + isNetworkImage = false;
273 + }
274 final bool isSvg = imagePath.endsWith('.svg');
275 final double imageHeight = height ?? 35;
276 final double imageWidth = width ?? 35;
lib/src/widgets/standard_checkbox.dart
+2 -2
@@ -16,7 +16,7 @@ class StandardCheckbox extends StatelessWidget {
16 final Color? borderColor;
17 final Color? iconColor;
18 final Color? captionColor;
19 - final Function(bool) onChanged;
19 + final Function(bool)? onChanged;
20
21 @override
22 Widget build(BuildContext context) {
@@ -40,7 +40,7 @@ class StandardCheckbox extends StatelessWidget {
40 BoxDecoration(border: boxBorder, borderRadius: BorderRadius.all(Radius.circular(8.0)));
41
42 return GestureDetector(
43 - onTap: () => onChanged(!value),
43 + onTap: onChanged == null ? null : () => onChanged!(!value),
44 child: Row(
45 mainAxisSize: MainAxisSize.min,
46 mainAxisAlignment: MainAxisAlignment.start,
lib/store/settings_store.dart
+12
@@ -80,6 +80,7 @@ abstract class SettingsStoreBase with Store {
80 required String initialLanguageCode,
81 required SyncMode initialSyncMode,
82 required bool initialSyncAll,
83 + required bool initialBuiltinTor,
84 // required String initialCurrentLocale,
85 required this.appVersion,
86 required this.deviceName,
@@ -184,6 +185,7 @@ abstract class SettingsStoreBase with Store {
185 initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings,
186 currentSyncMode = initialSyncMode,
187 currentSyncAll = initialSyncAll,
188 + currentBuiltinTor = initialBuiltinTor,
189 priority = ObservableMap<WalletType, TransactionPriority>() {
190 //this.nodes = ObservableMap<WalletType, Node>.of(nodes);
191
@@ -404,6 +406,11 @@ abstract class SettingsStoreBase with Store {
406 sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
407 });
408
409 + reaction((_) => currentBuiltinTor, (bool builtinTor) {
410 + sharedPreferences.setBool(PreferencesKey.builtinTorKey, builtinTor);
411 + });
412 +
413 +
414 reaction(
415 (_) => exchangeStatus,
416 (ExchangeApiMode mode) =>
@@ -821,6 +828,9 @@ abstract class SettingsStoreBase with Store {
828 @observable
829 bool currentSyncAll;
830
831 + @observable
832 + bool currentBuiltinTor;
833 +
834 String appVersion;
835
836 String deviceName;
@@ -1168,6 +1178,7 @@ abstract class SettingsStoreBase with Store {
1178 return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 2); // default to 2 - daily sync
1179 });
1180 final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true;
1181 + final builtinTor = sharedPreferences.getBool(PreferencesKey.builtinTorKey) ?? false;
1182
1183 // migrated to secure:
1184 final timeOutDuration = await SecureKey.getInt(
@@ -1353,6 +1364,7 @@ abstract class SettingsStoreBase with Store {
1364 initialSyncAll: savedSyncAll,
1365 shouldShowYatPopup: shouldShowYatPopup,
1366 shouldShowRepWarning: shouldShowRepWarning,
1367 + initialBuiltinTor: builtinTor,
1368 );
1369 }
1370
lib/store/yat/yat_store.dart
-4
@@ -5,12 +5,8 @@ import 'package:cw_core/wallet_base.dart';
5 import 'package:cw_core/balance.dart';
6 import 'package:cw_core/transaction_info.dart';
7 import 'package:cake_wallet/store/app_store.dart';
8 -import 'package:flutter/foundation.dart';
8 import 'package:mobx/mobx.dart';
9 import 'package:cw_core/wallet_type.dart';
11 -import 'dart:convert';
12 -import 'package:cake_wallet/store/yat/yat_exception.dart';
13 -import 'package:http/http.dart';
10 import 'dart:async';
11
12 part 'yat_store.g.dart';
lib/twitter/twitter_api.dart
+3 -2
@@ -2,7 +2,7 @@ import 'dart:convert';
2
3 import 'package:cake_wallet/.secrets.g.dart' as secrets;
4 import 'package:cake_wallet/twitter/twitter_user.dart';
5 -import 'package:http/http.dart' as http;
5 +import 'package:cw_core/utils/proxy_wrapper.dart';
6
7 class TwitterApi {
8 static const twitterBearerToken = secrets.twitterBearerToken;
@@ -23,13 +23,14 @@ class TwitterApi {
23 path: userPath + userName,
24 queryParameters: queryParams);
25
26 - final response = await http.get(uri, headers: headers).catchError((error) {
26 + final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers).catchError((error) {
27 throw Exception('HTTP request failed: $error');
28 });
29
30 if (response.statusCode != 200) {
31 throw Exception('Unexpected http status: ${response.statusCode}');
32 }
33 +
34
35 final Map<String, dynamic> responseJSON = jsonDecode(response.body) as Map<String, dynamic>;
36 if (responseJSON['errors'] != null &&
lib/utils/feature_flag.dart
+2 -1
@@ -1,10 +1,11 @@
1 import 'package:flutter/foundation.dart';
2 +import 'dart:io';
3
4 class FeatureFlag {
5 static const bool isCakePayEnabled = false;
6 static const bool isExolixEnabled = true;
6 - static const bool isInAppTorEnabled = false;
7 static const bool isBackgroundSyncEnabled = true;
8 + static final bool isInAppTorEnabled = (Platform.isAndroid);
9 static const int verificationWordsCount = kDebugMode ? 0 : 2;
10 static const bool hasDevOptions = bool.fromEnvironment('hasDevOptions', defaultValue: kDebugMode);
11 }
\ No newline at end of file
lib/utils/image_utill.dart
+6 -2
@@ -1,13 +1,17 @@
1 +import 'package:cw_core/utils/proxy_wrapper.dart';
2 import 'package:flutter/material.dart';
3 import 'package:flutter_svg/svg.dart';
4
5 class ImageUtil {
6 static Widget getImageFromPath({required String imagePath, double? height, double? width}) {
6 - final bool isNetworkImage = imagePath.startsWith('http') || imagePath.startsWith('https');
7 + bool isNetworkImage = imagePath.startsWith('http') || imagePath.startsWith('https');
8 + if (CakeTor.instance.enabled && isNetworkImage) {
9 + imagePath = "assets/images/tor_logo.svg";
10 + isNetworkImage = false;
11 + }
12 final bool isSvg = imagePath.endsWith('.svg');
13 final double _height = height ?? 35;
14 final double _width = width ?? 35;
10 -
15 if (isNetworkImage) {
16 return isSvg
17 ? SvgPicture.network(
lib/utils/tor.dart new
+80
@@ -0,0 +1,80 @@
1 +import 'dart:async';
2 +
3 +import 'package:cw_core/utils/proxy_wrapper.dart';
4 +import 'package:cw_core/utils/print_verbose.dart';
5 +import 'package:flutter/material.dart';
6 +
7 +bool didTorStart = false;
8 +Future<void> ensureTorStopped({required BuildContext? context}) async {
9 + if (!didTorStart) {
10 + printV("Tor hasn't been initialized yet, so it can't be stopped.");
11 + return;
12 + }
13 + BuildContext? dialogContext;
14 + if (context != null) dialogContext = await showFullscreenDialog(context);
15 + didTorStart = false;
16 + printV("Stopping tor");
17 + await CakeTor.instance.stop();
18 + printV("Tor stopped");
19 + if (context != null) dismissFullscreenDialog(dialogContext!);
20 +}
21 +
22 +Future<void> ensureTorStarted({required BuildContext? context}) async {
23 + if (didTorStart) {
24 + printV("Tor has already started");
25 + return;
26 + }
27 + BuildContext? dialogContext;
28 + if (context != null) dialogContext = await showFullscreenDialog(context);
29 + didTorStart = true;
30 + printV("Starting tor");
31 + // var rootToken = RootIsolateToken.instance!;
32 + // await Isolate.run(() async {
33 + // BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken);
34 + // await CakeTor.instance.start();
35 + // });
36 + // second start is fast but populates the values on current thread
37 + await CakeTor.instance.start();
38 + printV("Tor started");
39 + while (!CakeTor.instance.started) {
40 + printV("Waiting for tor to start (part 1)");
41 + await Future.delayed(const Duration(seconds: 1));
42 + }
43 + while (CakeTor.instance.port == -1) {
44 + printV("Waiting for tor to start (listening on port)");
45 + await Future.delayed(const Duration(seconds: 1));
46 + }
47 + printV("Tor started on port ${CakeTor.instance.port}");
48 + if (context != null) dismissFullscreenDialog(dialogContext!);
49 +}
50 +
51 +Future<BuildContext> showFullscreenDialog(BuildContext context) async {
52 + BuildContext? dialogContext;
53 + unawaited(
54 + showDialog(
55 + context: context,
56 + barrierDismissible: false,
57 + builder: (context) {
58 + dialogContext = context;
59 + return PopScope(
60 + canPop: false,
61 + child: Container(
62 + color: Colors.transparent,
63 + child: Center(
64 + child: CircularProgressIndicator(
65 + color: Colors.white,
66 + ),
67 + ),
68 + ),
69 + );
70 + },
71 + ),
72 + );
73 + await Future.delayed(const Duration(seconds: 1));
74 + return dialogContext!;
75 +}
76 +
77 +Future<void> dismissFullscreenDialog(BuildContext context) async {
78 + await Future.delayed(const Duration(seconds: 1));
79 + Navigator.of(context).pop();
80 +}
\ No newline at end of file
lib/view_model/cake_pay/cake_pay_cards_list_view_model.dart
+21 -12
@@ -6,6 +6,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
6 import 'package:cake_wallet/generated/i18n.dart';
7 import 'package:cake_wallet/store/settings_store.dart';
8 import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
9 +import 'package:cw_core/utils/print_verbose.dart';
10 import 'package:mobx/mobx.dart';
11
12 part 'cake_pay_cards_list_view_model.g.dart';
@@ -135,7 +136,11 @@ abstract class CakePayCardsListViewModelBase with Store {
136
137
138 Future<void> getCountries() async {
138 - availableCountries = await cakePayService.getCountries();
139 + try {
140 + availableCountries = await cakePayService.getCountries();
141 + } catch (e) {
142 + printV(e);
143 + }
144 }
145
146 @action
@@ -144,17 +149,21 @@ abstract class CakePayCardsListViewModelBase with Store {
149 int? currentPage,
150 }) async {
151 vendorsState = CakePayVendorLoadingState();
147 - searchString = text ?? '';
148 - var newVendors = await cakePayService.getVendors(
149 - country: Country.getCakePayName(selectedCountry),
150 - page: currentPage ?? page,
151 - search: searchString,
152 - giftCards: displayGiftCards,
153 - prepaidCards: displayPrepaidCards,
154 - custom: displayCustomValueCards,
155 - onDemand: displayDenominationsCards);
156 -
157 - cakePayVendors = CakePayVendorList = newVendors;
152 + try {
153 + searchString = text ?? '';
154 + var newVendors = await cakePayService.getVendors(
155 + country: Country.getCakePayName(selectedCountry),
156 + page: currentPage ?? page,
157 + search: searchString,
158 + giftCards: displayGiftCards,
159 + prepaidCards: displayPrepaidCards,
160 + custom: displayCustomValueCards,
161 + onDemand: displayDenominationsCards);
162 +
163 + cakePayVendors = CakePayVendorList = newVendors;
164 + } catch (e) {
165 + printV(e);
166 + }
167 vendorsState = CakePayVendorLoadedState();
168 }
169
lib/view_model/dashboard/dashboard_view_model.dart
+50 -4
@@ -13,6 +13,11 @@ import 'package:cake_wallet/entities/service_status.dart';
13 import 'package:cake_wallet/exchange/exchange_provider_description.dart';
14 import 'package:cake_wallet/generated/i18n.dart';
15 import 'package:cake_wallet/monero/monero.dart';
16 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
17 +import 'package:cake_wallet/utils/show_pop_up.dart';
18 +import 'package:cw_core/utils/proxy_wrapper.dart';
19 +import 'package:cake_wallet/utils/tor.dart';
20 +import 'package:cake_wallet/wownero/wownero.dart' as wow;
21 import 'package:cake_wallet/nano/nano.dart';
22 import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
23 import 'package:cake_wallet/store/app_store.dart';
@@ -47,10 +52,9 @@ import 'package:cw_core/wallet_base.dart';
52 import 'package:cw_core/wallet_info.dart';
53 import 'package:cw_core/wallet_type.dart';
54 import 'package:eth_sig_util/util/utils.dart';
50 -import 'package:flutter/foundation.dart';
55 +import 'package:flutter/material.dart';
56 import 'package:flutter/services.dart';
57 import 'package:flutter_daemon/flutter_daemon.dart';
53 -import 'package:http/http.dart' as http;
58 import 'package:mobx/mobx.dart';
59 import 'package:permission_handler/permission_handler.dart';
60 import 'package:shared_preferences/shared_preferences.dart';
@@ -1027,6 +1031,39 @@ abstract class DashboardViewModelBase with Store {
1031 @computed
1032 bool get syncAll => settingsStore.currentSyncAll;
1033
1034 + @computed
1035 + bool get builtinTor => settingsStore.currentBuiltinTor;
1036 +
1037 + @action
1038 + void setBuiltinTor(bool value, BuildContext context) {
1039 + if (value) {
1040 + unawaited(showPopUp<bool>(
1041 + context: context,
1042 + builder: (BuildContext context) {
1043 + return AlertWithOneAction(
1044 + alertTitle: S.of(context).tor_connection,
1045 + alertContent: S.of(context).tor_experimental,
1046 + buttonText: S.of(context).ok,
1047 + buttonAction: () => Navigator.of(context).pop(true),
1048 + );
1049 + },
1050 + ),
1051 + );
1052 + }
1053 + settingsStore.currentBuiltinTor = value;
1054 + if (value) {
1055 + unawaited(ensureTorStarted(context: context).then((_) async {
1056 + if (settingsStore.currentBuiltinTor == false) return; // return when tor got disabled in the meantime;
1057 + await wallet.connectToNode(node: appStore.settingsStore.getCurrentNode(wallet.type));
1058 + }));
1059 + } else {
1060 + unawaited(ensureTorStopped(context: context).then((_) async {
1061 + if (settingsStore.currentBuiltinTor == true) return; // return when tor got enabled in the meantime;
1062 + await wallet.connectToNode(node: appStore.settingsStore.getCurrentNode(wallet.type));
1063 + }));
1064 + }
1065 + }
1066 +
1067 @action
1068 void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
1069
@@ -1073,7 +1110,17 @@ abstract class DashboardViewModelBase with Store {
1110 }
1111 }
1112
1113 + static ServicesResponse? cachedServicesResponse;
1114 +
1115 Future<ServicesResponse> getServicesStatus() async {
1116 + if (cachedServicesResponse != null) {
1117 + return cachedServicesResponse!;
1118 + }
1119 + cachedServicesResponse = await _getServicesStatus();
1120 + return cachedServicesResponse!;
1121 + }
1122 +
1123 + Future<ServicesResponse> _getServicesStatus() async {
1124 try {
1125 if (isEnabledBulletinAction) {
1126 final uri = Uri.https(
@@ -1082,8 +1129,7 @@ abstract class DashboardViewModelBase with Store {
1129 {'key': secrets.fiatApiKey},
1130 );
1131
1085 - final res = await http.get(uri);
1086 -
1132 + final res = await ProxyWrapper().get(clearnetUri: uri);
1133 if (res.statusCode < 200 || res.statusCode >= 300) {
1134 throw res.body;
1135 }
lib/view_model/dashboard/home_settings_view_model.dart
+8 -7
@@ -12,6 +12,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
12 import 'package:cake_wallet/solana/solana.dart';
13 import 'package:cake_wallet/store/settings_store.dart';
14 import 'package:cake_wallet/tron/tron.dart';
15 +import 'package:cw_core/utils/proxy_wrapper.dart';
16 import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
17 import 'package:cake_wallet/zano/zano.dart';
18 import 'package:cw_core/crypto_currency.dart';
@@ -19,7 +20,6 @@ import 'package:cw_core/erc20_token.dart';
20 import 'package:cw_core/utils/print_verbose.dart';
21 import 'package:cw_core/wallet_type.dart';
22 import 'package:mobx/mobx.dart';
22 -import 'package:http/http.dart' as http;
23 import 'package:cake_wallet/.secrets.g.dart' as secrets;
24
25 part 'home_settings_view_model.g.dart';
@@ -240,14 +240,14 @@ abstract class HomeSettingsViewModelBase with Store {
240 );
241
242 try {
243 - final response = await http.get(
244 - uri,
243 + final response = await ProxyWrapper().get(
244 + clearnetUri: uri,
245 headers: {
246 "Accept": "application/json",
247 "X-API-Key": secrets.moralisApiKey,
248 },
249 );
250 -
250 +
251 final decodedResponse = jsonDecode(response.body);
252
253 final tokenInfo = Erc20TokenInfoMoralis.fromJson(decodedResponse[0] as Map<String, dynamic>);
@@ -309,8 +309,8 @@ abstract class HomeSettingsViewModelBase with Store {
309 );
310
311 try {
312 - final response = await http.get(uri);
313 -
312 + final response = await ProxyWrapper().get(clearnetUri: uri);
313 +
314 final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
315
316 if (decodedResponse['status'] != '1') {
@@ -351,7 +351,8 @@ abstract class HomeSettingsViewModelBase with Store {
351 );
352
353 try {
354 - final response = await http.get(uri);
354 + final response = await ProxyWrapper().get(clearnetUri: uri);
355 +
356
357 final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
358
lib/view_model/dashboard/nft_view_model.dart
+11 -11
@@ -7,7 +7,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
7 import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
8 import 'package:cake_wallet/src/screens/wallet_connect/widgets/bottom_sheet/bottom_sheet_message_display_widget.dart';
9 import 'package:cw_core/wallet_type.dart';
10 -import 'package:http/http.dart' as http;
10 +import 'package:cw_core/utils/proxy_wrapper.dart';
11 import 'package:mobx/mobx.dart';
12 import 'package:cake_wallet/.secrets.g.dart' as secrets;
13
@@ -80,15 +80,16 @@ abstract class NFTViewModelBase with Store {
80
81 isLoading = true;
82
83 - final response = await http.get(
84 - uri,
83 + final response = await ProxyWrapper().get(
84 + clearnetUri: uri,
85 headers: {
86 "Accept": "application/json",
87 "X-API-Key": secrets.moralisApiKey,
88 },
89 );
90 +
91
91 - final decodedResponse = jsonDecode(response.body);
92 + final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
93
94 if (walletType == WalletType.solana) {
95 final results = await Future.wait(
@@ -131,14 +132,14 @@ abstract class NFTViewModelBase with Store {
132 '/nft/$chainName/$address/metadata',
133 );
134
134 - final response = await http.get(
135 - uri,
135 + final response = await ProxyWrapper().get(
136 + clearnetUri: uri,
137 headers: {
138 "Accept": "application/json",
139 "X-API-Key": secrets.moralisApiKey,
140 },
141 );
141 -
142 +
143 final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
144
145 return SolanaNFTAssetModel.fromJson(decodedResponse);
@@ -171,15 +172,14 @@ abstract class NFTViewModelBase with Store {
172 "normalizeMetadata": "true",
173 },
174 );
174 -
175 - final response = await http.get(
176 - uri,
175 + final response = await ProxyWrapper().get(
176 + clearnetUri: uri,
177 headers: {
178 "Accept": "application/json",
179 "X-API-Key": secrets.moralisApiKey,
180 },
181 );
182 -
182 +
183 final decodedResponse = jsonDecode(response.body) as Map<String, dynamic>;
184
185 final nftAsset = NFTAssetModel.fromJson(decodedResponse);
lib/view_model/dev/network_requests_view_model.dart new
+16
@@ -0,0 +1,16 @@
1 +import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
2 +import 'package:mobx/mobx.dart';
3 +
4 +part 'network_requests_view_model.g.dart';
5 +
6 +class NetworkRequestsViewModel = NetworkRequestsViewModelBase with _$NetworkRequestsViewModel;
7 +
8 +abstract class NetworkRequestsViewModelBase with Store {
9 + @observable
10 + List<MemoryProxyLoggerEntry> logs = MemoryProxyLogger.logs;
11 +
12 + @action
13 + Future<void> loadLogs() async {
14 + logs = MemoryProxyLogger.logs;
15 + }
16 +}
\ No newline at end of file
lib/view_model/dev/send_network_requests_view_model.dart new
+16
@@ -0,0 +1,16 @@
1 +import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
2 +import 'package:mobx/mobx.dart';
3 +
4 +part 'send_network_requests_view_model.g.dart';
5 +
6 +class SendNetworkRequestsViewModel = SendNetworkRequestsViewModelBase with _$SendNetworkRequestsViewModel;
7 +
8 +abstract class SendNetworkRequestsViewModelBase with Store {
9 + @observable
10 + List<MemoryProxyLoggerEntry> logs = MemoryProxyLogger.logs;
11 +
12 + @action
13 + Future<void> loadLogs() async {
14 + logs = MemoryProxyLogger.logs;
15 + }
16 +}
\ No newline at end of file
lib/view_model/exchange/exchange_view_model.dart
+4 -5
@@ -7,6 +7,7 @@ import 'package:cake_wallet/core/create_trade_result.dart';
7 import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
8 import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
9 import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
10 +import 'package:cw_core/utils/proxy_wrapper.dart';
11 import 'package:cake_wallet/view_model/send/fees_view_model.dart';
12 import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
13 import 'package:cw_core/crypto_currency.dart';
@@ -16,7 +17,6 @@ import 'package:cw_core/unspent_coin_type.dart';
17 import 'package:cw_core/utils/print_verbose.dart';
18 import 'package:cw_core/wallet_type.dart';
19 import 'package:hive/hive.dart';
19 -import 'package:http/http.dart' as http;
20 import 'package:intl/intl.dart';
21 import 'package:mobx/mobx.dart';
22 import 'package:shared_preferences/shared_preferences.dart';
@@ -939,8 +939,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
939 }
940
941 Future<bool> _isContractAddress(String chainName, String contractAddress) async {
942 - final httpClient = http.Client();
943 -
942 final uri = Uri.https(
943 'deep-index.moralis.io',
944 '/api/v2.2/erc20/metadata',
@@ -951,13 +949,14 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
949 );
950
951 try {
954 - final response = await httpClient.get(
955 - uri,
952 + final response = await ProxyWrapper().get(
953 + clearnetUri: uri,
954 headers: {
955 "Accept": "application/json",
956 "X-API-Key": secrets.moralisApiKey,
957 },
958 );
959 +
960
961 final decodedResponse = jsonDecode(response.body)[0] as Map<String, dynamic>;
962
lib/view_model/node_list/node_create_or_edit_view_model.dart
+4
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/core/execution_state.dart';
2 import 'package:cake_wallet/entities/qr_scanner.dart';
3 import 'package:cake_wallet/store/settings_store.dart';
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5 import 'package:flutter/cupertino.dart';
6 import 'package:hive/hive.dart';
7 import 'package:mobx/mobx.dart';
@@ -58,6 +59,9 @@ abstract class NodeCreateOrEditViewModelBase with Store {
59 @observable
60 bool useSocksProxy;
61
62 + @computed
63 + bool get usesEmbeddedProxy => CakeTor.instance.started;
64 +
65 @observable
66 String socksProxyAddress;
67
lib/view_model/node_list/node_list_view_model.dart
+2 -1
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/store/app_store.dart';
3 import 'package:cake_wallet/utils/mobx.dart';
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5 import 'package:hive/hive.dart';
6 import 'package:mobx/mobx.dart';
7 import 'package:cw_core/wallet_base.dart';
@@ -38,7 +39,7 @@ abstract class NodeListViewModelBase with Store {
39
40 String getAlertContent(String uri) =>
41 S.current.change_current_node(uri) +
41 - '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}';
42 + '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + (CakeTor.instance.enabled ? '' : S.current.orbot_running_alert) : ''}';
43
44 final ObservableList<Node> nodes;
45 final SettingsStore settingsStore;
lib/view_model/node_list/pow_node_list_view_model.dart
+2 -1
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/store/app_store.dart';
3 import 'package:cake_wallet/utils/mobx.dart';
4 +import 'package:cw_core/utils/proxy_wrapper.dart';
5 import 'package:hive/hive.dart';
6 import 'package:mobx/mobx.dart';
7 import 'package:cw_core/wallet_base.dart';
@@ -38,7 +39,7 @@ abstract class PowNodeListViewModelBase with Store {
39
40 String getAlertContent(String uri) =>
41 S.current.change_current_node(uri) +
41 - '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + S.current.orbot_running_alert : ''}';
42 + '${uri.endsWith('.onion') || uri.contains('.onion:') ? '\n' + (CakeTor.instance.enabled ? '' : S.current.orbot_running_alert) : ''}';
43
44 final ObservableList<Node> nodes;
45 final SettingsStore settingsStore;
lib/view_model/start_tor_view_model.dart new
+95
@@ -0,0 +1,95 @@
1 +import 'dart:async';
2 +
3 +import 'package:cake_wallet/di.dart';
4 +import 'package:cake_wallet/main.dart';
5 +import 'package:cake_wallet/reactions/bootstrap.dart';
6 +import 'package:cake_wallet/routes.dart';
7 +import 'package:cake_wallet/store/app_store.dart';
8 +import 'package:cake_wallet/store/settings_store.dart';
9 +import 'package:cake_wallet/utils/tor.dart';
10 +import 'package:cw_core/utils/proxy_wrapper.dart';
11 +import 'package:flutter/material.dart';
12 +import 'package:mobx/mobx.dart';
13 +
14 +part 'start_tor_view_model.g.dart';
15 +
16 +class StartTorViewModel = StartTorViewModelBase with _$StartTorViewModel;
17 +
18 +abstract class StartTorViewModelBase with Store {
19 + StartTorViewModelBase() {
20 + _startTimer();
21 + }
22 +
23 + Timer? _timer;
24 + final int waitTimeInSeconds = 15;
25 +
26 + @observable
27 + bool isLoading = true;
28 +
29 + @observable
30 + bool timeoutReached = false;
31 +
32 + @observable
33 + int remainingSeconds = 15;
34 +
35 + @computed
36 + bool get showOptions => timeoutReached;
37 +
38 + @action
39 + void _startTimer() {
40 + remainingSeconds = waitTimeInSeconds;
41 + _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
42 + remainingSeconds -= 1;
43 +
44 + if (remainingSeconds <= 0) {
45 + timer.cancel();
46 + timeoutReached = true;
47 + }
48 + });
49 + }
50 +
51 + @observable
52 + bool didStartTor = false;
53 +
54 + @action
55 + Future<void> startTor(BuildContext context) async {
56 + if (didStartTor) {
57 + return;
58 + }
59 + await ensureTorStarted(context: null);
60 + while (true) {
61 + await Future.delayed(Duration(milliseconds: 250));
62 + if (CakeTor.instance.port != -1 && CakeTor.instance.started) {
63 + break;
64 + }
65 + }
66 + didStartTor = true;
67 + final appStore = getIt.get<AppStore>();
68 + bootstrapOnline(navigatorKey, loadWallet: true);
69 + appStore.wallet?.connectToNode(node: appStore.settingsStore.getCurrentNode(appStore.wallet!.type));
70 + Navigator.pushReplacementNamed(context, Routes.login);
71 + }
72 +
73 + @action
74 + void disableTor(BuildContext context) {
75 + final settingsStore = getIt.get<SettingsStore>();
76 + settingsStore.currentBuiltinTor = false;
77 + bootstrapOnline(navigatorKey, loadWallet: true);
78 + final appStore = getIt.get<AppStore>();
79 + appStore.wallet?.connectToNode(node: appStore.settingsStore.getCurrentNode(appStore.wallet!.type));
80 + Navigator.pushReplacementNamed(context, Routes.login);
81 + }
82 +
83 + @action
84 + void ignoreAndLaunchApp(BuildContext context) {
85 + bootstrapOnline(navigatorKey, loadWallet: true);
86 + final appStore = getIt.get<AppStore>();
87 + appStore.wallet?.connectToNode(node: appStore.settingsStore.getCurrentNode(appStore.wallet!.type));
88 + Navigator.pushReplacementNamed(context, Routes.login);
89 + }
90 +
91 + void dispose() {
92 + _timer?.cancel();
93 + _timer = null;
94 + }
95 +}
\ No newline at end of file
pubspec_base.yaml
+13 -1
@@ -100,13 +100,17 @@ dependencies:
100 # git:
101 # url: https://github.com/cake-tech/tor.git
102 # ref: main
103 - socks5_proxy: ^1.0.4
103 + socks5_proxy:
104 + git:
105 + url: https://github.com/LacticWhale/socks_dart
106 + ref: 27ad7c2efae8d7460325c74b90f660085cbd0685
107 flutter_svg: ^2.0.9
108 polyseed: ^0.0.7
109 nostr_tools:
110 git:
111 url: https://github.com/MrCyjaneK/nostr_tools.git
112 ref: 089d5a2dd751429a040ba10fb24fcbae564053e5
113 + solana: ^0.31.0+1
114 ledger_flutter_plus:
115 git:
116 url: https://github.com/vespr-wallet/ledger-flutter-plus
@@ -166,6 +170,14 @@ dependency_overrides:
170 git:
171 url: https://github.com/vespr-wallet/ledger-flutter-plus
172 ref: 60817d4b20144f9da9029f5034790272795b9d38
173 + socks5_proxy:
174 + git:
175 + url: https://github.com/LacticWhale/socks_dart.git
176 + ref: 27ad7c2efae8d7460325c74b90f660085cbd0685
177 + tor_binary:
178 + git:
179 + url: https://github.com/MrCyjaneK/flutter-tor_binary
180 + ref: cb811c610871a9517d47134b87c2f590c15c96c5
181 web_socket_channel: ^3.0.2
182 freezed_annotation: 2.4.4
183
res/values/strings_ar.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "من خلال إيقاف تشغيل هذا ، قد تكون معدلات الرسوم غير دقيقة في بعض الحالات ، لذلك قد ينتهي بك الأمر إلى دفع مبالغ زائدة أو دفع رسوم المعاملات الخاصة بك",
270 "disable_fiat": "تعطيل fiat",
271 "disable_sell": "قم بتعطيل إجراء البيع",
272 + "disable_tor": "تعطيل تور",
273 "disable_trade_option": "تعطيل خيار التجارة",
274 "disableBatteryOptimization": "تعطيل تحسين البطارية",
275 "disableBatteryOptimizationDescription": "هل تريد تعطيل تحسين البطارية من أجل جعل الخلفية مزامنة تعمل بحرية وسلاسة؟",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "نقوم بإنشاء عناوين جديدة في كل مرة تستخدم فيها عنوانًا ، لكن العناوين السابقة تستمر في العمل",
296 "email_address": "عنوان البريد الالكترونى",
297 "enable": "يُمكَِن",
298 + "enable_builtin_tor": "تمكين بنيت في تور",
299 "enable_mempool_api": "MEMPOOL API للحصول على رسوم وتواريخ دقيقة",
300 "enable_replace_by_fee": "تمكين الاستبدال",
301 "enable_silent_payments_scanning": "ابدأ في مسح المدفوعات الصامتة ، حتى يتم الوصول إلى الطرف",
@@ -334,6 +336,7 @@
336 "error_while_processing": "حدث خطأ أثناء التنقل",
337 "errorGettingCredentials": "ﺩﺎﻤﺘﻋﻻﺍ ﺕﺎﻧﺎﻴﺑ ﻰﻠﻋ ﻝﻮﺼﺤﻟﺍ ءﺎﻨﺛﺃ ﺄﻄﺧ ﺙﺪﺣ :ﻞﺸﻓ",
338 "errorSigningTransaction": "ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ ءﺎﻨﺛﺃ ﺄﻄﺧ ﺙﺪﺣ",
339 + "establishing_tor_connection": "ارتياح توصيل تور",
340 "estimated": "مُقدَّر",
341 "estimated_new_fee": "رسوم جديدة مقدرة",
342 "estimated_receive_amount": "مقدرة المبلغ الاستقبال",
@@ -860,6 +863,7 @@
863 "sort_by": "ترتيب حسب",
864 "spend_key_private": "مفتاح الإنفاق (خاص)",
865 "spend_key_public": "مفتاح الإنفاق (عام)",
866 + "starting_tor_proxy": "بدء الوكيل Tor",
867 "status": "الحالة:",
868 "step": "خطوة",
869 "string_default": "تقصير",
@@ -916,6 +920,8 @@
920 "tokenID": "ﻒﻳﺮﻌﺗ ﺔﻗﺎﻄﺑ",
921 "ton_extra_info": "يرجى عدم نسيان تحديد معرف المذكرة أثناء إرسال معاملة TON للتبادل",
922 "tor_connection": "ﺭﻮﺗ ﻝﺎﺼﺗﺍ",
923 + "tor_connection_timeout": "Tor Torn Connection Timeout",
924 + "tor_experimental": "هذه ميزة تجريبية ، وليس جميع الاتصالات المصنوعة من توجيه Cake Wallet Support - قد تستخدم بعض الاتصالات ClearNet ، من أجل توجيه حركة المرور بشكل كامل باستخدام TOR مع وضع VPN.",
925 "tor_only": "Tor فقط",
926 "total": "المجموع",
927 "total_saving": "إجمالي المدخرات",
res/values/strings_bg.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Като изключите това, таксите могат да бъдат неточни в някои случаи, така че може да се препланите или да не плащате таксите за вашите транзакции",
270 "disable_fiat": "Деактивиране на fiat",
271 "disable_sell": "Деактивирайте действието за продажба",
272 + "disable_tor": "Деактивирайте Tor",
273 "disable_trade_option": "Деактивирайте опцията за търговия",
274 "disableBatteryOptimization": "Деактивирайте оптимизацията на батерията",
275 "disableBatteryOptimizationDescription": "Искате ли да деактивирате оптимизацията на батерията, за да направите синхронизирането на фона да работи по -свободно и гладко?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Нови адреси се генерират всеки път, когато използвате този, но и предишните продължават да работят",
296 "email_address": "Имейл адрес",
297 "enable": "Активиране",
298 + "enable_builtin_tor": "Активирайте вграден Tor",
299 "enable_mempool_api": "Mempool API за точни такси и дати",
300 "enable_replace_by_fee": "Активиране на замяна по забрана",
301 "enable_silent_payments_scanning": "Започнете да сканирате безшумните плащания, докато се достигне съветът",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Възникна грешка при приемане",
337 "errorGettingCredentials": "Неуспешно: Грешка при получаване на идентификационни данни",
338 "errorSigningTransaction": "Възникна грешка при подписване на транзакция",
339 + "establishing_tor_connection": "Естибилиране на TOR връзка",
340 "estimated": "Изчислено",
341 "estimated_new_fee": "Прогнозна нова такса",
342 "estimated_receive_amount": "Прогнозна сума за получаване",
@@ -860,6 +863,7 @@
863 "sort_by": "Сортирай по",
864 "spend_key_private": "Spend key (таен)",
865 "spend_key_public": "Spend key (публичен)",
866 + "starting_tor_proxy": "Стартиране на прокси",
867 "status": "Статус: ",
868 "step": "Стъпка",
869 "string_default": "По подразбиране",
@@ -916,6 +920,8 @@
920 "tokenID": "документ за самоличност",
921 "ton_extra_info": "Моля, не забравяйте да посочите идентификационния номер на бележката, докато изпращате транзакцията TON за борсата",
922 "tor_connection": "Tor връзка",
923 + "tor_connection_timeout": "TOR ВРЕМЕ НА ВРЕМЕТО ВРЕМЕ",
924 + "tor_experimental": "Това е експериментална функция, не всички връзки, направени от маршрутизиране на тортата за портфейл за торта - някои връзки могат да използват ClearNet, за да се насочи напълно да използва трафик на приложения TOR с VPN режим.",
925 "tor_only": "Само чрез Tor",
926 "total": "Обща сума",
927 "total_saving": "Общо спестявания",
res/values/strings_cs.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Tímto vypnutím by sazby poplatků mohly být v některých případech nepřesné, takže byste mohli skončit přepláváním nebo nedoplatkem poplatků za vaše transakce",
270 "disable_fiat": "Zakázat fiat",
271 "disable_sell": "Zakázat akci prodeje",
272 + "disable_tor": "Zakázat tor",
273 "disable_trade_option": "Zakázat možnost TRADE",
274 "disableBatteryOptimization": "Zakázat optimalizaci baterie",
275 "disableBatteryOptimizationDescription": "Chcete deaktivovat optimalizaci baterie, aby se synchronizovala pozadí volně a hladce?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Po každém použití je generována nová adresa, ale předchozí adresy také stále fungují",
296 "email_address": "E-mailová adresa",
297 "enable": "Umožnit",
298 + "enable_builtin_tor": "Povolte vestavěné tor",
299 "enable_mempool_api": "Mempool API pro přesné poplatky a data",
300 "enable_replace_by_fee": "Povolit výměnu podle poplatku",
301 "enable_silent_payments_scanning": "Začněte skenovat tiché platby, dokud není dosaženo špičky",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Při převádění došlo k chybě",
337 "errorGettingCredentials": "Selhalo: Chyba při získávání přihlašovacích údajů",
338 "errorSigningTransaction": "Při podepisování transakce došlo k chybě",
339 + "establishing_tor_connection": "Estabilizující připojení Tor",
340 "estimated": "Odhadováno",
341 "estimated_new_fee": "Odhadovaný nový poplatek",
342 "estimated_receive_amount": "Odhadovaná částka přijímání",
@@ -860,6 +863,7 @@
863 "sort_by": "Seřazeno podle",
864 "spend_key_private": "Klíč pro platby (soukromý)",
865 "spend_key_public": "Klíč pro platby (veřejný)",
866 + "starting_tor_proxy": "Zahájení proxy Tor",
867 "status": "Status: ",
868 "step": "Krok",
869 "string_default": "Výchozí",
@@ -916,6 +920,8 @@
920 "tokenID": "ID",
921 "ton_extra_info": "Při odeslání TON transakce pro výměnu nezapomeňte zadat ID Memo ID",
922 "tor_connection": "Připojení Tor",
923 + "tor_connection_timeout": "Časový limit připojení",
924 + "tor_experimental": "Jedná se o experimentální funkci, ne všechna připojení vyrobená z podpory dortů na dort Tor - některá připojení mohou používat ClearNet, aby se plně směrovaly používání provozu aplikací s režimem VPN.",
925 "tor_only": "Pouze Tor",
926 "total": "Celkový",
927 "total_saving": "Celkem ušetřeno",
res/values/strings_de.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Wenn dies ausgeschaltet wird, sind die Gebührenquoten in einigen Fällen möglicherweise ungenau, sodass Sie die Gebühren für Ihre Transaktionen möglicherweise überbezahlt oder unterzahlt",
270 "disable_fiat": "Fiat deaktivieren",
271 "disable_sell": "Verkaufsaktion deaktivieren",
272 + "disable_tor": "Tor deaktivieren",
273 "disable_trade_option": "Handelsoption deaktivieren",
274 "disableBatteryOptimization": "Batterieoptimierung deaktivieren",
275 "disableBatteryOptimizationDescription": "Möchten Sie die Batterieoptimierung deaktivieren, um die Hintergrundsynchronisierung reibungsloser zu gestalten?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Wir generieren jedes Mal neue Adressen, wenn Sie eine verwenden, aber vorherige Adressen funktionieren weiterhin",
296 "email_address": "E-Mail-Adresse",
297 "enable": "Aktivieren",
298 + "enable_builtin_tor": "Aktivieren Sie den bau-in tor",
299 "enable_mempool_api": "Mempool-API für genaue Gebühren und Daten",
300 "enable_replace_by_fee": "Aktivieren Sie Ersatz für Fee",
301 "enable_silent_payments_scanning": "Scannen Sie nach Silent Payments ihrer Adresse",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Ein Fehler beim Proceessing trat ein Fehler auf",
337 "errorGettingCredentials": "Fehlgeschlagen: Fehler beim Abrufen der Anmeldeinformationen",
338 "errorSigningTransaction": "Beim Signieren der Transaktion ist ein Fehler aufgetreten",
339 + "establishing_tor_connection": "Einstellung der TOR -Verbindung",
340 "estimated": "Geschätzt",
341 "estimated_new_fee": "Geschätzte neue Gebühr",
342 "estimated_receive_amount": "Geschätzter Empfangsbetrag",
@@ -861,6 +864,7 @@
864 "sort_by": "Sortiere nach",
865 "spend_key_private": "Spend Key (geheim)",
866 "spend_key_public": "Spend Key (öffentlich)",
867 + "starting_tor_proxy": "TOR -Proxy beginnen",
868 "status": "Status: ",
869 "step": "Schritt",
870 "string_default": "Standard",
@@ -917,6 +921,8 @@
921 "tokenID": "AUSWEIS",
922 "ton_extra_info": "Bitte vergessen Sie nicht, die Memo -ID anzugeben, während Sie die TON -Transaktion für den Austausch senden",
923 "tor_connection": "Tor-Verbindung",
924 + "tor_connection_timeout": "TOR -Verbindungs ​​-Zeitüberschreitung",
925 + "tor_experimental": "Dies ist eine experimentelle Funktion, nicht alle Verbindungen, die aus Kuchen -Wallet -Stütze für die Routing hergestellt werden. Einige Verbindungen können ClearNET verwenden, um den App -Traffic -App -TOR mit dem VPN -Modus vollständig zu verwenden.",
926 "tor_only": "Nur Tor",
927 "total": "Gesamt",
928 "total_saving": "Gesamteinsparungen",
res/values/strings_en.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "By turning this off, the fee rates might be inaccurate in some cases, so you might end up overpaying or underpaying the fees for your transactions",
270 "disable_fiat": "Disable fiat",
271 "disable_sell": "Disable sell action",
272 + "disable_tor": "Disable Tor",
273 "disable_trade_option": "Disable trade option",
274 "disableBatteryOptimization": "Disable Battery Optimization",
275 "disableBatteryOptimizationDescription": "Do you want to disable battery optimization in order to make background sync run more freely and smoothly?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work",
296 "email_address": "Email Address",
297 "enable": "Enable",
298 + "enable_builtin_tor": "Enable built-in Tor",
299 "enable_mempool_api": "Mempool API for accurate fees and dates",
300 "enable_replace_by_fee": "Enable Replace-By-Fee",
301 "enable_silent_payments_scanning": "Start scanning for transactions sent to your Silent Payment address.",
@@ -334,6 +336,7 @@
336 "error_while_processing": "An error occurred while proceessing",
337 "errorGettingCredentials": "Failed: Error while getting credentials",
338 "errorSigningTransaction": "An error has occured while signing transaction",
339 + "establishing_tor_connection": "Estabilishing Tor connection",
340 "estimated": "Estimated",
341 "estimated_new_fee": "Estimated new fee",
342 "estimated_receive_amount": "Estimated receive amount",
@@ -861,6 +864,7 @@
864 "sort_by": "Sort by",
865 "spend_key_private": "Spend key (private)",
866 "spend_key_public": "Spend key (public)",
867 + "starting_tor_proxy": "Starting Tor proxy",
868 "status": "Status: ",
869 "step": "Step",
870 "string_default": "Default",
@@ -917,6 +921,8 @@
921 "tokenID": "ID",
922 "ton_extra_info": "Please don’t forget to specify the Memo ID while sending the TON transaction for the exchange",
923 "tor_connection": "Tor connection",
924 + "tor_connection_timeout": "Tor connection timeout",
925 + "tor_experimental": "This is an experimental feature, not all connections made from Cake Wallet support Tor routing - some connections may use clearnet, in order to fully route app traffic use Tor with VPN mode.",
926 "tor_only": "Tor only",
927 "total": "Total",
928 "total_saving": "Total Savings",
res/values/strings_es.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Al apagar esto, las tasas de tarifas pueden ser inexactas en algunos casos, por lo que puede terminar pagando en exceso o pagando menos las tarifas por sus transacciones",
270 "disable_fiat": "Deshabilitar fiat",
271 "disable_sell": "Desactivar acción de venta",
272 + "disable_tor": "Deshabilitar tor",
273 "disable_trade_option": "Deshabilitar la opción de comercio",
274 "disableBatteryOptimization": "Deshabilitar la optimización de la batería",
275 "disableBatteryOptimizationDescription": "¿Desea deshabilitar la optimización de la batería para que la sincronización de fondo se ejecute más libremente y sin problemas?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Generamos nuevas direcciones cada vez que usa una, pero las direcciones anteriores siguen funcionando",
296 "email_address": "Dirección de correo electrónico",
297 "enable": "Permitir",
298 + "enable_builtin_tor": "Habilitar buildi-in tor",
299 "enable_mempool_api": "API de Mempool para tarifas y fechas precisas",
300 "enable_replace_by_fee": "Habilitar reemplazar por tarea",
301 "enable_silent_payments_scanning": "Comienza a escanear pagos silenciosos, hasta que se alcance la altura actual",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Se produjo un error mientras procesaba",
337 "errorGettingCredentials": "Error: error al obtener las credenciales",
338 "errorSigningTransaction": "Se ha producido un error al firmar la transacción.",
339 + "establishing_tor_connection": "Estabilización de la conexión para",
340 "estimated": "Estimado",
341 "estimated_new_fee": "Nueva tarifa estimada",
342 "estimated_receive_amount": "Cantidad de recepción estimada",
@@ -861,6 +864,7 @@
864 "sort_by": "Ordenar por",
865 "spend_key_private": "Llave de gasto (privada)",
866 "spend_key_public": "Llave de gasto (pública)",
867 + "starting_tor_proxy": "Inicio Tor Proxy",
868 "status": "Estado: ",
869 "step": "Paso",
870 "string_default": "Por defecto",
@@ -917,6 +921,8 @@
921 "tokenID": "IDENTIFICACIÓN",
922 "ton_extra_info": "No olvide especificar el ID de memo mientras envía la transacción TON para el intercambio",
923 "tor_connection": "conexión tor",
924 + "tor_connection_timeout": "Tiempo de espera de conexión de Tor",
925 + "tor_experimental": "Esta es una característica experimental, no todas las conexiones hechas con el enrutamiento de soporte para la billetera de pastel: algunas conexiones pueden usar ClearNet, para enrutar completamente el tráfico de aplicaciones Uso Tor con el modo VPN.",
926 "tor_only": "solo Tor",
927 "total": "Total",
928 "total_saving": "Ahorro Total",
res/values/strings_fr.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "En désactivant cela, les taux de frais peuvent être inexacts dans certains cas, vous pourriez donc finir par payer trop ou sous-paiement les frais pour vos transactions",
270 "disable_fiat": "Désactiver les montants en fiat",
271 "disable_sell": "Désactiver l'action de vente",
272 + "disable_tor": "Désactiver Tor",
273 "disable_trade_option": "Désactiver l'option de commerce",
274 "disableBatteryOptimization": "Désactiver l'optimisation de la batterie",
275 "disableBatteryOptimizationDescription": "Voulez-vous désactiver l'optimisation de la batterie afin de faire fonctionner la synchronisation d'arrière-plan plus librement et en douceur?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Nous générons de nouvelles adresses à chaque fois que vous en utilisez une, mais les adresses précédentes continuent à fonctionner",
296 "email_address": "Adresse e-mail",
297 "enable": "Activer",
298 + "enable_builtin_tor": "Activer le Tor construit",
299 "enable_mempool_api": "API Mempool pour les frais et dates précis",
300 "enable_replace_by_fee": "Activer Remplace-by-Fee",
301 "enable_silent_payments_scanning": "Commencez à analyser les transactions envoyées à votre adresse de paiement silencieux.",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Une erreur s'est produite lors de la procédure",
337 "errorGettingCredentials": "Échec : erreur lors de l'obtention des informations d'identification",
338 "errorSigningTransaction": "Une erreur s'est produite lors de la signature de la transaction",
339 + "establishing_tor_connection": "Établir la connexion TOR",
340 "estimated": "Estimé",
341 "estimated_new_fee": "De nouveaux frais estimés",
342 "estimated_receive_amount": "Recevoir estimé le montant",
@@ -860,6 +863,7 @@
863 "sort_by": "Trier par",
864 "spend_key_private": "Clef de dépense (spend key) (privée)",
865 "spend_key_public": "Clef de dépense (spend key) (publique)",
866 + "starting_tor_proxy": "Démarrer le proxy Tor",
867 "status": "Statut: ",
868 "step": "Étape",
869 "string_default": "Défaut",
@@ -916,6 +920,8 @@
920 "tokenID": "IDENTIFIANT",
921 "ton_extra_info": "N'oubliez pas de spécifier l'identification de la note lors de l'envoi de la transaction TON pour l'échange",
922 "tor_connection": "Connexion Tor",
923 + "tor_connection_timeout": "Tor Connection Timeout",
924 + "tor_experimental": "Il s'agit d'une caractéristique expérimentale, toutes les connexions faites à partir du portefeuille du portefeuille de gâteaux TOR ROUTING - Certaines connexions peuvent utiliser ClearNet, afin d'achever entièrement le trafic d'application Utiliser TOR avec le mode VPN.",
925 "tor_only": "Tor uniquement",
926 "total": "Total",
927 "total_saving": "Économies totales",
res/values/strings_ha.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Ta hanyar juya wannan kashe, kudaden da zai iya zama ba daidai ba a wasu halaye, saboda haka zaku iya ƙare da overpaying ko a ƙarƙashin kudaden don ma'amaloli",
270 "disable_fiat": "Dakatar da fiat",
271 "disable_sell": "Kashe karbuwa",
272 + "disable_tor": "Musaki tor",
273 "disable_trade_option": "Musaki zaɓi na kasuwanci",
274 "disableBatteryOptimization": "Kashe ingantawa baturi",
275 "disableBatteryOptimizationDescription": "Shin kana son kashe ingantawa baturi don yin setnc bankwali gudu da yar kyauta da kyau?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Muna samar da sababbin adireshi duk lokacin da kuka yi amfani da ɗaya, amma adiresoshin da suka gabata suna ci gaba da aiki",
296 "email_address": "Adireshin i-mel",
297 "enable": "Ba dama",
298 + "enable_builtin_tor": "Sanya riguna-inf",
299 "enable_mempool_api": "Mampool API don ingantattun kudade da kwanakin",
300 "enable_replace_by_fee": "Ba da damar maye gurbin-by-kudin",
301 "enable_silent_payments_scanning": "Fara bincika biya na shiru, har sai tip ɗin ya kai",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Kuskure ya faru yayin bincike",
337 "errorGettingCredentials": "Ba a yi nasara ba: Kuskure yayin samun takaddun shaida",
338 "errorSigningTransaction": "An sami kuskure yayin sanya hannu kan ciniki",
339 + "establishing_tor_connection": "Kafa Tor Haɗaɗɗa",
340 "estimated": "Kiyasta",
341 "estimated_new_fee": "An kiyasta sabon kudin",
342 "estimated_receive_amount": "Kiyasta samun adadin",
@@ -862,6 +865,7 @@
865 "sort_by": "Kasa",
866 "spend_key_private": "makullin biya (maɓallin kalmar sirri)",
867 "spend_key_public": "makullin biya (maɓallin jama'a)",
868 + "starting_tor_proxy": "Fara tor proxy",
869 "status": "Matsayi:",
870 "step": "Taka",
871 "string_default": "Ƙin cika alƙawari",
@@ -918,6 +922,8 @@
922 "tokenID": "ID",
923 "ton_extra_info": "Don Allah kar a manta su saka ID na Memo yayin aikawa da ma'amala don musayar",
924 "tor_connection": "Tor haɗin gwiwa",
925 + "tor_connection_timeout": "A lokacin Tor Haɗin",
926 + "tor_experimental": "Wannan fasalin gwaji ne, ba duk haɗin da aka yi ba daga Wall Wall Wall Walling goyon baya Tor Routing - don cikakken amfani da hanyar zirga-zirga amfani da tor tare da yanayin vpn.",
927 "tor_only": "Tor kawai",
928 "total": "Duka",
929 "total_saving": "Jimlar Adana",
res/values/strings_hi.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "इसे बंद करने से, कुछ मामलों में शुल्क दरें गलत हो सकती हैं, इसलिए आप अपने लेनदेन के लिए फीस को कम कर सकते हैं या कम कर सकते हैं",
270 "disable_fiat": "िएट को अक्षम करें",
271 "disable_sell": "बेचने की कार्रवाई अक्षम करें",
272 + "disable_tor": "टोर को अक्षम करें",
273 "disable_trade_option": "व्यापार विकल्प अक्षम करें",
274 "disableBatteryOptimization": "बैटरी अनुकूलन अक्षम करें",
275 "disableBatteryOptimizationDescription": "क्या आप बैकग्राउंड सिंक को अधिक स्वतंत्र और सुचारू रूप से चलाने के लिए बैटरी ऑप्टिमाइज़ेशन को अक्षम करना चाहते हैं?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "हर बार जब आप एक का उपयोग करते हैं तो हम नए पते उत्पन्न करते हैं, लेकिन पिछले पते काम करना जारी रखते हैं",
296 "email_address": "ईमेल पता",
297 "enable": "सक्षम",
298 + "enable_builtin_tor": "बिल्ट-इन टोर को सक्षम करें",
299 "enable_mempool_api": "सटीक शुल्क और तिथियों के लिए मेमपूल एपीआई",
300 "enable_replace_by_fee": "प्रतिस्थापित-दर-शुल्क सक्षम करें",
301 "enable_silent_payments_scanning": "मूक भुगतान स्कैनिंग सक्षम करें",
@@ -334,6 +336,7 @@
336 "error_while_processing": "प्रोकसिंग करते समय एक त्रुटि हुई",
337 "errorGettingCredentials": "विफल: क्रेडेंशियल प्राप्त करते समय त्रुटि",
338 "errorSigningTransaction": "लेन-देन पर हस्ताक्षर करते समय एक त्रुटि उत्पन्न हुई है",
339 + "establishing_tor_connection": "टोर -कनेक्शन",
340 "estimated": "अनुमानित",
341 "estimated_new_fee": "अनुमानित नया शुल्क",
342 "estimated_receive_amount": "अनुमानित राशि",
@@ -862,6 +865,7 @@
865 "sort_by": "इसके अनुसार क्रमबद्ध करें",
866 "spend_key_private": "खर्च करना (निजी)",
867 "spend_key_public": "खर्च करना (जनता)",
868 + "starting_tor_proxy": "टोर प्रॉक्सी शुरू करना",
869 "status": "स्थिति: ",
870 "step": "कदम",
871 "string_default": "गलती करना",
@@ -918,6 +922,8 @@
922 "tokenID": "पहचान",
923 "ton_extra_info": "कृपया एक्सचेंज के लिए टन लेनदेन भेजते समय मेमो आईडी निर्दिष्ट करना न भूलें",
924 "tor_connection": "टोर कनेक्शन",
925 + "tor_connection_timeout": "टोर कनेक्शन टाइमआउट",
926 + "tor_experimental": "यह एक प्रायोगिक विशेषता है, केक वॉलेट सपोर्ट टोर रूटिंग से किए गए सभी कनेक्शन नहीं - कुछ कनेक्शन क्लीयरनेट का उपयोग कर सकते हैं, ताकि वीपीएन मोड के साथ ऐप ट्रैफिक का उपयोग पूरी तरह से रूट करने के लिए हो।",
927 "tor_only": "Tor केवल",
928 "total": "कुल",
929 "total_saving": "कुल बचत",
res/values/strings_hr.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Isključivanjem ovoga, stope naknade u nekim bi slučajevima mogle biti netočne, tako da biste mogli preplatiti ili predati naknadu za vaše transakcije",
270 "disable_fiat": "Isključi, fiat",
271 "disable_sell": "Onemogući akciju prodaje",
272 + "disable_tor": "Onemogućiti tor",
273 "disable_trade_option": "Onemogući trgovinsku opciju",
274 "disableBatteryOptimization": "Onemogući optimizaciju baterije",
275 "disableBatteryOptimizationDescription": "Želite li onemogućiti optimizaciju baterije kako bi se pozadinska sinkronizacija radila slobodnije i glatko?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Minden egyes alkalommal új címeket generálunk, de a korábbi címek továbbra is működnek",
296 "email_address": "Adresa e-pošte",
297 "enable": "Omogućiti",
298 + "enable_builtin_tor": "Omogućite ugrađeni u tor",
299 "enable_mempool_api": "Mempool API za točne naknade i datume",
300 "enable_replace_by_fee": "Omogući zamjenu",
301 "enable_silent_payments_scanning": "Započnite skeniranje tihih plaćanja, dok se ne postigne savjet",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Došlo je do pogreške tijekom prožimanja",
337 "errorGettingCredentials": "Neuspješno: Pogreška prilikom dobivanja vjerodajnica",
338 "errorSigningTransaction": "Došlo je do pogreške prilikom potpisivanja transakcije",
339 + "establishing_tor_connection": "Uspostavljanje torbice",
340 "estimated": "procijenjen",
341 "estimated_new_fee": "Procijenjena nova naknada",
342 "estimated_receive_amount": "Procijenjeni iznos primanja",
@@ -860,6 +863,7 @@
863 "sort_by": "Poredaj po",
864 "spend_key_private": "Spend key (privatni)",
865 "spend_key_public": "Spend key (javni)",
866 + "starting_tor_proxy": "Početak Tor proxy",
867 "status": "Status: ",
868 "step": "Korak",
869 "string_default": "Zadano",
@@ -916,6 +920,8 @@
920 "tokenID": "iskaznica",
921 "ton_extra_info": "Ne zaboravite navesti ID memorandu",
922 "tor_connection": "Tor veza",
923 + "tor_connection_timeout": "TOR TIMPOUCEN",
924 + "tor_experimental": "Ovo je eksperimentalna značajka, nisu sve veze napravljene od podrške za kolače za torte - neke veze mogu koristiti ClearNet, kako bi se u potpunosti usmjerio na APP promet s TOR -om s VPN načinom rada.",
925 "tor_only": "Samo Tor",
926 "total": "Ukupno",
927 "total_saving": "Ukupna ušteda",
res/values/strings_hy.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Դրանից անջատելով, վճարների տեմպերը որոշ դեպքերում կարող են անճիշտ լինել, այնպես որ դուք կարող եք վերջ տալ ձեր գործարքների համար վճարների գերավճարների կամ գերավճարների վրա",
270 "disable_fiat": "Անջատել ֆիատ",
271 "disable_sell": "Անջատել վաճառք գործողությունը",
272 + "disable_tor": "Անջատեք Tor- ը",
273 "disable_trade_option": "Անջատեք առեւտրի տարբերակը",
274 "disableBatteryOptimization": "Անջատել մարտկոցի օպտիմիզացիան",
275 "disableBatteryOptimizationDescription": "Դուք ցանկանում եք անջատել մարտկոցի օպտիմիզացիան ֆոնային համաժամացման ավելի ազատ և հարթ ընթացքի համար?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Մենք ստեղծում ենք նոր հասցե ամեն անգամ, երբ դուք օգտագործում եք այն, բայց նախորդ հասցեները շարունակում են աշխատել",
296 "email_address": "Էլ. փոստի հասցե",
297 "enable": "Միացնել",
298 + "enable_builtin_tor": "Միացրեք ներկառուցված ջահը",
299 "enable_mempool_api": "Mempool API ճշգրիտ վճարների եւ ամսաթվերի համար",
300 "enable_replace_by_fee": "Միացնել փոխարինումը միջնորդավճարով",
301 "enable_silent_payments_scanning": "Միացնել Լուռ Վճարումների սկանավորումը",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Նախագծման ժամանակ սխալ է տեղի ունեցել",
337 "errorGettingCredentials": "Սխալ. ծանրաբեռնված վստահագրեր ստանալիս",
338 "errorSigningTransaction": "Սխալ է տեղի ունեցել գործարքը ստորագրելիս",
339 + "establishing_tor_connection": "Չափայնացնում է Tor կապը",
340 "estimated": "Գնահատված",
341 "estimated_new_fee": "Գնահատված նոր միջնորդավճար",
342 "estimated_receive_amount": "Գնահատված ստացված գումար",
@@ -858,6 +861,7 @@
861 "sort_by": "Դասավորել ըստ",
862 "spend_key_private": "Վճարման բանալի (գախտնի)",
863 "spend_key_public": "Վճարման բանալի (հանրային)",
864 + "starting_tor_proxy": "Սկսելով Tor վստահված անձը",
865 "status": "Կարգավիճակ՝ ",
866 "step": "Քայլ",
867 "string_default": "Լռելայն",
@@ -914,6 +918,8 @@
918 "tokenID": "ID",
919 "ton_extra_info": "Խնդրում ենք մի մոռացեք նշել MEMO ID- ն, երբ փոխանակման համար տոննա գործարքը ուղարկեք",
920 "tor_connection": "Tor կապ",
921 + "tor_connection_timeout": "Tor կապի ժամկետ",
922 + "tor_experimental": "Սա փորձարարական առանձնահատկություն է, ոչ բոլոր կապերը տորթ դրամապանակների աջակցության համար",
923 "tor_only": "Միայն Tor",
924 "total": "Ընդհանուր",
925 "total_saving": "Ընդհանուր խնայողություն",
res/values/strings_id.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Dengan mematikan ini, tarif biaya mungkin tidak akurat dalam beberapa kasus, jadi Anda mungkin akan membayar lebih atau membayar biaya untuk transaksi Anda",
270 "disable_fiat": "Nonaktifkan fiat",
271 "disable_sell": "Nonaktifkan aksi jual",
272 + "disable_tor": "Nonaktifkan tor",
273 "disable_trade_option": "Nonaktifkan opsi perdagangan",
274 "disableBatteryOptimization": "Nonaktifkan optimasi baterai",
275 "disableBatteryOptimizationDescription": "Apakah Anda ingin menonaktifkan optimasi baterai untuk membuat sinkronisasi latar belakang berjalan lebih bebas dan lancar?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Kami menghasilkan alamat baru setiap kali Anda menggunakan satu, tetapi alamat sebelumnya tetap berfungsi",
296 "email_address": "Alamat Email",
297 "enable": "Memungkinkan",
298 + "enable_builtin_tor": "Aktifkan built-in tor",
299 "enable_mempool_api": "API Mempool untuk biaya dan tanggal yang akurat",
300 "enable_replace_by_fee": "Aktifkan ganti-by-fee",
301 "enable_silent_payments_scanning": "Mulailah memindai pembayaran diam, sampai ujung tercapai",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Terjadi kesalahan saat prosedur",
337 "errorGettingCredentials": "Gagal: Terjadi kesalahan saat mendapatkan kredensial",
338 "errorSigningTransaction": "Terjadi kesalahan saat menandatangani transaksi",
339 + "establishing_tor_connection": "Estabilishing Tor Connection",
340 "estimated": "Diperkirakan",
341 "estimated_new_fee": "Perkiraan biaya baru",
342 "estimated_receive_amount": "Diperkirakan jumlah menerima",
@@ -863,6 +866,7 @@
866 "sort_by": "Sortir dengan",
867 "spend_key_private": "Kunci pengeluaran (privat)",
868 "spend_key_public": "Kunci pengeluaran (publik)",
869 + "starting_tor_proxy": "Mulai untuk proxy",
870 "status": "Status: ",
871 "step": "Melangkah",
872 "string_default": "Bawaan",
@@ -919,6 +923,8 @@
923 "tokenID": "PENGENAL",
924 "ton_extra_info": "Harap jangan lupa untuk menentukan ID memo saat mengirim transaksi ton untuk pertukaran",
925 "tor_connection": "koneksi Tor",
926 + "tor_connection_timeout": "Tor Connection Timeout",
927 + "tor_experimental": "Ini adalah fitur eksperimental, tidak semua koneksi yang terbuat dari dukungan dompet kue untuk routing - beberapa koneksi dapat menggunakan clearnet, untuk sepenuhnya merutekan aplikasi lalu lintas menggunakan untuk mode VPN.",
928 "tor_only": "Hanya Tor",
929 "total": "Total",
930 "total_saving": "Total Pembayaran",
res/values/strings_it.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Disattivando quest'opzione, i tassi delle commissioni potrebbero essere inaccurati in alcuni casi, quindi potresti finire per pagare troppo o troppo poco le commissioni per le tue transazioni",
270 "disable_fiat": "Disabilita fiat",
271 "disable_sell": "Disabilita l'azione di vendita",
272 + "disable_tor": "Disabilita Tor",
273 "disable_trade_option": "Disabilita l'opzione di scambio",
274 "disableBatteryOptimization": "Disabilita l'ottimizzazione della batteria",
275 "disableBatteryOptimizationDescription": "Vuoi disabilitare l'ottimizzazione della batteria per migliorare la sincronizzazione in background?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Generiamo nuovi indirizzi ogni volta che ne utilizzi uno, ma gli indirizzi precedenti continuano a funzionare",
296 "email_address": "Indirizzo e-mail",
297 "enable": "Abilita",
298 + "enable_builtin_tor": "Abilita buildi-in tor",
299 "enable_mempool_api": "API Mempool per commissioni e date accurate",
300 "enable_replace_by_fee": "Abilita Replace-By-Fee",
301 "enable_silent_payments_scanning": "Inizia a scansionare le transazioni inviate al tuo indirizzo Silent Payments",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Si è verificato un errore durante la promozione",
337 "errorGettingCredentials": "Non riuscito: errore durante il recupero delle credenziali",
338 "errorSigningTransaction": "Si è verificato un errore durante la firma della transazione",
339 + "establishing_tor_connection": "Connessione TOR di esame",
340 "estimated": "Stimato",
341 "estimated_new_fee": "Nuova commissione stimata",
342 "estimated_receive_amount": "Importo di ricezione stimato",
@@ -861,6 +864,7 @@
864 "sort_by": "Ordina per",
865 "spend_key_private": "Chiave di spesa (privata)",
866 "spend_key_public": "Chiave di spesa (pubblica)",
867 + "starting_tor_proxy": "Avvio del proxy di Tor",
868 "status": "Stato: ",
869 "step": "Fare un passo",
870 "string_default": "Predefinito",
@@ -917,6 +921,8 @@
921 "tokenID": "ID",
922 "ton_extra_info": "Non dimenticare di specificare l'ID memo durante l'invio della transazione Ton per lo scambio",
923 "tor_connection": "Connessione Tor",
924 + "tor_connection_timeout": "Timeout di connessione TOR",
925 + "tor_experimental": "Questa è una funzionalità sperimentale, non tutte le connessioni realizzate con il supporto per il portafoglio di torta per routing - alcune connessioni possono utilizzare ClearNet, al fine di instradare completamente il traffico di app per l'uso del traffico con la modalità VPN.",
926 "tor_only": "Solo Tor",
927 "total": "Totale",
928 "total_saving": "Risparmio totale",
res/values/strings_ja.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "これをオフにすることで、料金金利は場合によっては不正確になる可能性があるため、取引の費用が過払いまたは不足している可能性があります",
270 "disable_fiat": "フィアットを無効にする",
271 "disable_sell": "販売アクションを無効にする",
272 + "disable_tor": "torを無効にします",
273 "disable_trade_option": "取引オプションを無効にします",
274 "disableBatteryOptimization": "バッテリーの最適化を無効にします",
275 "disableBatteryOptimizationDescription": "バックグラウンドシンクをより自由かつスムーズに実行するために、バッテリーの最適化を無効にしたいですか?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "使用するたびに新しいアドレスが生成されますが、以前のアドレスは引き続き機能します",
296 "email_address": "メールアドレス",
297 "enable": "有効にする",
298 + "enable_builtin_tor": "組み込みのTORを有効にします",
299 "enable_mempool_api": "正確な料金と日付のMempool API",
300 "enable_replace_by_fee": "交換ごとに有効にします",
301 "enable_silent_payments_scanning": "先端に達するまで、サイレント決済のスキャンを開始します",
@@ -334,6 +336,7 @@
336 "error_while_processing": "処理中にエラーが発生しました",
337 "errorGettingCredentials": "失敗: 認証情報の取得中にエラーが発生しました",
338 "errorSigningTransaction": "トランザクションの署名中にエラーが発生しました",
339 + "establishing_tor_connection": "TOR接続を確立します",
340 "estimated": "推定",
341 "estimated_new_fee": "推定新しい料金",
342 "estimated_receive_amount": "推定受信金額",
@@ -861,6 +864,7 @@
864 "sort_by": "並び替え",
865 "spend_key_private": "キーを使う (プライベート)",
866 "spend_key_public": "キーを使う (パブリック)",
867 + "starting_tor_proxy": "プロキシを開始します",
868 "status": "状態: ",
869 "step": "ステップ",
870 "string_default": "デフォルト",
@@ -917,6 +921,8 @@
921 "tokenID": "ID",
922 "ton_extra_info": "Exchangeのトントランザクションを送信しながら、メモIDを指定することを忘れないでください",
923 "tor_connection": "Tor接続",
924 + "tor_connection_timeout": "TOR接続タイムアウト",
925 + "tor_experimental": "これは実験的な機能であり、ケーキウォレットサポートTORルーティングから作成されたすべての接続ではありません。一部の接続では、APPトラフィックをVPNモードで完全にルーティングするために、ClearNetを使用する場合があります。",
926 "tor_only": "Torのみ",
927 "total": "合計",
928 "total_saving": "合計節約額",
res/values/strings_ko.arb
+7 -1
@@ -268,7 +268,8 @@
268 "disable_exchange_option": "교환 옵션 비활성화",
269 "disable_fee_api_warning": "이 기능을 끄면 경우에 따라 수수료율이 정확하지 않을 수 있으며, 트랜잭션 수수료를 과다 또는 과소 지불할 수 있습니다.",
270 "disable_fiat": "법정화폐 비활성화",
271 - "disable_sell": "판매 기능 비활성화",
271 + "disable_sell": "판매 조치 비활성화",
272 + "disable_tor": "Tor를 비활성화하십시오",
273 "disable_trade_option": "거래 옵션 비활성화",
274 "disableBatteryOptimization": "배터리 최적화 비활성화",
275 "disableBatteryOptimizationDescription": "백그라운드 동기화가 더 자유롭고 원활하게 실행되도록 배터리 최적화를 비활성화하시겠습니까?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "사용할 때마다 새 주소를 생성하지만 이전 주소도 계속 작동합니다",
296 "email_address": "이메일 주소",
297 "enable": "활성화",
298 + "enable_builtin_tor": "내장 Tor를 활성화하십시오",
299 "enable_mempool_api": "정확한 수수료 및 날짜를 위한 Mempool API",
300 "enable_replace_by_fee": "RBF(Replace-By-Fee) 활성화",
301 "enable_silent_payments_scanning": "사일런트 페이먼트 주소로 전송된 트랜잭션 스캔 시작",
@@ -334,6 +336,7 @@
336 "error_while_processing": "처리 중 오류가 발생했습니다",
337 "errorGettingCredentials": "실패: 자격 증명을 가져오는 중 오류 발생",
338 "errorSigningTransaction": "트랜잭션 서명 중 오류가 발생했습니다",
339 + "establishing_tor_connection": "Tor 연결을 실행합니다",
340 "estimated": "예상",
341 "estimated_new_fee": "예상 새 수수료",
342 "estimated_receive_amount": "예상 수령 금액",
@@ -861,6 +864,7 @@
864 "sort_by": "정렬 기준",
865 "spend_key_private": "지출 키 (개인)",
866 "spend_key_public": "지출 키 (공개)",
867 + "starting_tor_proxy": "Tor 프록시 시작",
868 "status": "상태: ",
869 "step": "단계",
870 "string_default": "기본값",
@@ -917,6 +921,8 @@
921 "tokenID": "ID",
922 "ton_extra_info": "교환을 위해 TON 트랜잭션을 보내는 동안 메모 ID를 지정하는 것을 잊지 마십시오.",
923 "tor_connection": "Tor 연결",
924 + "tor_connection_timeout": "Tor 연결 시간 초과",
925 + "tor_experimental": "이것은 케이크 지갑 지원 Tor 라우팅으로 만든 모든 연결을 실험적인 기능입니다. 일부 연결은 Clearnet을 사용할 수 있습니다.",
926 "tor_only": "Tor 전용",
927 "total": "합계",
928 "total_saving": "총 절약액",
res/values/strings_my.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "ဤအရာကိုဖွင့်ခြင်းအားဖြင့်အချို့သောကိစ္စရပ်များတွင်အခကြေးငွေနှုန်းထားများသည်တိကျမှုရှိနိုင်သည်,",
270 "disable_fiat": "Fiat ကိုပိတ်ပါ။",
271 "disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
272 + "disable_tor": "Tor ကိုပိတ်ပါ",
273 "disable_trade_option": "ကုန်သွယ်ရေး option ကိုပိတ်ပါ",
274 "disableBatteryOptimization": "ဘက်ထရီ optimization ကိုပိတ်ပါ",
275 "disableBatteryOptimizationDescription": "နောက်ခံထပ်တူပြုခြင်းနှင့်ချောချောမွေ့မွေ့ပြုလုပ်နိုင်ရန်ဘက်ထရီ optimization ကိုသင်ပိတ်ထားလိုပါသလား။",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "သင်အသုံးပြုသည့်အချိန်တိုင်းတွင် ကျွန်ုပ်တို့သည် လိပ်စာအသစ်များကို ထုတ်ပေးသော်လည်း ယခင်လိပ်စာများသည် ဆက်လက်အလုပ်လုပ်နေပါသည်။",
296 "email_address": "အီးမေးလ်လိပ်စာ",
297 "enable": "စွမ်းဆောင်နိုင်စေ",
298 + "enable_builtin_tor": "built-in Tor ကို Enable လုပ်ပါ",
299 "enable_mempool_api": "Mempool API တိကျသောအခကြေးငွေနှင့်ရက်စွဲများအတွက်",
300 "enable_replace_by_fee": "အစားထိုး - by- အခကြေးငွေ enable",
301 "enable_silent_payments_scanning": "အစွန်အဖျားသို့ရောက်ရှိသည်အထိအသံတိတ်ငွေပေးချေမှုကိုစကင်ဖတ်စစ်ဆေးပါ",
@@ -334,6 +336,7 @@
336 "error_while_processing": "procesing နေစဉ်အမှားတစ်ခုဖြစ်ပွားခဲ့သည်",
337 "errorGettingCredentials": "မအောင်မြင်ပါ- အထောက်အထားများ ရယူနေစဉ် အမှားအယွင်း",
338 "errorSigningTransaction": "ငွေပေးငွေယူ လက်မှတ်ထိုးစဉ် အမှားအယွင်းတစ်ခု ဖြစ်ပေါ်ခဲ့သည်။",
339 + "establishing_tor_connection": "Tor connection ကို estabilishing",
340 "estimated": "ခန့်မှန်း",
341 "estimated_new_fee": "ခန့်မှန်းသစ်ခန့်မှန်း",
342 "estimated_receive_amount": "ခန့်မှန်းရရှိသောပမာဏ",
@@ -860,6 +863,7 @@
863 "sort_by": "အလိုက်စဥ်သည်",
864 "spend_key_private": "သော့သုံးရန် (သီးသန့်)",
865 "spend_key_public": "သုံးစွဲရန်သော့ (အများပြည်သူ)",
866 + "starting_tor_proxy": "Tor proxy ကိုစတင်ခြင်း",
867 "status": "အခြေအနေ:",
868 "step": "လှမ်း",
869 "string_default": "ပျက်ကွက်ခြင်း",
@@ -916,6 +920,8 @@
920 "tokenID": "အမှတ်သညာ",
921 "ton_extra_info": "ငွေလဲလှယ်မှုအတွက်တန်ပြန်ငွေပေးငွေယူကိုပို့နေစဉ် Memo ID ကိုသတ်မှတ်ရန်မမေ့ပါနှင့်",
922 "tor_connection": "Tor ချိတ်ဆက်မှု",
923 + "tor_connection_timeout": "Tor Connection အချိန်ကုန်",
924 + "tor_experimental": "၎င်းသည်စမ်းသပ်အင်္ဂါရပ်တစ်ခုဖြစ်ပြီးကိတ်မုန့်ပိုက်ဆံအိတ်ကိုပံ့ပိုးပေးသော Tor Routing မှပြုလုပ်သောဆက်သွယ်မှုအားလုံးမှာ Connections သည် Concernet ကို အသုံးပြု. VPN tor ကို VPN Mode ဖြင့် Tor ကို အသုံးပြု. Concernet ကိုသုံးနိုင်သည်။",
925 "tor_only": "Tor သာ",
926 "total": "လုံးဝသော",
927 "total_saving": "စုစုပေါင်းစုဆောင်းငွေ",
res/values/strings_nl.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Door dit uit te schakelen, kunnen de tarieven in sommige gevallen onnauwkeurig zijn, dus u kunt de vergoedingen voor uw transacties te veel betalen of te weinig betalen",
270 "disable_fiat": "Schakel Fiat uit",
271 "disable_sell": "Verkoopactie uitschakelen",
272 + "disable_tor": "Schakel Tor uit",
273 "disable_trade_option": "Schakel handelsoptie uit",
274 "disableBatteryOptimization": "Schakel de batterijoptimalisatie uit",
275 "disableBatteryOptimizationDescription": "Wilt u de optimalisatie van de batterij uitschakelen om achtergrondsynchronisatie te laten werken, vrijer en soepeler?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "We genereren nieuwe adressen elke keer dat u er een gebruikt, maar eerdere adressen blijven werken",
296 "email_address": "E-mailadres",
297 "enable": "Inschakelen",
298 + "enable_builtin_tor": "Schakel ingebouwde tor in",
299 "enable_mempool_api": "Mempool API voor nauwkeurige kosten en datums",
300 "enable_replace_by_fee": "Schakel vervangen door een fee",
301 "enable_silent_payments_scanning": "Begin met het scannen van stille betalingen, totdat de tip is bereikt",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Er is een fout opgetreden tijdens het procederen",
337 "errorGettingCredentials": "Mislukt: fout bij het ophalen van inloggegevens",
338 "errorSigningTransaction": "Er is een fout opgetreden tijdens het ondertekenen van de transactie",
339 + "establishing_tor_connection": "Estabilishing Tor -verbinding",
340 "estimated": "Geschatte",
341 "estimated_new_fee": "Geschatte nieuwe vergoeding",
342 "estimated_receive_amount": "Geschat ontvangen bedrag",
@@ -860,6 +863,7 @@
863 "sort_by": "Sorteer op",
864 "spend_key_private": "Sleutel uitgeven (privaat)",
865 "spend_key_public": "Sleutel uitgeven (openbaar)",
866 + "starting_tor_proxy": "Tor Proxy starten",
867 "status": "Staat: ",
868 "step": "Stap",
869 "string_default": "Standaard",
@@ -916,6 +920,8 @@
920 "tokenID": "ID kaart",
921 "ton_extra_info": "Vergeet niet om de memo -ID op te geven tijdens het verzenden van de ton -transactie voor de uitwisseling",
922 "tor_connection": "Tor-verbinding",
923 + "tor_connection_timeout": "Tor -verbindingstime -out",
924 + "tor_experimental": "Dit is een experimentele functie, niet alle verbindingen gemaakt van Cake Wallet Support Tor Routing - Sommige verbindingen kunnen ClearNet gebruiken, om app -verkeer volledig te routeren Tor met VPN -modus.",
925 "tor_only": "Alleen Tor",
926 "total": "Totaal",
927 "total_saving": "Totale besparingen",
res/values/strings_pl.arb
+7 -1
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Wyłączając tą opcję, stawki opłaty mogą być w niektórych przypadkach niedokładne, więc możesz przepłacić opłatę za transakcje",
270 "disable_fiat": "Wyłącz waluty FIAT",
271 "disable_sell": "Wyłącz akcję sprzedaży",
272 + "disable_tor": "Wyłącz Tor",
273 "disable_trade_option": "Wyłącz opcję handlu",
274 "disableBatteryOptimization": "Wyłącz optymalizację baterii",
275 "disableBatteryOptimizationDescription": "Czy chcesz wyłączyć optymalizację baterii, aby synchronizacja tła działała swobodniej i płynnie?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Za każdym razem, gdy wykorzystasz adres, dla wiekszej prywatności generujemy nowy, ale poprzednie adresy nadal działają, i moga odbierać środki",
296 "email_address": "Adres e-mail",
297 "enable": "Włącz",
298 + "enable_builtin_tor": "Włącz wbudowany tor",
299 "enable_mempool_api": "Mempool API dla dokładnych opłat i dat",
300 "enable_replace_by_fee": "Włącz wymianę po lewej",
301 "enable_silent_payments_scanning": "Zacznij skanować Silent Payments",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Wystąpił błąd podczas przeróbki",
337 "errorGettingCredentials": "Niepowodzenie: Błąd podczas uzyskiwania poświadczeń",
338 "errorSigningTransaction": "Wystąpił błąd podczas podpisywania transakcji",
339 + "establishing_tor_connection": "Ustalenie połączenia TOR",
340 "estimated": "Oszacowano",
341 "estimated_new_fee": "Szacowana nowa opłata",
342 "estimated_receive_amount": "Szacowana kwota otrzymania",
@@ -860,6 +863,7 @@
863 "sort_by": "Sortuj według",
864 "spend_key_private": "Klucz prywatny",
865 "spend_key_public": "Klucz publiczny",
866 + "starting_tor_proxy": "Rozpoczęcie proxy TOR",
867 "status": "Status: ",
868 "step": "Krok",
869 "string_default": "Domyślny",
@@ -916,6 +920,7 @@
920 "tokenID": "ID",
921 "ton_extra_info": "Nie zapomnij określić identyfikatora notatki podczas wysyłania transakcji TON dla wymiany",
922 "tor_connection": "Połączenie przez Tor",
923 + "tor_connection_timeout": "Tor Connection Timeout",
924 "tor_only": "Tylko sieć Tor",
925 "total": "Całkowity",
926 "total_saving": "Całkowite oszczędności",
@@ -1105,5 +1110,6 @@
1110 "you_will_receive_estimated_amount": "Otrzymasz(oszacowane )",
1111 "you_will_send": "Konwertuj z",
1112 "youCanGoBackToYourDapp": "Możesz teraz wrócić do swojego dapp",
1108 - "yy": "RR"
1113 + "yy": "RR",
1114 + "tor_experimental": "Jest to funkcja eksperymentalna. Nie wszystkie połączenia wykonane z Cake Wallet korzystają z Tor, niektóre połączenia mogą korzystać z Clearnet, aby w pełni przekierować ruch skorzystaj z Tor w trybie VPN."
1115 }
\ No newline at end of file
res/values/strings_pt.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Ao desativar isso, as taxas de taxas podem ser imprecisas em alguns casos, para que você possa acabar pagando demais ou pagando as taxas por suas transações",
270 "disable_fiat": "Desativar fiat",
271 "disable_sell": "Desativar ação de venda",
272 + "disable_tor": "Desativar tor",
273 "disable_trade_option": "Desativar a opção comercial",
274 "disableBatteryOptimization": "Desative a otimização da bateria",
275 "disableBatteryOptimizationDescription": "Deseja desativar a otimização da bateria para fazer a sincronização de fundo funcionar de forma mais livre e suave?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Geramos novos endereços cada vez que você usa um, mas os endereços anteriores continuam funcionando",
296 "email_address": "Endereço de e-mail",
297 "enable": "Habilitar",
298 + "enable_builtin_tor": "Ative o Buildi-in Tor",
299 "enable_mempool_api": "Mempool API para taxas e datas precisas",
300 "enable_replace_by_fee": "Habilite substituir por taxa",
301 "enable_silent_payments_scanning": "Comece a escanear pagamentos silenciosos, até que o topo seja alcançada",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Ocorreu um erro ao procurar",
337 "errorGettingCredentials": "Falha: Erro ao obter credenciais",
338 "errorSigningTransaction": "Ocorreu um erro ao assinar a transação",
339 + "establishing_tor_connection": "Estabilizar a conexão com",
340 "estimated": "Estimado",
341 "estimated_new_fee": "Nova taxa estimada",
342 "estimated_receive_amount": "Valor estimado de recebimento",
@@ -862,6 +865,7 @@
865 "sort_by": "Ordenar por",
866 "spend_key_private": "Chave de gastos (privada)",
867 "spend_key_public": "Chave de gastos (pública)",
868 + "starting_tor_proxy": "Começando por proxy",
869 "status": "Status: ",
870 "step": "Etapa",
871 "string_default": "Padrão",
@@ -918,6 +922,8 @@
922 "tokenID": "EU IA",
923 "ton_extra_info": "Não se esqueça de especificar o ID do memorando ao enviar a transação TON para a troca",
924 "tor_connection": "Conexão Tor",
925 + "tor_connection_timeout": "Tor de tempo limite da conexão",
926 + "tor_experimental": "Esse é um recurso experimental, nem todas as conexões feitas com o suporte para o suporte para a carteira de bolo - algumas conexões podem usar o ClearNet, a fim de rotear totalmente o tráfego de aplicativos TOR com o modo VPN.",
927 "tor_only": "Tor apenas",
928 "total": "Total",
929 "total_saving": "Economia total",
res/values/strings_ru.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Выключив это, в некоторых случаях ставки платы могут быть неточными, так что вы можете в конечном итоге переплачивать или недоплачивать сборы за ваши транзакции",
270 "disable_fiat": "Отключить фиат",
271 "disable_sell": "Отключить действие продажи",
272 + "disable_tor": "Отключить Tor",
273 "disable_trade_option": "Отключить возможность торговли",
274 "disableBatteryOptimization": "Отключить оптимизацию батареи",
275 "disableBatteryOptimizationDescription": "Вы хотите отключить оптимизацию батареи, чтобы сделать фона синхронизации более свободно и плавно?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Мы генерируем новые адреса каждый раз, когда вы их используете, но предыдущие адреса продолжают работать.",
296 "email_address": "Адрес электронной почты",
297 "enable": "Давать возможность",
298 + "enable_builtin_tor": "Включить встроенный в Tor",
299 "enable_mempool_api": "Mempool API за точные сборы и даты",
300 "enable_replace_by_fee": "Включить замену за пикой",
301 "enable_silent_payments_scanning": "Начните сканировать безмолвные платежи, пока не будет достигнут наконечник",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Произошла ошибка во время выплаты",
337 "errorGettingCredentials": "Не удалось: ошибка при получении учетных данных.",
338 "errorSigningTransaction": "Произошла ошибка при подписании транзакции",
339 + "establishing_tor_connection": "Остановка связи",
340 "estimated": "Примерно",
341 "estimated_new_fee": "Расчетная новая плата",
342 "estimated_receive_amount": "Расчетная сумма получения",
@@ -861,6 +864,7 @@
864 "sort_by": "Сортировать по",
865 "spend_key_private": "Приватный ключ траты",
866 "spend_key_public": "Публичный ключ траты",
867 + "starting_tor_proxy": "Запуск прокси",
868 "status": "Статус: ",
869 "step": "Шаг",
870 "string_default": "По умолчанию",
@@ -917,6 +921,8 @@
921 "tokenID": "ИДЕНТИФИКАТОР",
922 "ton_extra_info": "Пожалуйста, не забудьте указать идентификатор записки при отправке TON транзакции для обмена",
923 "tor_connection": "Тор соединение",
924 + "tor_connection_timeout": "TOR Timeout",
925 + "tor_experimental": "Это экспериментальная функция, а не все соединения, сделанные из поддержки TOR для торта - некоторые подключения могут использовать ClearNet, чтобы полностью маршрутироваться с использованием трафика приложений в режиме VPN.",
926 "tor_only": "Только Tor",
927 "total": "Общий",
928 "total_saving": "Общая экономия",
res/values/strings_th.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "โดยการปิดสิ่งนี้อัตราค่าธรรมเนียมอาจไม่ถูกต้องในบางกรณีดังนั้นคุณอาจจบลงด้วยการจ่ายเงินมากเกินไปหรือจ่ายค่าธรรมเนียมสำหรับการทำธุรกรรมของคุณมากเกินไป",
270 "disable_fiat": "ปิดใช้งานสกุลเงินตรา",
271 "disable_sell": "ปิดการใช้งานการขาย",
272 + "disable_tor": "ปิดการใช้งาน TOR",
273 "disable_trade_option": "ปิดใช้งานตัวเลือกการค้า",
274 "disableBatteryOptimization": "ปิดใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่",
275 "disableBatteryOptimizationDescription": "คุณต้องการปิดใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่เพื่อให้การซิงค์พื้นหลังทำงานได้อย่างอิสระและราบรื่นมากขึ้นหรือไม่?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "เราสร้างที่อยู่ใหม่ทุกครั้งที่คุณใช้หนึ่งอย่าง แต่ที่อยู่เก่ายังสามารถใช้ได้ต่อไป",
296 "email_address": "ที่อยู่อีเมล",
297 "enable": "เปิดใช้งาน",
298 + "enable_builtin_tor": "เปิดใช้งาน Tor ในตัว",
299 "enable_mempool_api": "Mempool API สำหรับค่าธรรมเนียมและวันที่ที่ถูกต้อง",
300 "enable_replace_by_fee": "เปิดใช้งานการเปลี่ยนโดยค่าธรรมเนียม",
301 "enable_silent_payments_scanning": "เริ่มสแกนการชำระเงินแบบเงียบจนกว่าจะถึงปลาย",
@@ -334,6 +336,7 @@
336 "error_while_processing": "เกิดข้อผิดพลาดในขณะที่ proceessing",
337 "errorGettingCredentials": "ล้มเหลว: เกิดข้อผิดพลาดขณะรับข้อมูลรับรอง",
338 "errorSigningTransaction": "เกิดข้อผิดพลาดขณะลงนามธุรกรรม",
339 + "establishing_tor_connection": "การเชื่อมต่อ Tor",
340 "estimated": "ประมาณการ",
341 "estimated_new_fee": "ค่าธรรมเนียมใหม่โดยประมาณ",
342 "estimated_receive_amount": "โดยประมาณว่าจำนวนเงินที่ได้รับ",
@@ -860,6 +863,7 @@
863 "sort_by": "เรียงตาม",
864 "spend_key_private": "คีย์จ่าย (ส่วนตัว)",
865 "spend_key_public": "คีย์จ่าย (สาธารณะ)",
866 + "starting_tor_proxy": "เริ่มพร็อกซี TOR",
867 "status": "สถานะ: ",
868 "step": "ขั้นตอน",
869 "string_default": "ค่าเริ่มต้น",
@@ -916,6 +920,8 @@
920 "tokenID": "บัตรประจำตัวประชาชน",
921 "ton_extra_info": "โปรดอย่าลืมระบุรหัสบันทึกในขณะที่ส่งธุรกรรม TON สำหรับการแลกเปลี่ยน",
922 "tor_connection": "การเชื่อมต่อทอร์",
923 + "tor_connection_timeout": "หมดเวลาเชื่อมต่อ tor",
924 + "tor_experimental": "นี่เป็นคุณสมบัติการทดลองไม่ใช่การเชื่อมต่อทั้งหมดที่ทำจากการรองรับเค้กกระเป๋าเงิน TOR การกำหนดเส้นทาง - การเชื่อมต่อบางอย่างอาจใช้ Clearnet เพื่อกำหนดเส้นทางการจราจรแอพใช้ TOR ด้วยโหมด VPN",
925 "tor_only": "Tor เท่านั้น",
926 "total": "ทั้งหมด",
927 "total_saving": "ประหยัดรวม",
res/values/strings_tl.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Sa pamamagitan ng pag -off nito, ang mga rate ng bayad ay maaaring hindi tumpak sa ilang mga kaso, kaya maaari mong tapusin ang labis na bayad o pagsuporta sa mga bayarin para sa iyong mga transaksyon",
270 "disable_fiat": "Huwag paganahin ang fiat",
271 "disable_sell": "Huwag paganahin ang pagkilos ng pagbebenta",
272 + "disable_tor": "Huwag paganahin ang tor",
273 "disable_trade_option": "Huwag paganahin ang pagpipilian sa kalakalan",
274 "disableBatteryOptimization": "Huwag Paganahin ang Pag-optimize ng Baterya",
275 "disableBatteryOptimizationDescription": "Nais mo bang huwag paganahin ang pag-optimize ng baterya upang gawing mas malaya at maayos ang background sync?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Bumubuo kami ng mga bagong address sa tuwing gagamit ka ng isa, ngunit ang mga nakaraang address ay patuloy na gumagana",
296 "email_address": "Email Address",
297 "enable": "Paganahin",
298 + "enable_builtin_tor": "Paganahin ang built-in tor",
299 "enable_mempool_api": "Mempool API para sa tumpak na bayad at mga petsa",
300 "enable_replace_by_fee": "Paganahin ang Replace-By-Fee",
301 "enable_silent_payments_scanning": "Simulan ang pag -scan ng tahimik na pagbabayad, hanggang sa maabot ang tip",
@@ -334,6 +336,7 @@
336 "error_while_processing": "May naganap na error habang nagpapatuloy",
337 "errorGettingCredentials": "Nabigo: Error habang kumukuha ng mga kredensyal",
338 "errorSigningTransaction": "Error habang pinipirmahan ang transaksyon",
339 + "establishing_tor_connection": "Koneksyon ng Tor",
340 "estimated": "Tinatayang",
341 "estimated_new_fee": "Tinatayang bagong fee",
342 "estimated_receive_amount": "Tinatayang natanggap na halaga",
@@ -860,6 +863,7 @@
863 "sort_by": "Pag-uri-uriin sa pamamagitan ng",
864 "spend_key_private": "Spend key (private)",
865 "spend_key_public": "Spend key (public)",
866 + "starting_tor_proxy": "Simula sa Tor Proxy",
867 "status": "Katayuan: ",
868 "step": "Hakbang",
869 "string_default": "Default",
@@ -916,6 +920,8 @@
920 "tokenID": "ID",
921 "ton_extra_info": "Mangyaring huwag kalimutan na tukuyin ang memo ID habang nagpapadala ng toneladang transaksyon para sa palitan",
922 "tor_connection": "Koneksyon ng Tor",
923 + "tor_connection_timeout": "Oras ng koneksyon sa tor",
924 + "tor_experimental": "Ito ay isang pang -eksperimentong tampok, hindi lahat ng mga koneksyon na ginawa mula sa cake ng suporta sa wallet ng tor ruta - ang ilang mga koneksyon ay maaaring gumamit ng clearnet, upang ganap na ruta ang paggamit ng trapiko ng trapiko na may mode na VPN.",
925 "tor_only": "Tor lamang",
926 "total": "Kabuuan",
927 "total_saving": "Kabuuang ipon",
res/values/strings_tr.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Bunu kapatarak, ücret oranları bazı durumlarda yanlış olabilir, bu nedenle işlemleriniz için ücretleri fazla ödeyebilir veya az ödeyebilirsiniz.",
270 "disable_fiat": "İtibari paraları devre dışı bırak",
271 "disable_sell": "Satış işlemini devre dışı bırak",
272 + "disable_tor": "Tor'u devre dışı bırak",
273 "disable_trade_option": "Ticaret seçeneğini devre dışı bırakın",
274 "disableBatteryOptimization": "Pil optimizasyonunu devre dışı bırakın",
275 "disableBatteryOptimizationDescription": "Arka plan senkronizasyonunu daha özgür ve sorunsuz bir şekilde çalıştırmak için pil optimizasyonunu devre dışı bırakmak istiyor musunuz?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Adresini her kullandığında yeni adres oluşturuyoruz, ancak önceki adresler de çalışmaya devam eder",
296 "email_address": "E-posta Adresi",
297 "enable": "Olanak vermek",
298 + "enable_builtin_tor": "Yerleşik Tor'u etkinleştirin",
299 "enable_mempool_api": "Doğru ücretler ve tarihler için Mempool API'si",
300 "enable_replace_by_fee": "Farklı Değiştir'i Etkinleştir",
301 "enable_silent_payments_scanning": "Bahşiş ulaşılıncaya kadar sessiz ödemeleri taramaya başlayın",
@@ -334,6 +336,7 @@
336 "error_while_processing": "ProceSting sırasında bir hata oluştu",
337 "errorGettingCredentials": "Başarısız: Kimlik bilgileri alınırken hata oluştu",
338 "errorSigningTransaction": "İşlem imzalanırken bir hata oluştu",
339 + "establishing_tor_connection": "Fazla Tor Bağlantısı",
340 "estimated": "Tahmini",
341 "estimated_new_fee": "Tahmini yeni ücret",
342 "estimated_receive_amount": "Tahmini alma miktarı",
@@ -860,6 +863,7 @@
863 "sort_by": "Göre sırala",
864 "spend_key_private": "Harcama anahtarı (özel)",
865 "spend_key_public": "Harcama anahtarı (genel)",
866 + "starting_tor_proxy": "Tor Proxy'yi Başlatma",
867 "status": "Durum: ",
868 "step": "Adım",
869 "string_default": "Varsayılan",
@@ -916,6 +920,8 @@
920 "tokenID": "İD",
921 "ton_extra_info": "Lütfen değişim için ton işlemini gönderirken not kimliğini belirtmeyi unutmayın",
922 "tor_connection": "Tor bağlantısı",
923 + "tor_connection_timeout": "Tor bağlantı zaman aşımı",
924 + "tor_experimental": "Bu deneysel bir özelliktir, kek cüzdanı desteğinden yapılan tüm bağlantılar değil, TOR yönlendirmesinden yapılmıştır - bazı bağlantılar, VPN moduyla uygulama trafik kullanımını tam olarak yönlendirmek için ClearNet kullanabilir.",
925 "tor_only": "Yalnızca Tor",
926 "total": "Toplam",
927 "total_saving": "Toplam Tasarruf",
res/values/strings_uk.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Вимкнувши це, ставки плати в деяких випадках можуть бути неточними, тому ви можете переплатити або недооплатити плату за свої транзакції",
270 "disable_fiat": "Вимкнути фиат",
271 "disable_sell": "Вимкнути дію продажу",
272 + "disable_tor": "Вимкнути Тор",
273 "disable_trade_option": "Вимкнути можливість торгівлі",
274 "disableBatteryOptimization": "Вимкнути оптимізацію акумулятора",
275 "disableBatteryOptimizationDescription": "Ви хочете відключити оптимізацію акумулятора, щоб зробити фонову синхронізацію більш вільно та плавно?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "Ми створюємо нові адреси щоразу, коли ви використовуєте їх, але попередні адреси продовжують працювати",
296 "email_address": "Адреса електронної пошти",
297 "enable": "Ввімкнути",
298 + "enable_builtin_tor": "Увімкнути вбудований tor",
299 "enable_mempool_api": "API Mempool для точних зборів та дат",
300 "enable_replace_by_fee": "Увімкнути заміну з комісією",
301 "enable_silent_payments_scanning": "Почніть сканувати мовчазні платежі, поки не буде досягнуто наконечника",
@@ -334,6 +336,7 @@
336 "error_while_processing": "Помилка сталася під час проектування",
337 "errorGettingCredentials": "Помилка: помилка під час отримання облікових даних",
338 "errorSigningTransaction": "Під час підписання транзакції сталася помилка",
339 + "establishing_tor_connection": "Закінчення підключення до TOR",
340 "estimated": "Приблизно ",
341 "estimated_new_fee": "Орієнтовна нова комісія",
342 "estimated_receive_amount": "Орієнтовна сума отримує",
@@ -861,6 +864,7 @@
864 "sort_by": "Сортувати за",
865 "spend_key_private": "Приватний ключ витрати",
866 "spend_key_public": "Публічний ключ витрати",
867 + "starting_tor_proxy": "Початок Tor Proxy",
868 "status": "Статус: ",
869 "step": "Крок",
870 "string_default": "За замовчуванням",
@@ -917,6 +921,8 @@
921 "tokenID": "ID",
922 "ton_extra_info": "Не забудьте вказати ідентифікатор пам’яті під час надсилання транзакції TON для обміну",
923 "tor_connection": "Підключення Tor",
924 + "tor_connection_timeout": "Тайм -аут підключення TOR",
925 + "tor_experimental": "Це експериментальна функція, не всі з'єднання, зроблені з підтримки тортів, що підтримують маршрутизацію - деякі з'єднання можуть використовувати Clearnet, щоб повністю маршрутизувати трафік додатків, використовуючи TOR з режимом VPN.",
926 "tor_only": "Тільки Tor",
927 "total": "Загальний",
928 "total_saving": "Загальна економія",
res/values/strings_ur.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "اس کو بند کرنے سے ، کچھ معاملات میں فیس کی شرح غلط ہوسکتی ہے ، لہذا آپ اپنے لین دین کے لئے فیسوں کو زیادہ ادائیگی یا ادائیگی ختم کرسکتے ہیں۔",
270 "disable_fiat": "فیاٹ کو غیر فعال کریں۔",
271 "disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔",
272 + "disable_tor": "ٹور کو غیر فعال کریں",
273 "disable_trade_option": "تجارت کے آپشن کو غیر فعال کریں",
274 "disableBatteryOptimization": "بیٹری کی اصلاح کو غیر فعال کریں",
275 "disableBatteryOptimizationDescription": "کیا آپ پس منظر کی مطابقت پذیری کو زیادہ آزادانہ اور آسانی سے چلانے کے لئے بیٹری کی اصلاح کو غیر فعال کرنا چاہتے ہیں؟",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "جب بھی آپ ایک کا استعمال کرتے ہیں تو ہم نئے پتے تیار کرتے ہیں، لیکن پچھلے پتے کام کرتے رہتے ہیں۔",
296 "email_address": "ای میل اڈریس",
297 "enable": "قابل بنائیں",
298 + "enable_builtin_tor": "بلٹٹی ان ٹور کو فعال کریں",
299 "enable_mempool_api": "درست فیسوں اور تاریخوں کے لئے میمپول API",
300 "enable_replace_by_fee": "فی فیس کو تبدیل کریں",
301 "enable_silent_payments_scanning": "خاموش ادائیگیوں کو اسکین کرنا شروع کریں ، جب تک کہ نوک نہ پہنچ جائے",
@@ -334,6 +336,7 @@
336 "error_while_processing": "ایک غلطی پیش کرتے وقت ہوئی",
337 "errorGettingCredentials": "۔ﯽﺑﺍﺮﺧ ﮟﯿﻣ ﮯﻧﺮﮐ ﻞﺻﺎﺣ ﺩﺎﻨﺳﺍ :ﻡﺎﮐﺎﻧ",
338 "errorSigningTransaction": "۔ﮯﮨ ﯽﺌﮔﺁ ﺶﯿﭘ ﯽﺑﺍﺮﺧ ﮏﯾﺍ ﺖﻗﻭ ﮯﺗﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ",
339 + "establishing_tor_connection": "ٹور کنکشن کو کھڑا کرنا",
340 "estimated": "تخمینہ لگایا",
341 "estimated_new_fee": "تخمینہ شدہ نئی فیس",
342 "estimated_receive_amount": "تخمینہ وصول کی رقم",
@@ -862,6 +865,7 @@
865 "sort_by": "ترتیب دیں",
866 "spend_key_private": "خرچ کی کلید (نجی)",
867 "spend_key_public": "خرچ کی کلید (عوامی)",
868 + "starting_tor_proxy": "ٹور پراکسی شروع کرنا",
869 "status": "حالت:",
870 "step": "مرحلہ",
871 "string_default": "پہلے سے طے شدہ",
@@ -918,6 +922,8 @@
922 "tokenID": "ID",
923 "ton_extra_info": "ایکسچینج کے لئے ٹن ٹرانزیکشن بھیجتے وقت براہ کرم میمو آئی ڈی کی وضاحت کرنا نہ بھولیں",
924 "tor_connection": "ﻦﺸﮑﻨﮐ ﺭﻮﭨ",
925 + "tor_connection_timeout": "ٹور کنکشن ٹائم آؤٹ",
926 + "tor_experimental": "یہ ایک تجرباتی خصوصیت ہے ، کیک پرس پرس سپورٹ ٹور روٹنگ سے بنے تمام رابطے نہیں - کچھ رابطے کلینیٹ کا استعمال کرسکتے ہیں ، تاکہ مکمل طور پر ایپ ٹریفک کو VPN وضع کے ساتھ ٹور کا استعمال کریں۔",
927 "tor_only": "صرف Tor",
928 "total": "کل",
929 "total_saving": "کل بچت",
res/values/strings_vi.arb
+6
@@ -268,6 +268,7 @@
268 "disable_fee_api_warning": "Khi tắt chức năng này, tỉ lệ phí có thể không chính xác trong một số trường hợp, dẫn đến bạn trả quá hoặc không đủ phí cho giao dịch của mình.",
269 "disable_fiat": "Vô hiệu hóa tiền tệ fiat",
270 "disable_sell": "Vô hiệu hóa chức năng bán",
271 + "disable_tor": "Tắt Tor",
272 "disable_trade_option": "Tắt tùy chọn thương mại",
273 "disableBatteryOptimization": "Vô hiệu hóa Tối ưu hóa Pin",
274 "disableBatteryOptimizationDescription": "Bạn có muốn vô hiệu hóa tối ưu hóa pin để đồng bộ hóa nền hoạt động mượt mà hơn không?",
@@ -293,6 +294,7 @@
294 "electrum_address_disclaimer": "Chúng tôi tạo địa chỉ mới mỗi khi bạn sử dụng, nhưng các địa chỉ cũ vẫn tiếp tục hoạt động",
295 "email_address": "Địa chỉ Email",
296 "enable": "Cho phép",
297 + "enable_builtin_tor": "Bật Buildi-In Tor",
298 "enable_mempool_api": "API Mempool cho các khoản phí và ngày chính xác",
299 "enable_replace_by_fee": "Bật Thay thế Bằng Phí",
300 "enable_silent_payments_scanning": "Bật quét thanh toán im lặng",
@@ -333,6 +335,7 @@
335 "error_while_processing": "Xảy ra lỗi trong khi sử dụng",
336 "errorGettingCredentials": "Không thành công: Lỗi khi nhận thông tin xác thực",
337 "errorSigningTransaction": "Đã xảy ra lỗi khi ký giao dịch",
338 + "establishing_tor_connection": "Kết nối tor",
339 "estimated": "Ước tính",
340 "estimated_new_fee": "Phí mới ước tính",
341 "estimated_receive_amount": "Số tiền nhận ước tính",
@@ -857,6 +860,7 @@
860 "sort_by": "Sắp xếp theo",
861 "spend_key_private": "Khóa chi tiêu (riêng tư)",
862 "spend_key_public": "Khóa chi tiêu (công khai)",
863 + "starting_tor_proxy": "Bắt đầu proxy tor",
864 "status": "Trạng thái: ",
865 "step": "Bước chân",
866 "string_default": "Mặc định",
@@ -913,6 +917,8 @@
917 "tokenID": "ID",
918 "ton_extra_info": "Xin đừng quên chỉ định ID ghi nhớ trong khi gửi giao dịch tấn cho trao đổi",
919 "tor_connection": "Kết nối Tor",
920 + "tor_connection_timeout": "Thời gian chờ kết nối tor",
921 + "tor_experimental": "Đây là một tính năng thử nghiệm, không phải tất cả các kết nối được làm từ Wake Wallet Hỗ trợ định tuyến TOR - một số kết nối có thể sử dụng ClearNet, để định tuyến đầy đủ về lưu lượng truy cập ứng dụng Tor với chế độ VPN.",
922 "tor_only": "Chỉ Tor",
923 "total": "Tổng cộng",
924 "total_saving": "Tiết kiệm tổng cộng",
res/values/strings_yo.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "Nipa yiyi eyi kuro, awọn oṣuwọn owo naa le jẹ aiṣe deede ni awọn ọrọ kan, nitorinaa o le pari apọju tabi awọn idiyele ti o ni agbara fun awọn iṣowo rẹ",
270 "disable_fiat": "Pa owó tí ìjọba pàṣẹ wa lò",
271 "disable_sell": "Ko iṣọrọ iṣọrọ",
272 + "disable_tor": "Mu lile",
273 "disable_trade_option": "Mu aṣayan iṣowo ṣiṣẹ",
274 "disableBatteryOptimization": "Mu Ifasi batiri",
275 "disableBatteryOptimizationDescription": "Ṣe o fẹ lati mu iṣapelo batiri si lati le ṣiṣe ayẹwo ẹhin ati laisiyonu?",
@@ -295,6 +296,7 @@
296 "electrum_address_disclaimer": "A dá àwọn àdírẹ́sì títun ní gbogbo àwọn ìgbà t'ẹ́ lo ó kan ṣùgbọ́n ẹ lè tẹ̀síwájú lo àwọn àdírẹ́sì tẹ́lẹ̀tẹ́lẹ̀.",
297 "email_address": "Àdírẹ́sì ímeèlì",
298 "enable": "Mu ṣiṣẹ",
299 + "enable_builtin_tor": "Mu ṣiṣẹ",
300 "enable_mempool_api": "Mempool API fun awọn owo deede ati awọn ọjọ",
301 "enable_replace_by_fee": "Mu ki o rọpo",
302 "enable_silent_payments_scanning": "Bẹrẹ awọn sisanwo ipalọlọ, titi ti o fi de opin",
@@ -335,6 +337,7 @@
337 "error_while_processing": "Aṣiṣe kan waye lakoko ti o duro",
338 "errorGettingCredentials": "Kuna: Aṣiṣe lakoko gbigba awọn iwe-ẹri",
339 "errorSigningTransaction": "Aṣiṣe kan ti waye lakoko ti o fowo si iṣowo",
340 + "establishing_tor_connection": "Isopọ Stanishing",
341 "estimated": "Ó tó a fojú díwọ̀n",
342 "estimated_new_fee": "Ifoju tuntun owo tuntun",
343 "estimated_receive_amount": "Ifoju gba iye",
@@ -861,6 +864,7 @@
864 "sort_by": "Sa pelu",
865 "spend_key_private": "Kọ́kọ́rọ́ sísan (àdáni)",
866 "spend_key_public": "Kọ́kọ́rọ́ sísan (kò àdáni)",
867 + "starting_tor_proxy": "Bibẹrẹ Oluṣaaju Aṣoju",
868 "status": "Tó ń ṣẹlẹ̀: ",
869 "step": "Igbesẹ",
870 "string_default": "Aiyipada",
@@ -917,6 +921,8 @@
921 "tokenID": "ID",
922 "ton_extra_info": "Jọwọ maṣe gbagbe lati tokasi ID akọsilẹ lakoko fifiranṣẹ idunadura pupọ fun paṣipaarọ naa",
923 "tor_connection": "Tor asopọ",
924 + "tor_connection_timeout": "Akoko asopọ asopọ",
925 + "tor_experimental": "Eyi jẹ ẹya ti esiperimenta ti a ṣe lati awọn Awoṣe Adwat atilẹyin ti o wa ni atilẹyin idiwọ - diẹ ninu awọn asopọ le lo imple, lati le ni ipa ọna App Prat pẹlu ipo VPN.",
926 "tor_only": "Tor nìkan",
927 "total": "Apapọ",
928 "total_saving": "Owó t'ẹ́ ti pamọ́",
res/values/strings_zh.arb
+6
@@ -269,6 +269,7 @@
269 "disable_fee_api_warning": "通过将其关闭,在某些情况下,收费率可能不准确,因此您最终可能会超额付款或支付交易费用",
270 "disable_fiat": "禁用法令",
271 "disable_sell": "禁用卖出操作",
272 + "disable_tor": "禁用TOR",
273 "disable_trade_option": "禁用贸易选项",
274 "disableBatteryOptimization": "禁用电池优化",
275 "disableBatteryOptimizationDescription": "您是否要禁用电池优化以使背景同步更加自由,平稳地运行?",
@@ -294,6 +295,7 @@
295 "electrum_address_disclaimer": "每次您使用一个地址时,我们都会生成新地址,但之前的地址仍然有效",
296 "email_address": "电子邮件地址",
297 "enable": "使能够",
298 + "enable_builtin_tor": "启用内置的tor",
299 "enable_mempool_api": "Mempool API获得准确的费用和日期",
300 "enable_replace_by_fee": "启用by-Fee替换",
301 "enable_silent_payments_scanning": "开始扫描无声付款,直到达到提示",
@@ -334,6 +336,7 @@
336 "error_while_processing": "发动机时发生错误",
337 "errorGettingCredentials": "失败:获取凭据时出错",
338 "errorSigningTransaction": "签署交易时发生错误",
339 + "establishing_tor_connection": "tor连接",
340 "estimated": "估计值",
341 "estimated_new_fee": "估计新费用",
342 "estimated_receive_amount": "估计接收金额",
@@ -860,6 +863,7 @@
863 "sort_by": "排序方式",
864 "spend_key_private": "Spend 密钥 (私钥)",
865 "spend_key_public": "Spend 密钥 (公钥)",
866 + "starting_tor_proxy": "启动Tor代理",
867 "status": "状态: ",
868 "step": "步",
869 "string_default": "默认",
@@ -916,6 +920,8 @@
920 "tokenID": "ID",
921 "ton_extra_info": "请不要忘记在发送TON交易时指定备忘录",
922 "tor_connection": "Tor连接",
923 + "tor_connection_timeout": "TOR连接超时",
924 + "tor_experimental": "这是一个实验功能,并非所有由Cake Wallet Support Tor路由组成的连接 - 某些连接可能会使用Clearnet,以便完全路由App App App Appl使用VPN模式。",
925 "tor_only": "仅限 Tor",
926 "total": "全部的",
927 "total_saving": "总储蓄",